Browse Source

This commit re-introduces THREE.Skeleton to decouple bone/object transformations
from the transforms that bind a THREE.Skeleton to a THREE.SkinnedMesh. This
allows us to animate multiple THREE.SkinnedMesh instances with a single
THREE.Skeleton.

Here is a summary of the major changes:

extras/helpers/SkeletonHelper.js
* Fixed code mistakes (missing 'THREE').

objects/Bone.js
* Removed duplicate code that is already in THREE.Object3D.

objects/Skeleton.js
* Not a descendant of THREE.Object3D (previous version was).
* Stores boneMatrices and boneTexture.
* Independent of THREE.SkinnedMesh.

objects/SkinnedMesh.js
* Refactored boneMatrices and boneTexture related code into THREE.Skeleton.
* Added two bind matrix modes: "attached" and "detached".

renderers/WebGLRenderer.js
renderers/webgl/WebGLProgram.js
* Attach bindMatrix and bindMatrixInverse to shaders.

renderers/shaders/ShaderChunk.js
renderers/shaders/ShaderLib.js
* Use bind matrices during skinning.

Ian Kerr 11 years ago
parent
commit
889fa9ff63

+ 4 - 3
src/extras/helpers/SkeletonHelper.js

@@ -2,6 +2,7 @@
  * @author Sean Griffin / http://twitter.com/sgrif
  * @author Michael Guerrero / http://realitymeltdown.com
  * @author mrdoob / http://mrdoob.com/
+ * @author ikerr / http://verold.com
  */
 
 THREE.SkeletonHelper = function ( object ) {
@@ -65,9 +66,9 @@ THREE.SkeletonHelper.prototype.update = function () {
 
 	var geometry = this.geometry;
 
-	var matrixWorldInv = new Matrix4().getInverse( this.root.matrixWorld );
+	var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld );
 
-	var boneMatrix = new Matrix4();
+	var boneMatrix = new THREE.Matrix4();
 
 	var j = 0;
 
@@ -81,7 +82,7 @@ THREE.SkeletonHelper.prototype.update = function () {
 			geometry.vertices[ j ].setFromMatrixPosition( boneMatrix );
 
 			boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld );
-			geometry.vertices[ j + 1 ].setFromMatrixPosition( parentMatrix );
+			geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix );
 
 			j += 2;
 

+ 7 - 29
src/objects/Bone.js

