SkinnedMesh.js 4.5 KB
Newer Older
1 2
/**
 * @author mikael emtinger / http://gomo.se/
A
alteredq 已提交
3
 * @author alteredq / http://alteredqualia.com/
4 5
 */

6
THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
7

8
	THREE.Mesh.call( this, geometry, material );
9

10 11
	//

12
	this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;
13

14 15 16
	// init bones

	this.identityMatrix = new THREE.Matrix4();
A
alteredq 已提交
17

18
	this.bones = [];
19
	this.boneMatrices = [];
20

A
alteredq 已提交
21
	var b, bone, gbone, p, q, s;
22 23 24

	if ( this.geometry.bones !== undefined ) {

25
		for ( b = 0; b < this.geometry.bones.length; b ++ ) {
26

A
alteredq 已提交
27
			gbone = this.geometry.bones[ b ];
28

A
alteredq 已提交
29 30 31
			p = gbone.pos;
			q = gbone.rotq;
			s = gbone.scl;
32

A
alteredq 已提交
33
			bone = this.addBone();
34

A
alteredq 已提交
35
			bone.name = gbone.name;
36
			bone.position.set( p[0], p[1], p[2] );
A
alteredq 已提交
37
			bone.quaternion.set( q[0], q[1], q[2], q[3] );
38
			bone.useQuaternion = true;
39 40 41

			if ( s !== undefined ) {

A
alteredq 已提交
42
				bone.scale.set( s[0], s[1], s[2] );
43 44 45

			} else {

A
alteredq 已提交
46
				bone.scale.set( 1, 1, 1 );
47

48 49
			}

50
		}
51

52
		for ( b = 0; b < this.bones.length; b ++ ) {
53

A
alteredq 已提交
54 55
			gbone = this.geometry.bones[ b ];
			bone = this.bones[ b ];
56 57 58

			if ( gbone.parent === -1 ) {

59
				this.add( bone );
60 61 62

			} else {

63
				this.bones[ gbone.parent ].add( bone );
A
alteredq 已提交
64

65 66
			}

67
		}
A
alteredq 已提交
68

69 70
		//

71 72
		var nBones = this.bones.length;

73 74 75 76
		if ( this.useVertexTexture ) {

			// layout (1 matrix = 4 pixels)
			//	RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
			//  with  8x8  pixel texture max   16 bones  (8 * 8  / 4)
			//  	 16x16 pixel texture max   64 bones (16 * 16 / 4)
			//  	 32x32 pixel texture max  256 bones (32 * 32 / 4)
			//  	 64x64 pixel texture max 1024 bones (64 * 64 / 4)

			var size;

			if ( nBones > 256 )
				size = 64;
			else if ( nBones > 64 )
				size = 32;
			else if ( nBones > 16 )
				size = 16;
			else
				size = 8;
92

93 94
			this.boneTextureWidth = size;
			this.boneTextureHeight = size;
95 96 97 98 99 100 101 102 103 104

			this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel
			this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType );
			this.boneTexture.minFilter = THREE.NearestFilter;
			this.boneTexture.magFilter = THREE.NearestFilter;
			this.boneTexture.generateMipmaps = false;
			this.boneTexture.flipY = false;

		} else {

105
			this.boneMatrices = new Float32Array( 16 * nBones );
106 107

		}
108

109 110 111 112 113 114
		this.pose();

	}

};

115
THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
116

A
alteredq 已提交
117
THREE.SkinnedMesh.prototype.addBone = function( bone ) {
118

A
alteredq 已提交
119
	if ( bone === undefined ) {
120

A
alteredq 已提交
121
		bone = new THREE.Bone( this );
122

A
alteredq 已提交
123
	}
124

A
alteredq 已提交
125
	this.bones.push( bone );
126

A
alteredq 已提交
127
	return bone;
128

A
alteredq 已提交
129
};
130

A
alteredq 已提交
131
THREE.SkinnedMesh.prototype.updateMatrixWorld = function ( force ) {
132

A
alteredq 已提交
133
	this.matrixAutoUpdate && this.updateMatrix();
134

A
alteredq 已提交
135
	// update matrixWorld
136

A
alteredq 已提交
137
	if ( this.matrixWorldNeedsUpdate || force ) {
138

A
alteredq 已提交
139
		if ( this.parent ) {
140

A
alteredq 已提交
141
			this.matrixWorld.multiply( this.parent.matrixWorld, this.matrix );
142

A
alteredq 已提交
143
		} else {
144

A
alteredq 已提交
145
			this.matrixWorld.copy( this.matrix );
146 147 148

		}

A
alteredq 已提交
149
		this.matrixWorldNeedsUpdate = false;
150

A
alteredq 已提交
151
		force = true;
152

A
alteredq 已提交
153
	}
154

A
alteredq 已提交
155
	// update children
156

A
alteredq 已提交
157
	for ( var i = 0, l = this.children.length; i < l; i ++ ) {
158

A
alteredq 已提交
159
		var child = this.children[ i ];
M
Mikael Emtinger 已提交
160

A
alteredq 已提交
161
		if ( child instanceof THREE.Bone ) {
M
Mikael Emtinger 已提交
162

A
alteredq 已提交
163
			child.update( this.identityMatrix, false );
M
Mikael Emtinger 已提交
164

A
alteredq 已提交
165
		} else {
166

A
alteredq 已提交
167
			child.updateMatrixWorld( true );
168

M
Mikael Emtinger 已提交
169 170
		}

171
	}
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
	
	// make a snapshot of the bones' rest position
	
	if (this.boneInverses == undefined)
	{
		this.boneInverses = [];
		
		for ( var b = 0; b < this.bones.length; b ++ )
		{
			var inverse = new THREE.Matrix4();
			
			inverse.getInverse( this.bones[ b ].skinMatrix );
			
			this.boneInverses.push( inverse );
		}
	}
188

A
alteredq 已提交
189
	// flatten bone matrices to array
190

191 192 193 194 195 196 197 198 199 200 201 202 203
	for ( var b = 0; b < this.bones.length; b ++ )
	{
		var offset = new THREE.Matrix4();
		
		// compute the offset between the current and the original transform;
		
		//TODO: we could get rid of this multiplication step if the skinMatrix
		// was already representing the offset; however, this requires some
		// major changes to the animation system
		
		offset.multiply( this.bones[ b ].skinMatrix, this.boneInverses[ b ] );

		offset.flattenToArrayOffset( this.boneMatrices, b * 16 );		
204 205
	}

206 207 208 209 210
	if ( this.useVertexTexture ) {

		this.boneTexture.needsUpdate = true;

	}
211

212 213 214 215 216 217 218 219
};

/*
 * Pose
 */

THREE.SkinnedMesh.prototype.pose = function() {

A
alteredq 已提交
220
	this.updateMatrixWorld( true );
221 222
		
	for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) {
223

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
		// normalize weights

		var sw = this.geometry.skinWeights[ i ];
		
		var scale = 1.0 / sw.lengthManhattan();
		
		if ( scale != Infinity ) {
		
			sw.multiplyScalar( scale );
			
		} else {
		
			sw.set( 1 ); // this will be normalized by the shader anyway
			
		}
	}
240
};
241