Browse Source

This commit improves the flexibility of the animation system by allowing any
Object3D instace to be used for skinning--not just Bone instances. Furthermore,
this commit removes the restriction that a SkinnedMesh can only be skinned by
child objects/bones. Now, a SkinnedMesh can reference arbitrary objects for the
purpose of skinning. One implication of this is that Bone.skinMatrix has been
removed as it is no longer necessary.

Here is a breakdown of the notable changes:

src/extras/animation/Animation.js
* Fixed animation of non-bones
* Removed references to skinMatrix

src/extras/animation/AnimationHandler.js
* Removed special handling of SkinnedMatrix instances

src/extras/animation/KeyFrameAnimation.js
* Removed special handling of Bone instances
* Removed references to skinMatrix

src/extras/helpers/SkeletonHelper.js
* Removed references to skinMatrix and Skeleton

src/objects/Bone.js
* Removed references to skinMatrix

src/objects/Skeleton.js
* Removed entirely, refactoring functionality back into SkinnedMesh

src/objects/SkinnedMesh.js
* Re-added code to construct the skeleton hierarchy when it is present in the geometry object that is passed to the constructor. This is here for backwards compatibility and could be refactored at a later time.
* Added bind() function that takes an array of bones (Object3D instances) that are used to skin the mesh. This function establishes the initial bind transforms of the mesh and bones.
* Added initBoneInverses() function that is used internally by bind() to calculate the bind matrices.
* Added initBoneMatrices(), which contains refactored code that previously existed in the constructor (prior to the THREE.Skeleton refactor). This code sets up storage for the bone matrices and/or texture. It is used internally by bind().
* Added updateBoneMatrices(), which is called by the renderer after the scene's world matrices have been updated. This is done in a separate pass because SkinnedMesh can depend on objects that are stored anywhere in the scene hierarchy, not necessarily child objects.

src/renderers/WebGLRenderer.js
* Call updateBoneMatrices() on SkinnedMesh instances after world matrices have been updated
* Removed references to Skeleton

Ian Kerr 11 năm trước cách đây
mục cha
commit
aff4c1c2a1

+ 165 - 279
build/three.js

@@ -6,6 +6,11 @@
 
 var THREE = { REVISION: '68dev' };
 