@@ -1,6 +1,7 @@
 /**
  * @author mikael emtinger / http://gomo.se/
  * @author alteredq / http://alteredqualia.com/
+ * @author ikerr / http://verold.com
  */
 
 THREE.Bone = function( belongsToSkin ) {
@@ -17,38 +18,15 @@ THREE.Bone = function( belongsToSkin ) {
 
 THREE.Bone.prototype = Object.create( THREE.Object3D.prototype );
 
-THREE.Bone.prototype.update = function ( forceUpdate ) {
+THREE.Bone.prototype.updateMatrixWorld = function ( force ) {
 
-	// update local
+	THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
 
-	if ( this.matrixAutoUpdate ) {
+	// Reset weights to be re-accumulated in the next frame
 
-		forceUpdate |= this.updateMatrix();
-
-	}
-
-	// update skin matrix
-
-	if ( forceUpdate || this.matrixWorldNeedsUpdate ) {
-
-		this.matrixWorldNeedsUpdate = false;
-		forceUpdate = true;
-
-		// Reset weights to be re-accumulated in the next frame
-
-		this.accumulatedRotWeight = 0;
-		this.accumulatedPosWeight = 0;
-		this.accumulatedSclWeight = 0;
-
-	}
-
-	// update children
-
-	for ( var i = 0, l = this.children.length; i < l; i ++ ) {
-
-		this.children[ i ].update( forceUpdate );
-
-	}
+	this.accumulatedRotWeight = 0;
+	this.accumulatedPosWeight = 0;
+	this.accumulatedSclWeight = 0;
 
 };
 

+ 180 - 0
src/objects/Skeleton.js

@@ -0,0 +1,180 @@
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author michael guerrero / http://realitymeltdown.com
+ * @author ikerr / http://verold.com
+ */
+
+THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) {
+
+	this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;
+
+	this.identityMatrix = new THREE.Matrix4();
+
+	// copy the bone array
+
+	bones = bones || [];
+
+	this.bones = bones.slice( 0 );
+
+	// create a bone texture or an array of floats
+
+	if ( this.useVertexTexture ) {
+
+		// layout (1 matrix = 4 pixels)
+		//      RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
+		//  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 ( this.bones.length > 256 )
+			size = 64;
+		else if ( this.bones.length > 64 )
+			size = 32;
+		else if ( this.bones.length > 16 )
+			size = 16;
+		else
+			size = 8;
+
+		this.boneTextureWidth = size;
+		this.boneTextureHeight = size;
+
+		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 {
+
+		this.boneMatrices = new Float32Array( 16 * this.bones.length );
+
+	}
+
+	// use the supplied bone inverses or calculate the inverses
+
+	if ( boneInverses === undefined ) {
+
+		this.calculateInverses();
+
+	}
+	else {
+
+		if ( this.bones.length === boneInverses.length ) {
+
+			this.boneInverses = boneInverses.slice( 0 );
+
+		}
+		else {
+
+			console.warn( 'THREE.Skeleton bonInverses is the wrong length.' );
+
+			this.boneInverses = [];
+
+			for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+				this.boneInverses.push( new THREE.Matrix4() );
+
+			}
+
+		}
+
+	}
+
+};
+
+THREE.Skeleton.prototype.calculateInverses = function () {
+
+	this.boneInverses = [];
+
+	for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+		var inverse = new THREE.Matrix4();
+
+		if ( this.bones[ b ] ) {
+
+			inverse.getInverse( this.bones[ b ].matrixWorld );
+
+		}
+
+		this.boneInverses.push( inverse );
+
+	}
+
+};
+
+THREE.Skeleton.prototype.pose = function () {
+
+	var bone;
+
+	// recover the bind-time world matrices
+
+	for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+		bone = this.bones[ b ];
+
+		if ( bone ) {
+
+			bone.matrixWorld.getInverse( this.boneInverses[ b ] );
+
+		}
+
+	}
+
+	// compute the local matrices, positions, rotations and scales
+
+	for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+		bone = this.bones[ b ];
+
+		if ( bone ) {
+
+			if ( bone.parent ) {
+
+				bone.matrix.getInverse( bone.parent.matrixWorld );
+				bone.matrix.multiply( bone.matrixWorld );
+
+			}
+			else {
+
+				bone.matrix.copy( bone.matrixWorld );
+
+			}
+
+			bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
+
+		}
+
+	}
+
+};
+
+THREE.Skeleton.prototype.update = function () {
+
+	var offsetMatrix = new THREE.Matrix4();
+
+	// flatten bone matrices to array
+
+	for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+		// compute the offset between the current and the original transform
+
+		var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix;
+
+		offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] );
+		offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );
+
+	}
+
+	if ( this.useVertexTexture ) {
+
+		this.boneTexture.needsUpdate = true;
+
+	}
+
+};
+

+ 43 - 140
src/objects/SkinnedMesh.js

@@ -1,31 +1,28 @@
 /**
  * @author mikael emtinger / http://gomo.se/
  * @author alteredq / http://alteredqualia.com/
+ * @author ikerr / http://verold.com
  */
 
 THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
 
 	THREE.Mesh.call( this, geometry, material );
 
-	this.useVertexTexture = useVertexTexture;
-
-	this.identityMatrix = new THREE.Matrix4();
-
-	this.bones = [];
-
-	this.boneMatrices = null;
+	this.bindMode = "attached";
+	this.bindMatrix = new THREE.Matrix4();
+	this.bindMatrixInverse = new THREE.Matrix4();
 
 	// init bones
 
 	// TODO: remove bone creation as there is no reason (other than
 	// convenience) for THREE.SkinnedMesh to do this.
 
