Repository navigation
Expand file tree
/
Copy pathskin-instance.js
More file actions
159 lines (123 loc) · 4.64 KB
/
Copy pathskin-instance.js
File metadata and controls
159 lines (123 loc) · 4.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import { math } from '../math/math.js';
import { Mat4 } from '../math/mat4.js';
import { FILTER_NEAREST, PIXELFORMAT_RGBA32F } from '../graphics/constants.js';
import { Texture } from '../graphics/texture.js';
var _invMatrix = new Mat4();
/**
* @class
* @name SkinInstance
* @classdesc A skin instance is responsible for generating the matrix palette that is used to
* skin vertices from object space to world space.
* @param {Skin} skin - The skin that will provide the inverse bind pose matrices to
* generate the final matrix palette.
* @property {GraphNode[]} bones An array of nodes representing each bone in this skin instance.
*/
class SkinInstance {
constructor(skin) {
this._dirty = true;
// optional root bone - used for cache lookup, not used for skinning
this._rootBone = null;
// sequencial index of when the bone update was performed the last time
this._skinUpdateIndex = -1;
// true if bones need to be updated before the frustum culling (bones are needed to update bounds of the MeshInstance)
this._updateBeforeCull = true;
if (skin) {
this.initSkin(skin);
}
}
init(device, numBones) {
if (device.supportsBoneTextures) {
// texture size - roughly square that fits all bones, width is multiply of 3 to simplify shader math
var numPixels = numBones * 3;
var width = Math.ceil(Math.sqrt(numPixels));
width = math.roundUp(width, 3);
var height = Math.ceil(numPixels / width);
this.boneTexture = new Texture(device, {
width: width,
height: height,
format: PIXELFORMAT_RGBA32F,
mipmaps: false,
minFilter: FILTER_NEAREST,
magFilter: FILTER_NEAREST
});
this.boneTexture.name = 'skin';
this.matrixPalette = this.boneTexture.lock();
} else {
this.matrixPalette = new Float32Array(numBones * 12);
}
}
destroy() {
if (this.boneTexture) {
this.boneTexture.destroy();
this.boneTexture = null;
}
}
get rootBone() {
return this._rootBone;
}
set rootBone(rootBone) {
this._rootBone = rootBone;
}
initSkin(skin) {
this.skin = skin;
// Unique per clone
this.bones = [];
var numBones = skin.inverseBindPose.length;
this.init(skin.device, numBones);
this.matrices = [];
for (var i = 0; i < numBones; i++) {
this.matrices[i] = new Mat4();
}
}
uploadBones(device) {
// TODO: this is a bit strange looking. Change the Texture API to do a reupload
if (device.supportsBoneTextures) {
this.boneTexture.lock();
this.boneTexture.unlock();
}
}
_updateMatrices(rootNode, skinUpdateIndex) {
// if not already up to date
if (this._skinUpdateIndex !== skinUpdateIndex) {
this._skinUpdateIndex = skinUpdateIndex;
_invMatrix.copy(rootNode.getWorldTransform()).invert();
for (var i = this.bones.length - 1; i >= 0; i--) {
this.matrices[i].mulAffine2(_invMatrix, this.bones[i].getWorldTransform()); // world space -> rootNode space
this.matrices[i].mulAffine2(this.matrices[i], this.skin.inverseBindPose[i]); // rootNode space -> bind space
}
}
}
updateMatrices(rootNode, skinUpdateIndex) {
if (this._updateBeforeCull) {
this._updateMatrices(rootNode, skinUpdateIndex);
}
}
updateMatrixPalette(rootNode, skinUpdateIndex) {
// make sure matrices are up to date
this._updateMatrices(rootNode, skinUpdateIndex);
// copy matrices to palette
var pe;
var mp = this.matrixPalette;
var base;
var count = this.bones.length;
for (var i = 0; i < count; i++) {
pe = this.matrices[i].data;
// Copy the matrix into the palette, ready to be sent to the vertex shader, transpose matrix from 4x4 to 4x3 format as well
base = i * 12;
mp[base] = pe[0];
mp[base + 1] = pe[4];
mp[base + 2] = pe[8];
mp[base + 3] = pe[12];
mp[base + 4] = pe[1];
mp[base + 5] = pe[5];
mp[base + 6] = pe[9];
mp[base + 7] = pe[13];
mp[base + 8] = pe[2];
mp[base + 9] = pe[6];
mp[base + 10] = pe[10];
mp[base + 11] = pe[14];
}
this.uploadBones(this.skin.device);
}
}
export { SkinInstance };