+// browserify support
+if (typeof module === 'object') {
+	module.exports = THREE;
+}
+
 self.console = self.console || {
 
 	info: function () {},
@@ -14429,7 +14434,6 @@ THREE.Bone = function( belongsToSkin ) {
 	THREE.Object3D.call( this );
 
 	this.skin = belongsToSkin;
-	this.skinMatrix = new THREE.Matrix4();
 
 	this.accumulatedRotWeight = 0;
 	this.accumulatedPosWeight = 0;
@@ -14439,7 +14443,7 @@ THREE.Bone = function( belongsToSkin ) {
 
 THREE.Bone.prototype = Object.create( THREE.Object3D.prototype );
 
-THREE.Bone.prototype.update = function ( parentSkinMatrix, forceUpdate ) {
+THREE.Bone.prototype.update = function ( forceUpdate ) {
 
 	// update local
 
@@ -14453,16 +14457,6 @@ THREE.Bone.prototype.update = function ( parentSkinMatrix, forceUpdate ) {
 
 	if ( forceUpdate || this.matrixWorldNeedsUpdate ) {
 
-		if ( parentSkinMatrix ) {
-
-			this.skinMatrix.multiplyMatrices( parentSkinMatrix, this.matrix );
-
-		} else {
-
-			this.skinMatrix.copy( this.matrix );
-
-		}
-
 		this.matrixWorldNeedsUpdate = false;
 		forceUpdate = true;
 
@@ -14478,7 +14472,7 @@ THREE.Bone.prototype.update = function ( parentSkinMatrix, forceUpdate ) {
 
 	for ( var i = 0, l = this.children.length; i < l; i ++ ) {
 
-		this.children[ i ].update( this.skinMatrix, forceUpdate );
+		this.children[ i ].update( forceUpdate );
 
 	}
 
@@ -14488,31 +14482,41 @@ THREE.Bone.prototype.update = function ( parentSkinMatrix, forceUpdate ) {
 /**
  * @author mikael emtinger / http://gomo.se/
  * @author alteredq / http://alteredqualia.com/
- * @author michael guerrero / http://realitymeltdown.com
  */
 
-THREE.Skeleton = function ( boneList, useVertexTexture ) {
+THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
 
-	this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;
+	THREE.Mesh.call( this, geometry, material );
 
-	// init bones
+	this.useVertexTexture = useVertexTexture;
+
+	this.identityMatrix = new THREE.Matrix4();
 
 	this.bones = [];
-	this.boneMatrices = [];
+
+	this.boneMatrices = null;
+
+	// 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;
 
-	if ( boneList !== undefined ) {
+	var bones = [];
 
-		for ( var b = 0; b < boneList.length; ++b ) {
+	if ( this.geometry && this.geometry.bones !== undefined ) {
 
-			gbone = boneList[ b ];
+		for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) {
+
+			gbone = this.geometry.bones[ b ];
 
 			p = gbone.pos;
 			q = gbone.rotq;
 			s = gbone.scl;
 
-			bone = this.addBone();
+			bone = new Bone( this );
+			bones.push( bone );
 
 			bone.name = gbone.name;
 			bone.position.set( p[0], p[1], p[2] );
@@ -14530,214 +14534,167 @@ THREE.Skeleton = function ( boneList, useVertexTexture ) {
 
 		}
 
-		for ( var b = 0; b < boneList.length; ++b ) {
+		for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) {
 
-			gbone = boneList[ b ];
+			gbone = this.geometry.bones[ b ];
 
 			if ( gbone.parent !== -1 ) {
 
-				this.bones[ gbone.parent ].add( this.bones[ b ] );
+				bones[ gbone.parent ].add( bones[ b ] );
 
 			}
 
 		}
 
-		//
-
-		var nBones = this.bones.length;
-
-		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.bind( bones );
 
+};
 
-THREE.Skeleton.prototype = Object.create( THREE.Mesh.prototype );
 
+THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
 
-THREE.Skeleton.prototype.addBone = function( bone ) {
+THREE.SkinnedMesh.prototype.bind = function( bones ) {
 
-	if ( bone === undefined ) {
+	bones = bones || [];
+	this.bones = bones.slice( 0 );
+	this.boneInverses = undefined;
 
-		bone = new THREE.Bone( this );
+	// update bone matrices and/or texture
 
-	}
+	this.initBoneMatrices();
 
-	this.bones.push( bone );
+	// update bone inverses
 
-	return bone;
+	this.pose();
 
 };
 
-
-THREE.Skeleton.prototype.calculateInverses = function( bone ) {
+THREE.SkinnedMesh.prototype.initBoneInverses = function () {
 
 	this.boneInverses = [];
 
-	for ( var b = 0, bl = this.bones.length; b < bl; ++b ) {
+	for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
 
 		var inverse = new THREE.Matrix4();
 
-		inverse.getInverse( this.bones[ b ].skinMatrix );
+		if ( this.bones[ b ] ) {
 
-		this.boneInverses.push( inverse );
-
-	}
-
-};
-
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- */
-
-THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
-
-	THREE.Mesh.call( this, geometry, material );
-
-	this.skeleton = new THREE.Skeleton( this.geometry && this.geometry.bones, useVertexTexture );
-
-  // Add root level bones as children of the mesh
-
-	for ( var b = 0; b < this.skeleton.bones.length; ++b ) {
-
-		var bone = this.skeleton.bones[ b ];
-
-		if ( bone.parent === undefined ) {
-
-			this.add( bone );
+			inverse.getInverse( this.bones[ b ].matrixWorld );
 
 		}
 
-	}
+		inverse.multiply( this.matrixWorld );
 
-	this.identityMatrix = new THREE.Matrix4();
+		this.boneInverses.push( inverse );
 
-	this.pose();
+	}
 
 };
 
+THREE.SkinnedMesh.prototype.initBoneMatrices = function () {
 
-THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
-
-THREE.SkinnedMesh.prototype.updateMatrixWorld = function () {
-
-	var offsetMatrix = new THREE.Matrix4();
-
-	return function ( force ) {
+	var nBones = this.bones.length;
 
-		this.matrixAutoUpdate && this.updateMatrix();
+	// clear the old bone texture and float array
 
-		// update matrixWorld
+	this.boneMatrices = null;
+	this.boneTextureWidth = undefined;
+	this.boneTextureHeight = undefined;
 
-		if ( this.matrixWorldNeedsUpdate || force ) {
+	if ( this.boneTexture ) {
 
-			if ( this.parent ) {
+		this.boneTexture.dispose();
+		this.boneTexture = undefined;
 
-				this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+	}
 
-			} else {
+	// create a bone texture or an array of floats
 
-				this.matrixWorld.copy( this.matrix );
+	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)
 
-			this.matrixWorldNeedsUpdate = false;
+		var size;
 
-			force = true;
-
-		}
-
-		// update children
+		if ( nBones > 256 )
+			size = 64;
+		else if ( nBones > 64 )
+			size = 32;
+		else if ( nBones > 16 )
+			size = 16;
+		else
+			size = 8;
 
-		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+		this.boneTextureWidth = size;
+		this.boneTextureHeight = size;
 
-			var child = this.children[ i ];
+		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;
 
-			if ( child instanceof THREE.Bone ) {
+	} else {
 
-				child.update( this.identityMatrix, false );
+		this.boneMatrices = new Float32Array( 16 * nBones );
 
-			} else {
+	}
 
-				child.updateMatrixWorld( true );
+};
 
-			}
+THREE.SkinnedMesh.prototype.updateBoneMatrices = function () {
 
-		}
+	var offsetMatrix = new THREE.Matrix4();
 
-		// make a snapshot of the bones' rest position
+	var invMatrixWorld = new THREE.Matrix4().getInverse( this.matrixWorld );
 
-		if ( this.skeleton.boneInverses === undefined ) {
+	// make a snapshot of the bones' rest position
 
-			this.skeleton.calculateInverses();
+	if ( this.boneInverses === undefined ) {
 
-		}
+		this.initBoneInverses();
 
-		// flatten bone matrices to array
+	}
 
-		for ( var b = 0, bl = this.skeleton.bones.length; b < bl; b ++ ) {
+	// flatten bone matrices to array
 
-			// compute the offset between the current and the original transform;
+	for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
 
-			// 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
+		// compute the offset between the current and the original transform;
 
-			offsetMatrix.multiplyMatrices( this.skeleton.bones[ b ].skinMatrix, this.skeleton.boneInverses[ b ] );
-			offsetMatrix.flattenToArrayOffset( this.skeleton.boneMatrices, b * 16 );
+		var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix;
 
-		}
+		offsetMatrix.multiplyMatrices( invMatrixWorld, matrix );
+		offsetMatrix.multiply( this.boneInverses[ b ] );
+		offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );
 
-		if ( this.skeleton.useVertexTexture ) {
+	}
 
-			this.skeleton.boneTexture.needsUpdate = true;
+	if ( this.useVertexTexture ) {
 
-		}
+		this.boneTexture.needsUpdate = true;
 
-	};
+	}
 
-}();
+};
 
 THREE.SkinnedMesh.prototype.pose = function () {
 
 	this.updateMatrixWorld( true );
 
+	// force recomputation of bone inverses
+
+	this.boneInverses = undefined;
+	this.updateBoneMatrices();
+
 	this.normalizeSkinWeights();
 
 };
@@ -14786,6 +14743,7 @@ THREE.SkinnedMesh.prototype.clone = function ( object ) {
 
 };
 
+
 /**
  * @author alteredq / http://alteredqualia.com/
  */
@@ -14845,7 +14803,7 @@ THREE.MorphAnimMesh.prototype.parseAnimations = function () {
 
 	var firstAnimation, animations = geometry.animations;
 
-	var pattern = /([a-z]+)(\d+)/;
+	var pattern = /([a-z]+)_?(\d+)/;
 
 	for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
 
@@ -20571,10 +20529,10 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					var size = 1;   // "f" and "i"
 
-					if( attribute.type === "v2" ) size = 2;
-					else if( attribute.type === "v3" ) size = 3;
-					else if( attribute.type === "v4" ) size = 4;
-					else if( attribute.type === "c"  ) size = 3;
+					if ( attribute.type === "v2" ) size = 2;
+					else if ( attribute.type === "v3" ) size = 3;
+					else if ( attribute.type === "v4" ) size = 4;
+					else if ( attribute.type === "c"  ) size = 3;
 
 					attribute.size = size;
 
@@ -22044,7 +22002,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 					normalArray,
 					i, il = object.count * 3;
 
-				for( i = 0; i < il; i += 9 ) {
+				for ( i = 0; i < il; i += 9 ) {
 
 					normalArray = object.normalArray;
 
@@ -22307,7 +22265,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				var type, size;
 
-				if ( index.array instanceof Uint32Array ){
+				if ( index.array instanceof Uint32Array ) {
 
 					type = _gl.UNSIGNED_INT;
 					size = 4;
@@ -22855,6 +22813,25 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		if ( camera.parent === undefined ) camera.updateMatrixWorld();
 
+		// update SkinnedMesh objects
+		function updateSkinnedMesh( object ) {
+
+			if ( object instanceof THREE.SkinnedMesh ) {
+
+				object.updateBoneMatrices();
+
+			}
+
+			for ( var i = 0, l = object.children.length; i < l; i ++ ) {
+
+				updateSkinnedMesh( object.children[ i ] );
+
+			}
+
+		}
+
+		updateSkinnedMesh( scene );
+
 		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
 
 		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
@@ -23472,7 +23449,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			// check all geometry groups
 
-			for( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) {
+			for ( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) {
 
 				geometryGroup = geometry.geometryGroupsList[ i ];
 
@@ -23708,7 +23685,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			skinning: material.skinning,
 			maxBones: maxBones,
-			useVertexTexture: _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture,
+			useVertexTexture: _supportsBoneTextures && object && object.useVertexTexture,
 
 			morphTargets: material.morphTargets,
 			morphNormals: material.morphNormals,
@@ -23921,26 +23898,26 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		if ( material.skinning ) {
 
-			if ( _supportsBoneTextures && object.skeleton.useVertexTexture ) {
+			if ( _supportsBoneTextures && object.useVertexTexture ) {
 
 				if ( p_uniforms.boneTexture !== null ) {
 
 					var textureUnit = getTextureUnit();
 
 					_gl.uniform1i( p_uniforms.boneTexture, textureUnit );
-					_this.setTexture( object.skeleton.boneTexture, textureUnit );
+					_this.setTexture( object.boneTexture, textureUnit );
 
 				}
 
 				if ( p_uniforms.boneTextureWidth !== null ) {
 
-					_gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth );
+					_gl.uniform1i( p_uniforms.boneTextureWidth, object.boneTextureWidth );
 
 				}
 
 				if ( p_uniforms.boneTextureHeight !== null ) {
 
-					_gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight );
+					_gl.uniform1i( p_uniforms.boneTextureHeight, object.boneTextureHeight );
 
 				}
 
@@ -23948,7 +23925,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				if ( p_uniforms.boneGlobalMatrices !== null ) {
 
-					_gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices );
+					_gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.boneMatrices );
 
 				}
 
@@ -24528,7 +24505,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				}
 
-				for( i = 0, il = uniform.value.length; i < il; i ++ ) {
+				for ( i = 0, il = uniform.value.length; i < il; i ++ ) {
 
 					uniform._array[ i ] = getTextureUnit();
 
@@ -24536,7 +24513,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				_gl.uniform1iv( location, uniform._array );
 
-				for( i = 0, il = uniform.value.length; i < il; i ++ ) {
+				for ( i = 0, il = uniform.value.length; i < il; i ++ ) {
 
 					texture = uniform.value[ i ];
 					textureUnit = uniform._array[ i ];
@@ -25135,10 +25112,10 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			} else if ( texture instanceof THREE.CompressedTexture ) {
 
-				for( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+				for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
 
 					mipmap = mipmaps[ i ];
-					if ( texture.format!==THREE.RGBAFormat ) {
+					if ( texture.format !== THREE.RGBAFormat ) {
 						_gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
 					} else {
 						_gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
@@ -25260,7 +25237,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				for ( var i = 0; i < 6; i ++ ) {
 
-					if( !isCompressed ) {
+					if ( !isCompressed ) {
 
 						_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );
 
@@ -25268,10 +25245,10 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						var mipmap, mipmaps = cubeImage[ i ].mipmaps;
 
-						for( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {
+						for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {
 
 							mipmap = mipmaps[ j ];
-							if ( texture.format!==THREE.RGBAFormat ) {
+							if ( texture.format !== THREE.RGBAFormat ) {
 
 								_gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
 
@@ -25330,7 +25307,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 			_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
 
 		/* For some reason this is not working. Defaulting to RGBA4.
-		} else if( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
+		} else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
 
 			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height );
 			_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
@@ -25597,7 +25574,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	function allocateBones ( object ) {
 
-		if ( _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture ) {
+		if ( _supportsBoneTextures && object && object.useVertexTexture ) {
 
 			return 1024;
 
@@ -25618,11 +25595,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			if ( object !== undefined && object instanceof THREE.SkinnedMesh ) {
 
-				maxBones = Math.min( object.skeleton.bones.length, maxBones );
+				maxBones = Math.min( object.bones.length, maxBones );
 
-				if ( maxBones < object.skeleton.bones.length ) {
+				if ( maxBones < object.bones.length ) {
 
-					console.warn( "WebGLRenderer: too many bones - " + object.skeleton.bones.length + ", this GPU supports just " + maxBones + " (try OpenGL instead of ANGLE)" );
+					console.warn( "WebGLRenderer: too many bones - " + object.bones.length + ", this GPU supports just " + maxBones + " (try OpenGL instead of ANGLE)" );
 
 				}
 
@@ -25662,7 +25639,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		var maxShadows = 0;
 
-		for ( var l = 0, ll = lights.length; l < ll; l++ ) {
+		for ( var l = 0, ll = lights.length; l < ll; l ++ ) {
 
 			var light = lights[ l ];
 
@@ -30337,19 +30314,7 @@ THREE.AnimationHandler = {
 
 		var hierarchy = [];
 
-		if ( root instanceof THREE.SkinnedMesh ) {
-
-			for ( var b = 0; b < root.skeleton.bones.length; b++ ) {
-
-				hierarchy.push( root.skeleton.bones[ b ] );
-
-			}
-
-		} else {
-
-			parseRecurseHierarchy( root, hierarchy );
-
-		}
+		parseRecurseHierarchy( root, hierarchy );
 
 		return hierarchy;
 
@@ -30457,7 +30422,7 @@ THREE.Animation.prototype.reset = function () {
 			object.animationCache[this.data.name] = {};
 			object.animationCache[this.data.name].prevKey = { pos: 0, rot: 0, scl: 0 };
 			object.animationCache[this.data.name].nextKey = { pos: 0, rot: 0, scl: 0 };
-			object.animationCache[this.data.name].originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
+			object.animationCache[this.data.name].originalMatrix = object.matrix;
 
 		}
 
@@ -30626,7 +30591,7 @@ THREE.Animation.prototype.update = (function(){
 							object.accumulatedPosWeight += this.weight;
 
 						} else
-							vector = newVector;
+							vector.copy( newVector );
 
 					} else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
 						this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
@@ -30709,7 +30674,7 @@ THREE.Animation.prototype.update = (function(){
 						object.accumulatedSclWeight += this.weight;
 
 					} else
-						vector = newVector;
+						vector.copy( newVector );
 
 				}
 
@@ -30867,7 +30832,7 @@ THREE.KeyFrameAnimation.prototype.play = function ( startTime ) {
 				node.animationCache = {};
 				node.animationCache.prevKey = null;
 				node.animationCache.nextKey = null;
-				node.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
+				node.animationCache.originalMatrix = object.matrix;
 
 			}
 
@@ -30914,17 +30879,8 @@ THREE.KeyFrameAnimation.prototype.stop = function() {
 
 			var original = node.animationCache.originalMatrix;
 
-			if( obj instanceof THREE.Bone ) {
-
-				original.copy( obj.skinMatrix );
-				obj.skinMatrix = original;
-
-			} else {
-
-				original.copy( obj.matrix );
-				obj.matrix = original;
-
-			}
+			original.copy( obj.matrix );
+			obj.matrix = original;
 
 			delete node.animationCache;
 
@@ -34750,76 +34706,6 @@ THREE.PointLightHelper.prototype.update = function () {
 };
 
 
-/**
- * @author Sean Griffin / http://twitter.com/sgrif
- * @author Michael Guerrero / http://realitymeltdown.com
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.SkeletonHelper = function ( object ) {
-
-	var skeleton = object.skeleton;
-
-	var geometry = new THREE.Geometry();
-
-	for ( var i = 0; i < skeleton.bones.length; i ++ ) {
-
-		var bone = skeleton.bones[ i ];
-
-		if ( bone.parent instanceof THREE.Bone ) {
-
-			geometry.vertices.push( new THREE.Vector3() );
-			geometry.vertices.push( new THREE.Vector3() );
-			geometry.colors.push( new THREE.Color( 0, 0, 1 ) );
-			geometry.colors.push( new THREE.Color( 0, 1, 0 ) );
-
-		}
-
-	}
-
-	var material = new THREE.LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, transparent: true } );
-
-	THREE.Line.call( this, geometry, material, THREE.LinePieces );
-
-	this.skeleton = skeleton;
-
-	this.matrixWorld = object.matrixWorld;
-	this.matrixAutoUpdate = false;
-
-	this.update();
-
-};
-
-
-THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype );
-
-THREE.SkeletonHelper.prototype.update = function () {
-
-	var geometry = this.geometry;
-
-	var j = 0;
-
-	for ( var i = 0; i < this.skeleton.bones.length; i ++ ) {
-
-		var bone = this.skeleton.bones[ i ];
-
-		if ( bone.parent instanceof THREE.Bone ) {
-
-			geometry.vertices[ j ].setFromMatrixPosition( bone.skinMatrix );
-			geometry.vertices[ j + 1 ].setFromMatrixPosition( bone.parent.skinMatrix );
-
-			j += 2;
-
-		}
-
-	}
-
-	geometry.verticesNeedUpdate = true;
-
-	geometry.computeBoundingSphere();
-
-};
-
 /**
  * @author alteredq / http://alteredqualia.com/
  * @author mrdoob / http://mrdoob.com/
@@ -35412,7 +35298,7 @@ THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fp
 
 THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) {
 
-	var pattern = /([a-z]+)(\d+)/;
+	var pattern = /([a-z]+)_?(\d+)/;
 
 	var firstAnimation, frameRanges = {};
 

+ 1 - 0
build/three.min.js

@@ -1,4 +1,5 @@
 // three.js / threejs.org/license
+// three.js / threejs.org/license
 'use strict';var THREE={REVISION:"68dev"};self.console=self.console||{info:function(){},log:function(){},debug:function(){},warn:function(){},error:function(){}};
 (function(){for(var a=0,b=["ms","moz","webkit","o"],c=0;c<b.length&&!self.requestAnimationFrame;++c)self.requestAnimationFrame=self[b[c]+"RequestAnimationFrame"],self.cancelAnimationFrame=self[b[c]+"CancelAnimationFrame"]||self[b[c]+"CancelRequestAnimationFrame"];void 0===self.requestAnimationFrame&&void 0!==self.setTimeout&&(self.requestAnimationFrame=function(b){var c=Date.now(),f=Math.max(0,16-(c-a)),g=self.setTimeout(function(){b(c+f)},f);a=c+f;return g});void 0===self.cancelAnimationFrame&&void 0!==
 self.clearTimeout&&(self.cancelAnimationFrame=function(a){self.clearTimeout(a)})})();THREE.CullFaceNone=0;THREE.CullFaceBack=1;THREE.CullFaceFront=2;THREE.CullFaceFrontBack=3;THREE.FrontFaceDirectionCW=0;THREE.FrontFaceDirectionCCW=1;THREE.BasicShadowMap=0;THREE.PCFShadowMap=1;THREE.PCFSoftShadowMap=2;THREE.FrontSide=0;THREE.BackSide=1;THREE.DoubleSide=2;THREE.NoShading=0;THREE.FlatShading=1;THREE.SmoothShading=2;THREE.NoColors=0;THREE.FaceColors=1;THREE.VertexColors=2;THREE.NoBlending=0;

+ 1 - 1
examples/js/BlendCharacter.js

@@ -193,7 +193,7 @@ THREE.BlendCharacter = function () {
 
 			if ( this.animations[ a ].isPlaying ) {
 
-				this.animations[ a ].pause();
+				this.animations[ a ].stop();
 
 			}
 

+ 1 - 2
examples/webgldeferred_animation.html

@@ -255,7 +255,6 @@
 					geometry.computeBoundingBox();
 
 					ensureLoop( geometry.animation );
-					THREE.AnimationHandler.add( geometry.animation );
 
 					for ( var i = 0, il = materials.length; i < il; i ++ ) {
 
@@ -292,7 +291,7 @@
 					skins.push( mesh );
 
 
-					animation = new THREE.Animation( mesh, "ActionFemale" );
+					animation = new THREE.Animation( mesh, geometry.animation );
 					animation.play();
 					animation.update( 0 );
 

+ 3 - 3
src/extras/animation/Animation.js

@@ -66,7 +66,7 @@ THREE.Animation.prototype.reset = function () {
 			object.animationCache[this.data.name] = {};
 			object.animationCache[this.data.name].prevKey = { pos: 0, rot: 0, scl: 0 };
 			object.animationCache[this.data.name].nextKey = { pos: 0, rot: 0, scl: 0 };
-			object.animationCache[this.data.name].originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
+			object.animationCache[this.data.name].originalMatrix = object.matrix;
 
 		}
 
@@ -235,7 +235,7 @@ THREE.Animation.prototype.update = (function(){
 							object.accumulatedPosWeight += this.weight;
 
 						} else
-							vector = newVector;
+							vector.copy( newVector );
 
 					} else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
 						this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
@@ -318,7 +318,7 @@ THREE.Animation.prototype.update = (function(){
 						object.accumulatedSclWeight += this.weight;
 
 					} else
-						vector = newVector;
+						vector.copy( newVector );
 
 				}
 

+ 1 - 13
src/extras/animation/AnimationHandler.js

@@ -149,19 +149,7 @@ THREE.AnimationHandler = {
 
 		var hierarchy = [];
 
-		if ( root instanceof THREE.SkinnedMesh ) {
-
-			for ( var b = 0; b < root.skeleton.bones.length; b++ ) {
-
-				hierarchy.push( root.skeleton.bones[ b ] );
-
-			}
-
-		} else {
-
-			parseRecurseHierarchy( root, hierarchy );
-
-		}
+		parseRecurseHierarchy( root, hierarchy );
 
 		return hierarchy;
 

+ 3 - 12
src/extras/animation/KeyFrameAnimation.js

@@ -75,7 +75,7 @@ THREE.KeyFrameAnimation.prototype.play = function ( startTime ) {
 				node.animationCache = {};
 				node.animationCache.prevKey = null;
 				node.animationCache.nextKey = null;
-				node.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
+				node.animationCache.originalMatrix = object.matrix;
 
 			}
 
@@ -122,17 +122,8 @@ THREE.KeyFrameAnimation.prototype.stop = function() {
 
 			var original = node.animationCache.originalMatrix;
 
-			if( obj instanceof THREE.Bone ) {
-
-				original.copy( obj.skinMatrix );
-				obj.skinMatrix = original;
-
-			} else {
-
-				original.copy( obj.matrix );
-				obj.matrix = original;
-
-			}
+			original.copy( obj.matrix );
+			obj.matrix = original;
 
 			delete node.animationCache;
 

+ 35 - 8
src/extras/helpers/SkeletonHelper.js

@@ -6,13 +6,13 @@
 
 THREE.SkeletonHelper = function ( object ) {
 
-	var skeleton = object.skeleton;
+	this.bones = this.getBoneList( object );
 
 	var geometry = new THREE.Geometry();
 
-	for ( var i = 0; i < skeleton.bones.length; i ++ ) {
+	for ( var i = 0; i < this.bones.length; i ++ ) {
 
-		var bone = skeleton.bones[ i ];
+		var bone = this.bones[ i ];
 
 		if ( bone.parent instanceof THREE.Bone ) {
 
@@ -29,7 +29,7 @@ THREE.SkeletonHelper = function ( object ) {
 
 	THREE.Line.call( this, geometry, material, THREE.LinePieces );
 
-	this.skeleton = skeleton;
+	this.root = object;
 
 	this.matrixWorld = object.matrixWorld;
 	this.matrixAutoUpdate = false;
@@ -41,20 +41,47 @@ THREE.SkeletonHelper = function ( object ) {
 
 THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype );
 
+THREE.SkeletonHelper.prototype.getBoneList = function( object ) {
+
+	var boneList = [];
+
+	if ( object instanceof THREE.Bone ) {
+
+		boneList.push( object );
+
+	}
+
+	for ( var i = 0; i < object.children.length; i ++ ) {
+
+		boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) );
+
+	}
+
+	return boneList;
+
+};
+
 THREE.SkeletonHelper.prototype.update = function () {
 
 	var geometry = this.geometry;
 
+	var matrixWorldInv = new Matrix4().getInverse( this.root.matrixWorld );
+
+	var boneMatrix = new Matrix4();
+
 	var j = 0;
 
-	for ( var i = 0; i < this.skeleton.bones.length; i ++ ) {
+	for ( var i = 0; i < this.bones.length; i ++ ) {
 
-		var bone = this.skeleton.bones[ i ];
+		var bone = this.bones[ i ];
 
 		if ( bone.parent instanceof THREE.Bone ) {
 
-			geometry.vertices[ j ].setFromMatrixPosition( bone.skinMatrix );
-			geometry.vertices[ j + 1 ].setFromMatrixPosition( bone.parent.skinMatrix );
+			boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld );
+			geometry.vertices[ j ].setFromMatrixPosition( boneMatrix );
+
+			boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld );
+			geometry.vertices[ j + 1 ].setFromMatrixPosition( parentMatrix );
 
 			j += 2;
 

+ 2 - 13
src/objects/Bone.js

@@ -8,7 +8,6 @@ THREE.Bone = function( belongsToSkin ) {
 	THREE.Object3D.call( this );
 
 	this.skin = belongsToSkin;
-	this.skinMatrix = new THREE.Matrix4();
 
 	this.accumulatedRotWeight = 0;
 	this.accumulatedPosWeight = 0;
@@ -18,7 +17,7 @@ THREE.Bone = function( belongsToSkin ) {
 
 THREE.Bone.prototype = Object.create( THREE.Object3D.prototype );
 
-THREE.Bone.prototype.update = function ( parentSkinMatrix, forceUpdate ) {
+THREE.Bone.prototype.update = function ( forceUpdate ) {
 
 	// update local
 
@@ -32,16 +31,6 @@ THREE.Bone.prototype.update = function ( parentSkinMatrix, forceUpdate ) {
 
 	if ( forceUpdate || this.matrixWorldNeedsUpdate ) {
 
-		if ( parentSkinMatrix ) {
-
-			this.skinMatrix.multiplyMatrices( parentSkinMatrix, this.matrix );
-
-		} else {
-
-			this.skinMatrix.copy( this.matrix );
-
-		}
-
 		this.matrixWorldNeedsUpdate = false;
 		forceUpdate = true;
 
@@ -57,7 +46,7 @@ THREE.Bone.prototype.update = function ( parentSkinMatrix, forceUpdate ) {
 
 	for ( var i = 0, l = this.children.length; i < l; i ++ ) {
 
-		this.children[ i ].update( this.skinMatrix, forceUpdate );
+		this.children[ i ].update( forceUpdate );
 
 	}
 

+ 0 - 135
src/objects/Skeleton.js

@@ -1,135 +0,0 @@
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- * @author michael guerrero / http://realitymeltdown.com
- */
-
-THREE.Skeleton = function ( boneList, useVertexTexture ) {
-
-	this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;
-
-	// init bones
-
-	this.bones = [];
-	this.boneMatrices = [];
-
-	var bone, gbone, p, q, s;
-
-	if ( boneList !== undefined ) {
-
-		for ( var b = 0; b < boneList.length; ++b ) {
-
-			gbone = boneList[ b ];
-
-			p = gbone.pos;
-			q = gbone.rotq;
-			s = gbone.scl;
-
-			bone = this.addBone();
-
-			bone.name = gbone.name;
-			bone.position.set( p[0], p[1], p[2] );
-			bone.quaternion.set( q[0], q[1], q[2], q[3] );
-
-			if ( s !== undefined ) {
-
-				bone.scale.set( s[0], s[1], s[2] );
-
-			} else {
-
-				bone.scale.set( 1, 1, 1 );
-
-			}
-
-		}
-
-		for ( var b = 0; b < boneList.length; ++b ) {
-
-			gbone = boneList[ b ];
-
-			if ( gbone.parent !== -1 ) {
-
-				this.bones[ gbone.parent ].add( this.bones[ b ] );
-
-			}
-
-		}
-
-		//
-
-		var nBones = this.bones.length;
-
-		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 );
-
-		}
-
-	}
-
-};
-
-
-THREE.Skeleton.prototype = Object.create( THREE.Mesh.prototype );
-
-
-THREE.Skeleton.prototype.addBone = function( bone ) {
-
-	if ( bone === undefined ) {
-
-		bone = new THREE.Bone( this );
-
-	}
-
-	this.bones.push( bone );
-
-	return bone;
-
-};
-
-
-THREE.Skeleton.prototype.calculateInverses = function( bone ) {
-
-	this.boneInverses = [];
-
-	for ( var b = 0, bl = this.bones.length; b < bl; ++b ) {
-
-		var inverse = new THREE.Matrix4();
-
-		inverse.getInverse( this.bones[ b ].skinMatrix );
-
-		this.boneInverses.push( inverse );
-
-	}
-
-};

+ 149 - 49
src/objects/SkinnedMesh.js

@@ -7,114 +7,213 @@ THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
 
 	THREE.Mesh.call( this, geometry, material );
 
-	this.skeleton = new THREE.Skeleton( this.geometry && this.geometry.bones, useVertexTexture );
+	this.useVertexTexture = useVertexTexture;
 
-  // Add root level bones as children of the mesh
+	this.identityMatrix = new THREE.Matrix4();
+
+	this.bones = [];
+
+	this.boneMatrices = null;
+
+	// 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 ) {
+
+		for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) {
 
-	for ( var b = 0; b < this.skeleton.bones.length; ++b ) {
+			gbone = this.geometry.bones[ b ];
 
-		var bone = this.skeleton.bones[ b ];
+			p = gbone.pos;
+			q = gbone.rotq;
+			s = gbone.scl;
 
-		if ( bone.parent === undefined ) {
+			bone = new Bone( this );
+			bones.push( bone );
 
-			this.add( bone );
+			bone.name = gbone.name;
+			bone.position.set( p[0], p[1], p[2] );
+			bone.quaternion.set( q[0], q[1], q[2], q[3] );
+
+			if ( s !== undefined ) {
+
+				bone.scale.set( s[0], s[1], s[2] );
+
+			} else {
+
+				bone.scale.set( 1, 1, 1 );
+
+			}
 
 		}
 
-	}
+		for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) {
 
-	this.identityMatrix = new THREE.Matrix4();
+			gbone = this.geometry.bones[ b ];
 
-	this.pose();
+			if ( gbone.parent !== -1 ) {
+
+				bones[ gbone.parent ].add( bones[ b ] );
+
+			}
+
+		}
+
+	}
+
+	this.bind( bones );
 
 };
 
 
 THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
 
-THREE.SkinnedMesh.prototype.updateMatrixWorld = function () {
+THREE.SkinnedMesh.prototype.bind = function( bones ) {
 
-	var offsetMatrix = new THREE.Matrix4();
+	bones = bones || [];
+	this.bones = bones.slice( 0 );
+	this.boneInverses = undefined;
 
-	return function ( force ) {
+	// update bone matrices and/or texture
 
-		this.matrixAutoUpdate && this.updateMatrix();
+	this.initBoneMatrices();
 
-		// update matrixWorld
+	// update bone inverses
 
-		if ( this.matrixWorldNeedsUpdate || force ) {
+	this.pose();
 
-			if ( this.parent ) {
+};
 
-				this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+THREE.SkinnedMesh.prototype.initBoneInverses = function () {
 
-			} else {
+	this.boneInverses = [];
 
-				this.matrixWorld.copy( this.matrix );
+	for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
 
-			}
+		var inverse = new THREE.Matrix4();
 
-			this.matrixWorldNeedsUpdate = false;
+		if ( this.bones[ b ] ) {
 
-			force = true;
+			inverse.getInverse( this.bones[ b ].matrixWorld );
 
 		}
 
-		// update children
+		inverse.multiply( this.matrixWorld );
 
-		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+		this.boneInverses.push( inverse );
 
-			var child = this.children[ i ];
+	}
 
-			if ( child instanceof THREE.Bone ) {
+};
 
-				child.update( this.identityMatrix, false );
+THREE.SkinnedMesh.prototype.initBoneMatrices = function () {
 
-			} else {
+	var nBones = this.bones.length;
 
-				child.updateMatrixWorld( true );
+	// clear the old bone texture and float array
 
-			}
+	this.boneMatrices = null;
+	this.boneTextureWidth = undefined;
+	this.boneTextureHeight = undefined;
 
-		}
+	if ( this.boneTexture ) {
 
-		// make a snapshot of the bones' rest position
+		this.boneTexture.dispose();
+		this.boneTexture = undefined;
 
-		if ( this.skeleton.boneInverses === undefined ) {
+	}
 
-			this.skeleton.calculateInverses();
+	// create a bone texture or an array of floats
 
-		}
+	if ( this.useVertexTexture ) {
 
-		// flatten bone matrices to array
+		// 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)
 
-		for ( var b = 0, bl = this.skeleton.bones.length; b < bl; b ++ ) {
+		var size;
 
-			// compute the offset between the current and the original transform;
+		if ( nBones > 256 )
+			size = 64;
+		else if ( nBones > 64 )
+			size = 32;
+		else if ( nBones > 16 )
+			size = 16;
+		else
+			size = 8;
 
-			// 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
+		this.boneTextureWidth = size;
+		this.boneTextureHeight = size;
 
-			offsetMatrix.multiplyMatrices( this.skeleton.bones[ b ].skinMatrix, this.skeleton.boneInverses[ b ] );
-			offsetMatrix.flattenToArrayOffset( this.skeleton.boneMatrices, b * 16 );
+		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 {
 
-		if ( this.skeleton.useVertexTexture ) {
+		this.boneMatrices = new Float32Array( 16 * nBones );
 
-			this.skeleton.boneTexture.needsUpdate = true;
+	}
 
-		}
+};
+
+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();
+
+	}
+
+	// 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( invMatrixWorld, matrix );
+		offsetMatrix.multiply( this.boneInverses[ b ] );
+		offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );
+
+	}
+
+	if ( this.useVertexTexture ) {
+
+		this.boneTexture.needsUpdate = true;
+
+	}
+
+};
 
 THREE.SkinnedMesh.prototype.pose = function () {
 
 	this.updateMatrixWorld( true );
 
+	// force recomputation of bone inverses
+
+	this.boneInverses = undefined;
+	this.updateBoneMatrices();
+
 	this.normalizeSkinWeights();
 
 };
@@ -162,3 +261,4 @@ THREE.SkinnedMesh.prototype.clone = function ( object ) {
 	return object;
 
 };
+

+ 29 - 10
src/renderers/WebGLRenderer.js

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

+ 0 - 1
utils/build/includes/canvas.json

@@ -50,7 +50,6 @@
 	"src/objects/Line.js",
 	"src/objects/Mesh.js",
 	"src/objects/Bone.js",
-	"src/objects/Skeleton.js",
 	"src/objects/Sprite.js",
 	"src/scenes/Scene.js",
 	"src/renderers/CanvasRenderer.js",

+ 0 - 1
utils/build/includes/common.json

@@ -69,7 +69,6 @@
 	"src/objects/Line.js",
 	"src/objects/Mesh.js",
 	"src/objects/Bone.js",
-	"src/objects/Skeleton.js",
 	"src/objects/SkinnedMesh.js",
 	"src/objects/MorphAnimMesh.js",
 	"src/objects/LOD.js",

+ 0 - 1
utils/build/includes/extras.json

@@ -55,7 +55,6 @@
 	"src/extras/helpers/GridHelper.js",
 	"src/extras/helpers/HemisphereLightHelper.js",
 	"src/extras/helpers/PointLightHelper.js",
-	"src/extras/helpers/SkeletonHelper.js",
 	"src/extras/helpers/SpotLightHelper.js",
 	"src/extras/helpers/VertexNormalsHelper.js",
 	"src/extras/helpers/VertexTangentsHelper.js",

+ 0 - 1
utils/build/includes/webgl.json

@@ -60,7 +60,6 @@
 	"src/objects/Line.js",
 	"src/objects/Mesh.js",
 	"src/objects/Bone.js",
-	"src/objects/Skeleton.js",
 	"src/objects/SkinnedMesh.js",
 	"src/objects/MorphAnimMesh.js",
 	"src/objects/LOD.js",