-	var bone, gbone, p, q, s;
-
 	var bones = [];
 
 	if ( this.geometry && this.geometry.bones !== undefined ) {
 
+		var bone, gbone, p, q, s;
+
 		for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) {
 
 			gbone = this.geometry.bones[ b ];
@@ -34,7 +31,7 @@ THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
 			q = gbone.rotq;
 			s = gbone.scl;
 
-			bone = new Bone( this );
+			bone = new THREE.Bone( this );
 			bones.push( bone );
 
 			bone.name = gbone.name;
@@ -61,160 +58,46 @@ THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
 
 				bones[ gbone.parent ].add( bones[ b ] );
 
-			}
-
-		}
-
-	}
-
-	this.bind( bones );
-
-};
-
-
-THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
-
-THREE.SkinnedMesh.prototype.bind = function( bones ) {
-
-	bones = bones || [];
-	this.bones = bones.slice( 0 );
-	this.boneInverses = undefined;
-
-	// update bone matrices and/or texture
-
-	this.initBoneMatrices();
-
-	// update bone inverses
-
-	this.pose();
-
-};
-
-THREE.SkinnedMesh.prototype.initBoneInverses = function () {
-
-	this.boneInverses = [];
-
-	for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
-
-		var inverse = new THREE.Matrix4();
+			} else {
 
-		if ( this.bones[ b ] ) {
+				this.add( bones[ b ] );
 
-			inverse.getInverse( this.bones[ b ].matrixWorld );
+			}
 
 		}
 
-		inverse.multiply( this.matrixWorld );
-
-		this.boneInverses.push( inverse );
-
-	}
-
-};
-
-THREE.SkinnedMesh.prototype.initBoneMatrices = function () {
-
-	var nBones = this.bones.length;
-
-	// clear the old bone texture and float array
-
-	this.boneMatrices = null;
-	this.boneTextureWidth = undefined;
-	this.boneTextureHeight = undefined;
-
-	if ( this.boneTexture ) {
-
-		this.boneTexture.dispose();
-		this.boneTexture = undefined;
-
 	}
 
-	// create a bone texture or an array of floats
-
-	if ( this.useVertexTexture ) {
-
-		// layout (1 matrix = 4 pixels)
-		//      RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
-		//  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;
-
-		this.boneTextureWidth = size;
-		this.boneTextureHeight = size;
-
-		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 {
-
-		this.boneMatrices = new Float32Array( 16 * nBones );
+	this.normalizeSkinWeights();
 
-	}
+	this.updateMatrixWorld( true );
+	this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ) );
 
 };
 
-THREE.SkinnedMesh.prototype.updateBoneMatrices = function () {
-
-	var offsetMatrix = new THREE.Matrix4();
-
-	var invMatrixWorld = new THREE.Matrix4().getInverse( this.matrixWorld );
-
-	// make a snapshot of the bones' rest position
 
-	if ( this.boneInverses === undefined ) {
-
-		this.initBoneInverses();
-
-	}
+THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
 
-	// flatten bone matrices to array
+THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) {
 
-	for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+	this.skeleton = skeleton;
 
-		// compute the offset between the current and the original transform;
+	if ( bindMatrix === undefined ) {
 
-		var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix;
+		this.updateMatrixWorld( true );
 
-		offsetMatrix.multiplyMatrices( invMatrixWorld, matrix );
-		offsetMatrix.multiply( this.boneInverses[ b ] );
-		offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );
+		bindMatrix = this.matrixWorld;
 
 	}
 
-	if ( this.useVertexTexture ) {
-
-		this.boneTexture.needsUpdate = true;
-
-	}
+	this.bindMatrix.copy( bindMatrix );
+	this.bindMatrixInverse.getInverse( bindMatrix );
 
 };
 
 THREE.SkinnedMesh.prototype.pose = function () {
 
-	this.updateMatrixWorld( true );
-
-	// force recomputation of bone inverses
-
-	this.boneInverses = undefined;
-	this.updateBoneMatrices();
-
-	this.normalizeSkinWeights();
+	this.skeleton.pose();
 
 };
 
@@ -248,7 +131,27 @@ THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () {
 
 };
 
-THREE.SkinnedMesh.prototype.clone = function ( object ) {
+THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) {
+
+	THREE.Mesh.prototype.updateMatrixWorld.call( this, true );
+
+	if ( this.bindMode === "attached" ) {
+
+		this.bindMatrixInverse.getInverse( this.matrixWorld );
+
+	} else if ( this.bindMode === "detached" ) {
+
+		this.bindMatrixInverse.getInverse( this.bindMatrix );
+
+	} else {
+
+		console.warn( 'THREE.SkinnedMesh unreckognized bindMode: ' + this.bindMode );
+
+	}
+
+};
+
+THREE.SkinnedMesh.prototype.clone = function( object ) {
 
 	if ( object === undefined ) {
 

+ 28 - 16
src/renderers/WebGLRenderer.js

@@ -3255,24 +3255,24 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		if ( camera.parent === undefined ) camera.updateMatrixWorld();
 
-		// update SkinnedMesh objects
-		function updateSkinnedMesh( object ) {
+		// update Skeleton objects
+		function updateSkeletons( object ) {
 
 			if ( object instanceof THREE.SkinnedMesh ) {
 
-				object.updateBoneMatrices();
+				object.skeleton.update();
 
 			}
 
 			for ( var i = 0, l = object.children.length; i < l; i ++ ) {
 
-				updateSkinnedMesh( object.children[ i ] );
+				updateSkeletons( object.children[ i ] );
 
 			}
 
 		}
 
-		updateSkinnedMesh( scene );
+		updateSkeletons( scene );
 
 		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
 
@@ -4127,7 +4127,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			skinning: material.skinning,
 			maxBones: maxBones,
-			useVertexTexture: _supportsBoneTextures && object && object.useVertexTexture,
+			useVertexTexture: _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture,
 
 			morphTargets: material.morphTargets,
 			morphNormals: material.morphNormals,
@@ -4340,34 +4340,46 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		if ( material.skinning ) {
 
-			if ( _supportsBoneTextures && object.useVertexTexture ) {
+			if ( object.bindMatrix && p_uniforms.bindMatrix !== null ) {
+
+				_gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements );
+
+			}
+
+			if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== null ) {
+
+				_gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements );
+
+			}
+
+			if ( _supportsBoneTextures && object.skeleton && object.skeleton.useVertexTexture ) {
 
 				if ( p_uniforms.boneTexture !== null ) {
 
 					var textureUnit = getTextureUnit();
 
 					_gl.uniform1i( p_uniforms.boneTexture, textureUnit );
-					_this.setTexture( object.boneTexture, textureUnit );
+					_this.setTexture( object.skeleton.boneTexture, textureUnit );
 
 				}
 
 				if ( p_uniforms.boneTextureWidth !== null ) {
 
-					_gl.uniform1i( p_uniforms.boneTextureWidth, object.boneTextureWidth );
+					_gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth );
 
 				}
 
 				if ( p_uniforms.boneTextureHeight !== null ) {
 
-					_gl.uniform1i( p_uniforms.boneTextureHeight, object.boneTextureHeight );
+					_gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight );
 
 				}
 
-			} else {
+			} else if ( object.skeleton && object.skeleton.boneMatrices ) {
 
 				if ( p_uniforms.boneGlobalMatrices !== null ) {
 
-					_gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.boneMatrices );
+					_gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices );
 
 				}
 
@@ -6016,7 +6028,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	function allocateBones ( object ) {
 
-		if ( _supportsBoneTextures && object && object.useVertexTexture ) {
+		if ( _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture ) {
 
 			return 1024;
 
@@ -6037,11 +6049,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			if ( object !== undefined && object instanceof THREE.SkinnedMesh ) {
 
-				maxBones = Math.min( object.bones.length, maxBones );
+				maxBones = Math.min( object.skeleton.bones.length, maxBones );
 
-				if ( maxBones < object.bones.length ) {
+				if ( maxBones < object.skeleton.bones.length ) {
 
-					console.warn( "WebGLRenderer: too many bones - " + object.bones.length + ", this GPU supports just " + maxBones + " (try OpenGL instead of ANGLE)" );
+					console.warn( "WebGLRenderer: too many bones - " + object.skeleton.bones.length + ", this GPU supports just " + maxBones + " (try OpenGL instead of ANGLE)" );
 
 				}
 

+ 7 - 2
src/renderers/shaders/ShaderChunk.js

@@ -1130,6 +1130,9 @@ THREE.ShaderChunk = {
 
 		"#ifdef USE_SKINNING",
 
+		"	uniform mat4 bindMatrix;",
+		"	uniform mat4 bindMatrixInverse;",
+
 		"	#ifdef BONE_TEXTURE",
 
 		"		uniform sampler2D boneTexture;",
@@ -1194,11 +1197,11 @@ THREE.ShaderChunk = {
 
 		"	#ifdef USE_MORPHTARGETS",
 
-		"	vec4 skinVertex = vec4( morphed, 1.0 );",
+		"	vec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );",
 
 		"	#else",
 
-		"	vec4 skinVertex = vec4( position, 1.0 );",
+		"	vec4 skinVertex = bindMatrix * vec4( position, 1.0 );",
 
 		"	#endif",
 
@@ -1207,6 +1210,7 @@ THREE.ShaderChunk = {
 		"	skinned += boneMatY * skinVertex * skinWeight.y;",
 		"	skinned += boneMatZ * skinVertex * skinWeight.z;",
 		"	skinned += boneMatW * skinVertex * skinWeight.w;",
+		"	skinned  = bindMatrixInverse * skinned;",
 
 		"#endif"
 
@@ -1309,6 +1313,7 @@ THREE.ShaderChunk = {
 		"	skinMatrix += skinWeight.y * boneMatY;",
 		"	skinMatrix += skinWeight.z * boneMatZ;",
 		"	skinMatrix += skinWeight.w * boneMatW;",
+		"	skinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;",
 
 		"	#ifdef USE_MORPHNORMALS",
 

+ 4 - 2
src/renderers/shaders/ShaderLib.js

@@ -1121,13 +1121,14 @@ THREE.ShaderLib = {
 
 			"			#ifdef USE_SKINNING",
 
-			"				vec4 skinVertex = vec4( position, 1.0 );",
+			"				vec4 skinVertex = bindMatrix * vec4( position, 1.0 );",
 
 			"				vec4 skinned = vec4( 0.0 );",
 			"				skinned += boneMatX * skinVertex * skinWeight.x;",
 			"				skinned += boneMatY * skinVertex * skinWeight.y;",
 			"				skinned += boneMatZ * skinVertex * skinWeight.z;",
 			"				skinned += boneMatW * skinVertex * skinWeight.w;",
+			"				skinned  = bindMatrixInverse * skinned;",
 
 			"				displacedPosition = skinned.xyz;",
 
@@ -1143,13 +1144,14 @@ THREE.ShaderLib = {
 
 			"		#ifdef USE_SKINNING",
 
-			"			vec4 skinVertex = vec4( position, 1.0 );",
+			"			vec4 skinVertex = bindMatrix * vec4( position, 1.0 );",
 
 			"			vec4 skinned = vec4( 0.0 );",
 			"			skinned += boneMatX * skinVertex * skinWeight.x;",
 			"			skinned += boneMatY * skinVertex * skinWeight.y;",
 			"			skinned += boneMatZ * skinVertex * skinWeight.z;",
 			"			skinned += boneMatW * skinVertex * skinWeight.w;",
+			"			skinned  = bindMatrixInverse * skinned;",
 
 			"			displacedPosition = skinned.xyz;",
 

+ 1 - 2
src/renderers/webgl/WebGLProgram.js

@@ -298,8 +298,7 @@ THREE.WebGLProgram = ( function () {
 
 		var identifiers = [
 
-			'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition',
-			'morphTargetInfluences'
+			'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition', 'morphTargetInfluences', 'bindMatrix', 'bindMatrixInverse'
 
 		];