浏览代码

Merge branch 'blending-take2' of https://github.com/insominx/three.js into dev

Mr.doob 11 年之前
父节点
当前提交
2f697e2126

+ 265 - 0
examples/js/BlendCharacter.js

@@ -0,0 +1,265 @@
+/**
+ * @author Michael Guerrero / http://realitymeltdown.com
+ */
+
+THREE.BlendCharacter = function () {
+
+	this.animations = {};
+	this.weightSchedule = [];
+	this.warpSchedule = [];
+
+	this.load = function( url, onLoad ) {
+
+		var scope = this;
+
+		var loader = new THREE.JSONLoader();
+		loader.load( url, function( geometry, materials ) {
+
+			var originalMaterial = materials[ 0 ];
+			originalMaterial.skinning = true;
+
+			THREE.SkinnedMesh.call( scope, geometry, originalMaterial );
+
+			// Create the animations
+
+			for ( var i = 0; i < geometry.animations.length; ++i ) {
+
+				THREE.AnimationHandler.add( geometry.animations[ i ] );
+
+				var animName = geometry.animations[ i ].name;
+				scope.animations[ animName ] = new THREE.Animation( scope, animName );
+
+			}
+
+			// Create the debug visualization
+
+			scope.skeletonHelper = new THREE.SkeletonHelper( scope.skeleton, 0.5, 1 );
+			scope.add( scope.skeletonHelper );
+
+			scope.toggleShowBones( false );
+
+			// Loading is complete, fire the callback
+			if ( onLoad !== undefined ) onLoad();
+
+		} );
+
+	};
+
+	this.update = function( dt ) {
+
+		for ( var i = this.weightSchedule.length - 1; i >= 0; --i ) {
+
+			var data = this.weightSchedule[ i ];
+			data.timeElapsed += dt;
+
+			// If the transition is complete, remove it from the schedule
+
+			if ( data.timeElapsed > data.duration ) {
+
+				data.anim.weight = data.endWeight;
+				this.weightSchedule.splice( i, 1 );
+
+				// If we've faded out completely, stop the animation
+
+				if ( data.anim.weight == 0 ) {
+
+					data.anim.stop( 0 );
+
+				}
+
+			} else {
+
+				// interpolate the weight for the current time
+
+				data.anim.weight = data.startWeight + (data.endWeight - data.startWeight) * data.timeElapsed / data.duration;
+
+			}
+
+		}
+
+		this.updateWarps( dt );
+		this.skeletonHelper.update();
+
+	};
+
+	this.updateWarps = function( dt ) {
+
+		// Warping modifies the time scale over time to make 2 animations of different
+		// lengths match. This is useful for smoothing out transitions that get out of
+		// phase such as between a walk and run cycle
+
+		for ( var i = this.warpSchedule.length - 1; i >= 0; --i ) {
+
+			var data = this.warpSchedule[ i ];
+			data.timeElapsed += dt;
+
+			if ( data.timeElapsed > data.duration ) {
+
+				data.to.weight = 1;
+				data.to.timeScale = 1;
+				data.from.weight = 0;
+				data.from.timeScale = 1;
+				data.from.stop( 0 );
+
+				this.warpSchedule.splice( i, 1 );
+
+			} else {
+
+				var alpha = data.timeElapsed / data.duration;
+
+				var fromLength = data.from.data.length;
+				var toLength = data.to.data.length;
+
+				var fromToRatio = fromLength / toLength;
+				var toFromRatio = toLength / fromLength;
+
+				// scale from each time proportionally to the other animation
+
+				data.from.timeScale = ( 1 - alpha ) + fromToRatio * alpha;
+				data.to.timeScale = alpha + toFromRatio * ( 1 - alpha );
+
+				data.from.weight = 1 - alpha;
+				data.to.weight = alpha;
+
+			}
+
+		}
+
+	}
+
+	this.play = function(animName, weight) {
+
+		this.animations[ animName ].play( 0, weight );
+
+	};
+
+	this.crossfade = function( fromAnimName, toAnimName, duration ) {
+
+		var fromAnim = this.animations[ fromAnimName ];
+		var toAnim = this.animations[ toAnimName ];
+
+		fromAnim.play( 0, 1 );
+		toAnim.play( 0, 0 );
+
+		this.weightSchedule.push( {
+
+			anim: fromAnim,
+			startWeight: 1,
+			endWeight: 0,
+			timeElapsed: 0,
+			duration: duration
+
+		} );
+
+		this.weightSchedule.push( {
+
+			anim: toAnim,
+			startWeight: 0,
+			endWeight: 1,
+			timeElapsed: 0,
+			duration: duration
+
+		} );
+
+	};
+
+	this.warp = function( fromAnimName, toAnimName, duration ) {
+
+		var fromAnim = this.animations[ fromAnimName ];
+		var toAnim = this.animations[ toAnimName ];
+
+		fromAnim.play( 0, 1 );
+		toAnim.play( 0, 0 );
+
+		this.warpSchedule.push( {
+
+			from: fromAnim,
+			to: toAnim,
+			timeElapsed: 0,
+			duration: duration
+
+		} );
+
+	};
+
+	this.applyWeight = function(animName, weight) {
+
+		this.animations[ animName ].weight = weight;
+
+	};
+
+	this.pauseAll = function() {
+
+		for ( var a in this.animations ) {
+
+			if ( this.animations[ a ].isPlaying ) {
+
+				this.animations[ a ].pause();
+
+			}
+
+		}
+
+	};
+
+	this.unPauseAll = function() {
+
+    for ( var a in this.animations ) {
+
+      if ( this.animations[ a ].isPlaying && this.animations[ a ].isPaused ) {
+
+        this.animations[ a ].pause();
+
+      }
+
+    }
+
+  };
+
+
+	this.stopAll = function() {
+
+		for ( a in this.animations ) {
+
+			if ( this.animations[ a ].isPlaying ) {
+				this.animations[ a ].stop(0);
+			}
+
+			this.animations[ a ].weight = 0;
+
+		}
+
+		this.weightSchedule.length = 0;
+		this.warpSchedule.length = 0;
+
+	}
+
+	this.toggleShowBones = function( shouldShow ) {
+
+		this.visible = !shouldShow;
+		this.skeletonHelper.setVisible( shouldShow );
+
+	}
+
+};
+
+
+THREE.BlendCharacter.prototype = Object.create( THREE.SkinnedMesh.prototype );
+
+THREE.BlendCharacter.prototype.getForward = function() {
+
+	var forward = new THREE.Vector3();
+
+	return function() {
+
+		// pull the character's forward basis vector out of the matrix
+		forward.set(
+			-this.matrix.elements[ 8 ],
+			-this.matrix.elements[ 9 ],
+			-this.matrix.elements[ 10 ]
+		);
+
+		return forward;
+	}
+}
+

+ 194 - 0
examples/js/BlendCharacterGui.js

@@ -0,0 +1,194 @@
+/**
+ * @author Michael Guerrero / http://realitymeltdown.com
+ */
+
+function BlendCharacterGui(animations) {
+
+	var controls = {
+
+		gui: null,
+		"Lock Camera": false,
+		"Show Bones": false,
+		"Time Scale": 1.0,
+		"Step Size": 0.016,
+		"Crossfade Time": 3.5,
+		"idle": 0.33,
+		"walk": 0.33,
+		"run": 0.33
+
+	};
+
+	var animations = animations;
+
+	this.shouldShowBones = function() {
+
+		return controls['Show Bones'];
+
+	};
+
+	this.getTimeScale = function() {
+
+		return controls['Time Scale'];
+
+	};
+
+	this.update = function() {
+
+		controls[ 'idle'] = animations[ 'idle' ].weight;
+		controls[ 'walk'] = animations[ 'walk' ].weight;
+		controls[ 'run'] = animations[ 'run' ].weight;
+
+	};
+
+	var init = function() {
+
+		controls.gui = new dat.GUI();
+
+		var settings = controls.gui.addFolder( 'Settings' );
+		var playback = controls.gui.addFolder( 'Playback' );
+		var blending = controls.gui.addFolder( 'Blend Tuning' );
+
+		settings.add( controls, "Lock Camera" ).onChange( controls.lockCameraChanged );
+		settings.add( controls, "Show Bones" ).onChange( controls.showBonesChanged );
+		settings.add( controls, "Time Scale", 0, 1, 0.01 );
+		settings.add( controls, "Step Size", 0.01, 0.1, 0.01 );
+		settings.add( controls, "Crossfade Time", 0.1, 6.0, 0.05 );
+
+		// These controls execute functions
+		playback.add( controls, "start" );
+		playback.add( controls, "pause" );
+		playback.add( controls, "step" );
+		playback.add( controls, "idle to walk" );
+		playback.add( controls, "walk to run" );
+		playback.add( controls, "warp walk to run" );
+
+		blending.add( controls, "idle", 0, 1, 0.01).listen().onChange( controls.weight );
+		blending.add( controls, "walk", 0, 1, 0.01).listen().onChange( controls.weight );
+		blending.add( controls, "run", 0, 1, 0.01).listen().onChange( controls.weight );
+
+		settings.open();
+		playback.open();
+		blending.open();
+
+	}
+
+	var getAnimationData = function() {
+
+		return {
+
+			detail: {
+
+				anims: [ "idle", "walk", "run" ],
+
+				weights: [ controls['idle'],
+						   controls['walk'],
+						   controls['run'] ]
+			}
+
+		};
+	}
+
+	controls.start = function() {
+
+		var startEvent = new CustomEvent( 'start-animation', getAnimationData() );
+		window.dispatchEvent(startEvent);
+
+	};
+
+	controls.stop = function() {
+
+		var stopEvent = new CustomEvent( 'stop-animation' );
+		window.dispatchEvent( stopEvent );
+
+	};
+
+	controls.pause = function() {
+
+		var pauseEvent = new CustomEvent( 'pause-animation' );
+		window.dispatchEvent( pauseEvent );
+
+	};
+
+	controls.step = function() {
+
+		var stepData = { detail: { stepSize: controls['Step Size'] } };
+		window.dispatchEvent( new CustomEvent('step-animation', stepData ));
+
+	};
+
+	controls.weight = function() {
+
+		// renormalize
+		var sum = controls['idle'] + controls['walk'] + controls['run'];
+		controls['idle'] /= sum;
+		controls['walk'] /= sum;
+		controls['run'] /= sum;
+
+		var weightEvent = new CustomEvent( 'weight-animation', getAnimationData() );
+		window.dispatchEvent(weightEvent);
+	};
+
+	controls.crossfade = function( from, to ) {
+
+		var fadeData = getAnimationData();
+		fadeData.detail.from = from;
+		fadeData.detail.to = to;
+		fadeData.detail.time = controls[ "Crossfade Time" ];
+
+		window.dispatchEvent( new CustomEvent( 'crossfade', fadeData ) );
+	}
+
+	controls.warp = function( from, to ) {
+
+		var warpData = getAnimationData();
+		warpData.detail.from = 'walk';
+		warpData.detail.to = 'run';
+		warpData.detail.time = controls[ "Crossfade Time" ];
+
+		window.dispatchEvent( new CustomEvent( 'warp', warpData ) );
+	}
+
+	controls['idle to walk'] = function() {
+
+		controls.crossfade( 'idle', 'walk' );
+
+	};
+
+	controls['walk to run'] = function() {
+
+		controls.crossfade( 'walk', 'run' );
+
+	};
+
+	controls['warp walk to run'] = function() {
+
+		controls.warp( 'walk', 'run' );
+
+	};
+
+	controls.lockCameraChanged = function() {
+
+		var data = {
+			detail: {
+				shouldLock: controls['Lock Camera']
+			}
+		}
+
+		window.dispatchEvent( new CustomEvent( 'toggle-lock-camera', data ) );
+	}
+
+	controls.showBonesChanged = function() {
+
+		var data = {
+			detail: {
+				shouldShow: controls['Show Bones']
+			}
+		}
+
+		window.dispatchEvent( new CustomEvent( 'toggle-show-bones', data ) );
+	}
+
+
+	init.call(this);
+
+}

二进制
examples/models/skinned/marine/M4.png


二进制
examples/models/skinned/marine/MarineCv2_color.jpg


二进制
examples/models/skinned/marine/m4.blend


文件差异内容过多而无法显示
+ 37 - 0
examples/models/skinned/marine/m4.js


文件差异内容过多而无法显示
+ 37 - 0
examples/models/skinned/marine/marine.js


二进制
examples/models/skinned/marine/marine_anims.blend


文件差异内容过多而无法显示
+ 37 - 0
examples/models/skinned/marine/marine_anims.js


二进制
examples/models/skinned/marine/marine_fbx.blend


二进制
examples/models/skinned/marine/marine_fk.blend


二进制
examples/models/skinned/marine/marine_ikrig.blend


文件差异内容过多而无法显示
+ 37 - 0
examples/models/skinned/marine/marine_ikrig.js


+ 257 - 0
examples/webgl_animation_skinning_blending.html

@@ -0,0 +1,257 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <title>three.js webgl - animation - skinning</title>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <style>
+      body {
+        color: #000;
+        font-family:Monospace;
+        font-size:13px;
+        text-align:center;
+
+        background-color: #fff;
+        margin: 0px;
+        overflow: hidden;
+      }
+
+      #info {
+        position: absolute;
+        top: 0px; width: 100%;
+        padding: 5px;
+      }
+    </style>
+  </head>
+  <body>
+    <div id="container"></div>
+    <div id="info">
+      <a href="http://threejs.org" target="_blank">three.js</a> - Skeletal Animation Blending
+      <br><br> Adjust blend weights to affect the animations that are currently playing.
+      <br> Cross fades (and warping) blend between 2 animations and end with a single animation.
+    </div>
+
+    <script src="../build/Three.js"></script>
+
+    <script src="js/Detector.js"></script>
+    <script src="js/libs/stats.min.js"></script>
+    <script src="js/Controls/OrbitControls.js"></script>
+    <script src="js/BlendCharacter.js"></script>
+    <script src="js/BlendCharacterGui.js"></script>
+    <script src="js/libs/dat.gui.min.js"></script>
+
+    <script>
+
+      if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+      var container, stats;
+
+      var blendMesh, camera, scene, renderer, controls;
+
+      var clock = new THREE.Clock();
+      var gui = null;
+
+      var isFrameStepping = false;
+      var timeToStep = 0;
+
+      init();
+
+      function init() {
+
+        container = document.getElementById( 'container' );
+
+        scene = new THREE.Scene();
+        scene.add ( new THREE.AmbientLight( 0xaaaaaa ) );
+
+        var light = new THREE.DirectionalLight( 0xffffff, 1.5 );
+        light.position = new THREE.Vector3( 0, 0, 1000.0 );
+        scene.add( light );
+
+        renderer = new THREE.WebGLRenderer( { antialias: true, alpha: false } );
+        renderer.setClearColor( '#777777', 1 );
+        renderer.setSize( window.innerWidth, window.innerHeight );
+        renderer.autoClear = true;
+
+        container.appendChild( renderer.domElement );
+
+        //
+
+        stats = new Stats();
+        stats.domElement.style.position = 'absolute';
+        stats.domElement.style.top = '0px';
+        container.appendChild( stats.domElement );
+
+        //
+
+        window.addEventListener( 'resize', onWindowResize, false );
+
+        // listen for messages from the gui
+        window.addEventListener( 'start-animation', onStartAnimation );
+        window.addEventListener( 'stop-animation', onStopAnimation );
+        window.addEventListener( 'pause-animation', onPauseAnimation );
+        window.addEventListener( 'step-animation', onStepAnimation );
+        window.addEventListener( 'weight-animation', onWeightAnimation );
+        window.addEventListener( 'crossfade', onCrossfade );
+        window.addEventListener( 'warp', onWarp );
+        window.addEventListener( 'toggle-lock-camera', onLockCameraToggle );
+        window.addEventListener( 'toggle-show-bones', onShowBonesToggle );
+
+        blendMesh = new THREE.BlendCharacter();
+        blendMesh.load( "models/skinned/marine/marine_anims.js", start );
+
+      }
+
+      function onWindowResize() {
+
+        camera.aspect = window.innerWidth / window.innerHeight;
+        camera.updateProjectionMatrix();
+
+        renderer.setSize( window.innerWidth, window.innerHeight );
+
+      }
+
+      function onStartAnimation( event ) {
+
+        var data = event.detail;
+
+        blendMesh.stopAll();
+
+        // the blend mesh will combine 1 or more animations
+        for ( var i = 0; i < data.anims.length; ++i ) {
+
+          blendMesh.play(data.anims[i], data.weights[i]);
+
+        }
+
+        isFrameStepping = false;
+
+      }
+
+      function onStopAnimation( event ) {
+
+        blendMesh.stopAll();
+        isFrameStepping = false;
+
+      }
+
+      function onPauseAnimation( event ) {
+
+        ( isFrameStepping ) ? blendMesh.unPauseAll(): blendMesh.pauseAll();
+
+        isFrameStepping = false;
+
+      }
+
+      function onStepAnimation( event ) {
+
+        blendMesh.unPauseAll();
+        isFrameStepping = true;
+        timeToStep = event.detail.stepSize;
+      }
+
+      function onWeightAnimation(event) {
+
+        var data = event.detail;
+        for ( var i = 0; i < data.anims.length; ++i ) {
+
+          blendMesh.applyWeight(data.anims[i], data.weights[i]);
+
+        }
+
+      }
+
+      function onCrossfade(event) {
+
+        var data = event.detail;
+
+        blendMesh.stopAll();
+        blendMesh.crossfade( data.from, data.to, data.time );
+
+        isFrameStepping = false;
+
+      }
+
+      function onWarp( event ) {
+
+        var data = event.detail;
+
+        blendMesh.stopAll();
+        blendMesh.warp( data.from, data.to, data.time );
+
+        isFrameStepping = false;
+
+      }
+
+
+      function onLockCameraToggle( event ) {
+
+        var shouldLock = event.detail.shouldLock;
+        controls.enabled = !shouldLock;
+
+      }
+
+      function onShowBonesToggle( event ) {
+
+        var shouldShow = event.detail.shouldShow;
+        blendMesh.toggleShowBones( shouldShow );
+
+      }
+
+      function start() {
+
+        blendMesh.rotation.y = Math.PI * -135 / 180;
+        scene.add( blendMesh );
+
+        var aspect = window.innerWidth / window.innerHeight;
+        var radius = blendMesh.geometry.boundingSphere.radius;
+
+        camera = new THREE.PerspectiveCamera( 45, aspect, 1, 10000 );
+        camera.position.set( 0.0, radius, radius * 3.5 );
+
+        controls = new THREE.OrbitControls( camera );
+        controls.target = new THREE.Vector3( 0, radius, 0 );
+        controls.update();
+
+        // Set default weights
+
+        blendMesh.animations[ 'idle' ].weight = 1 / 3;
+        blendMesh.animations[ 'walk' ].weight = 1 / 3;
+        blendMesh.animations[ 'run' ].weight = 1 / 3;
+
+        gui = new BlendCharacterGui(blendMesh.animations);
+
+        animate();
+      }
+
+      function animate() {
+
+        requestAnimationFrame( animate, renderer.domElement );
+
+        // step forward in time based on whether we're stepping and scale
+
+        var scale = gui.getTimeScale();
+        var delta = clock.getDelta();
+        var stepSize = (!isFrameStepping) ? delta * scale: timeToStep;
+
+        // modify blend weights
+
+        blendMesh.update( stepSize );
+        gui.update();
+
+        THREE.AnimationHandler.update( stepSize );
+
+        renderer.render( scene, camera );
+        stats.update();
+
+        // if we are stepping, consume time
+        // ( will equal step size next time a single step is desired )
+
+        timeToStep = 0;
+
+      }
+
+    </script>
+
+  </body>
+</html>
+

+ 165 - 94
src/extras/animation/Animation.js

@@ -16,23 +16,24 @@ THREE.Animation = function ( root, name ) {
 	this.isPlaying = false;
 	this.isPlaying = false;
 	this.isPaused = true;
 	this.isPaused = true;
 	this.loop = true;
 	this.loop = true;
+	this.weight = 0;
 
 
 	this.interpolationType = THREE.AnimationHandler.LINEAR;
 	this.interpolationType = THREE.AnimationHandler.LINEAR;
 
 
 };
 };
 
 
-THREE.Animation.prototype.play = function ( startTime ) {
 
 
-	this.currentTime = startTime !== undefined ? startTime : 0;
+THREE.Animation.prototype.keyTypes = [ "pos", "rot", "scl" ];
 
 
-	if ( this.isPlaying === false ) {
 
 
-		this.isPlaying = true;
+THREE.Animation.prototype.play = function ( startTime, weight ) {
 
 
-		this.reset();
-		this.update( 0 );
+	this.currentTime = startTime !== undefined ? startTime : 0;
+	this.weight = weight !== undefined ? weight: 1;
 
 
-	}
+	this.isPlaying = true;
+	this.reset();
+	this.update( 0 );
 
 
 	this.isPaused = false;
 	this.isPaused = false;
 
 
@@ -77,22 +78,40 @@ THREE.Animation.prototype.reset = function () {
 		if ( object.animationCache === undefined ) {
 		if ( object.animationCache === undefined ) {
 
 
 			object.animationCache = {};
 			object.animationCache = {};
-			object.animationCache.prevKey = { pos: 0, rot: 0, scl: 0 };
-			object.animationCache.nextKey = { pos: 0, rot: 0, scl: 0 };
-			object.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
 
 
 		}
 		}
 
 
-		var prevKey = object.animationCache.prevKey;
-		var nextKey = object.animationCache.nextKey;
+		if ( object.animationCache[this.data.name] === undefined ) {
+
+			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;
+
+		}
+
+		var animationCache = object.animationCache[this.data.name];
 
 
-		prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ];
-		prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ];
-		prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ];
+		// Get keys to match our current time
 
 
-		nextKey.pos = this.getNextKeyWith( "pos", h, 1 );
-		nextKey.rot = this.getNextKeyWith( "rot", h, 1 );
-		nextKey.scl = this.getNextKeyWith( "scl", h, 1 );
+		for ( var t = 0; t < 3; t ++ ) {
+
+			var type = this.keyTypes[ t ];
+
+			var prevKey = this.data.hierarchy[ h ].keys[ 0 ];
+			var nextKey = this.getNextKeyWith( type, h, 1 );
+
+			while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
+
+				prevKey = nextKey;
+				nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
+
+			}
+
+			animationCache.prevKey[ type ] = prevKey;
+			animationCache.nextKey[ type ] = nextKey;
+
+		}
 
 
 	}
 	}
 
 
@@ -103,7 +122,9 @@ THREE.Animation.prototype.update = (function(){
 
 
 	var points = [];
 	var points = [];
 	var target = new THREE.Vector3();
 	var target = new THREE.Vector3();
-	
+	var newVector = new THREE.Vector3();
+	var newQuat = new THREE.Quaternion();
+
 	// Catmull-Rom spline
 	// Catmull-Rom spline
 
 
 	var interpolateCatmullRom = function ( points, scale ) {
 	var interpolateCatmullRom = function ( points, scale ) {
@@ -111,172 +132,222 @@ THREE.Animation.prototype.update = (function(){
 		var c = [], v3 = [],
 		var c = [], v3 = [],
 		point, intPoint, weight, w2, w3,
 		point, intPoint, weight, w2, w3,
 		pa, pb, pc, pd;
 		pa, pb, pc, pd;
-	
+
 		point = ( points.length - 1 ) * scale;
 		point = ( points.length - 1 ) * scale;
 		intPoint = Math.floor( point );
 		intPoint = Math.floor( point );
 		weight = point - intPoint;
 		weight = point - intPoint;
-	
+
 		c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
 		c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
 		c[ 1 ] = intPoint;
 		c[ 1 ] = intPoint;
 		c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1;
 		c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1;
 		c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2;
 		c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2;
-	
+
 		pa = points[ c[ 0 ] ];
 		pa = points[ c[ 0 ] ];
 		pb = points[ c[ 1 ] ];
 		pb = points[ c[ 1 ] ];
 		pc = points[ c[ 2 ] ];
 		pc = points[ c[ 2 ] ];
 		pd = points[ c[ 3 ] ];
 		pd = points[ c[ 3 ] ];
-	
+
 		w2 = weight * weight;
 		w2 = weight * weight;
 		w3 = weight * w2;
 		w3 = weight * w2;
-	
+
 		v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 );
 		v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 );
 		v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 );
 		v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 );
 		v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 );
 		v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 );
-	
+
 		return v3;
 		return v3;
 
 
 	};
 	};
 
 
 	var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) {
 	var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) {
-	
+
 		var v0 = ( p2 - p0 ) * 0.5,
 		var v0 = ( p2 - p0 ) * 0.5,
 			v1 = ( p3 - p1 ) * 0.5;
 			v1 = ( p3 - p1 ) * 0.5;
-	
+
 		return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
 		return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
-	
+
 	};
 	};
-	
+
 	return function ( delta ) {
 	return function ( delta ) {
 		if ( this.isPlaying === false ) return;
 		if ( this.isPlaying === false ) return;
-	
+
 		this.currentTime += delta * this.timeScale;
 		this.currentTime += delta * this.timeScale;
-	
+
+		if ( this.weight === 0 )
+			return;
+
 		//
 		//
-	
+
 		var vector;
 		var vector;
-		var types = [ "pos", "rot", "scl" ];
-	
 		var duration = this.data.length;
 		var duration = this.data.length;
-	
+
 		if ( this.loop === true && this.currentTime > duration ) {
 		if ( this.loop === true && this.currentTime > duration ) {
-	
+
 			this.currentTime %= duration;
 			this.currentTime %= duration;
 			this.reset();
 			this.reset();
-	
+
 		} else if ( this.loop === false && this.currentTime > duration ) {
 		} else if ( this.loop === false && this.currentTime > duration ) {
-	
+
 			this.stop();
 			this.stop();
 			return;
 			return;
-	
+
 		}
 		}
-	
-		this.currentTime = Math.min( this.currentTime, duration );
-	
+
 		for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
 		for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
-	
+
 			var object = this.hierarchy[ h ];
 			var object = this.hierarchy[ h ];
-			var animationCache = object.animationCache;
-	
+			var animationCache = object.animationCache[this.data.name];
+
 			// loop through pos/rot/scl
 			// loop through pos/rot/scl
-	
+
 			for ( var t = 0; t < 3; t ++ ) {
 			for ( var t = 0; t < 3; t ++ ) {
-	
+
 				// get keys
 				// get keys
-	
-				var type    = types[ t ];
+
+				var type    = this.keyTypes[ t ];
 				var prevKey = animationCache.prevKey[ type ];
 				var prevKey = animationCache.prevKey[ type ];
 				var nextKey = animationCache.nextKey[ type ];
 				var nextKey = animationCache.nextKey[ type ];
-	
+
 				if ( nextKey.time <= this.currentTime ) {
 				if ( nextKey.time <= this.currentTime ) {
-	
+
 					prevKey = this.data.hierarchy[ h ].keys[ 0 ];
 					prevKey = this.data.hierarchy[ h ].keys[ 0 ];
 					nextKey = this.getNextKeyWith( type, h, 1 );
 					nextKey = this.getNextKeyWith( type, h, 1 );
-	
+
 					while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
 					while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
-	
+
 						prevKey = nextKey;
 						prevKey = nextKey;
 						nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
 						nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
-	
+
 					}
 					}
-	
+
 					animationCache.prevKey[ type ] = prevKey;
 					animationCache.prevKey[ type ] = prevKey;
 					animationCache.nextKey[ type ] = nextKey;
 					animationCache.nextKey[ type ] = nextKey;
-	
+
 				}
 				}
-	
+
 				object.matrixAutoUpdate = true;
 				object.matrixAutoUpdate = true;
 				object.matrixWorldNeedsUpdate = true;
 				object.matrixWorldNeedsUpdate = true;
-	
+
 				var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
 				var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
-	
+
 				var prevXYZ = prevKey[ type ];
 				var prevXYZ = prevKey[ type ];
 				var nextXYZ = nextKey[ type ];
 				var nextXYZ = nextKey[ type ];
-	
+
 				if ( scale < 0 ) scale = 0;
 				if ( scale < 0 ) scale = 0;
 				if ( scale > 1 ) scale = 1;
 				if ( scale > 1 ) scale = 1;
-	
+
 				// interpolate
 				// interpolate
-	
+
 				if ( type === "pos" ) {
 				if ( type === "pos" ) {
-	
+
 					vector = object.position;
 					vector = object.position;
-	
+
 					if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
 					if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
-	
-						vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
-						vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
-						vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
-	
+
+						newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
+						newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
+						newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
+
+						// blend
+						if (object instanceof THREE.Bone) {
+
+							var proportionalWeight = this.weight / ( this.weight + object.accumulatedPosWeight );
+							vector.lerp( newVector, proportionalWeight );
+							object.accumulatedPosWeight += this.weight;
+
+						} else
+							vector = newVector;
+
 					} else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
 					} else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
 						this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
 						this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
-	
+
 						points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ];
 						points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ];
 						points[ 1 ] = prevXYZ;
 						points[ 1 ] = prevXYZ;
 						points[ 2 ] = nextXYZ;
 						points[ 2 ] = nextXYZ;
 						points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ];
 						points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ];
-	
+
 						scale = scale * 0.33 + 0.33;
 						scale = scale * 0.33 + 0.33;
-	
+
 						var currentPoint = interpolateCatmullRom( points, scale );
 						var currentPoint = interpolateCatmullRom( points, scale );
-	
-						vector.x = currentPoint[ 0 ];
-						vector.y = currentPoint[ 1 ];
-						vector.z = currentPoint[ 2 ];
-	
+
+						if ( object instanceof THREE.Bone ) {
+
+							var proportionalWeight = this.weight / ( this.weight + object.accumulatedPosWeight );
+							object.accumulatedPosWeight += this.weight;
+
+						}
+						else
+							var proportionalWeight = 1;
+
+						// blend
+						vector.x = vector.x + ( currentPoint[ 0 ] - vector.x ) * proportionalWeight;
+						vector.y = vector.y + ( currentPoint[ 1 ] - vector.y ) * proportionalWeight;
+						vector.z = vector.z + ( currentPoint[ 2 ] - vector.z ) * proportionalWeight;
+
 						if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
 						if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
-	
+
 							var forwardPoint = interpolateCatmullRom( points, scale * 1.01 );
 							var forwardPoint = interpolateCatmullRom( points, scale * 1.01 );
-	
+
 							target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
 							target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
 							target.sub( vector );
 							target.sub( vector );
 							target.y = 0;
 							target.y = 0;
 							target.normalize();
 							target.normalize();
-	
+
 							var angle = Math.atan2( target.x, target.z );
 							var angle = Math.atan2( target.x, target.z );
 							object.rotation.set( 0, angle, 0 );
 							object.rotation.set( 0, angle, 0 );
-	
+
 						}
 						}
-	
+
 					}
 					}
-	
+
 				} else if ( type === "rot" ) {
 				} else if ( type === "rot" ) {
-	
-					THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale );
-	
+
+					THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale );
+
+					// Avoid paying the cost of an additional slerp if we don't have to
+					if ( !( object instanceof THREE.Bone ) ) {
+
+						object.quaternion.copy(newQuat);
+
+					}
+					else if ( object.accumulatedRotWeight === 0) {
+
+						object.quaternion.copy(newQuat);
+						object.accumulatedRotWeight = this.weight;
+
+					}
+					else {
+
+						var proportionalWeight = this.weight / ( this.weight + object.accumulatedRotWeight );
+						THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight );
+						object.accumulatedRotWeight += this.weight;
+
+					}
+
 				} else if ( type === "scl" ) {
 				} else if ( type === "scl" ) {
-	
+
 					vector = object.scale;
 					vector = object.scale;
-	
-					vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
-					vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
-					vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
-	
+
+					newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
+					newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
+					newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
+
+					if ( object instanceof THREE.Bone ) {
+
+						var proportionalWeight = this.weight / ( this.weight + object.accumulatedSclWeight);
+						vector.lerp( newVector, proportionalWeight );
+						object.accumulatedSclWeight += this.weight;
+
+					} else
+						vector = newVector;
+
 				}
 				}
-	
+
 			}
 			}
-	
+
 		}
 		}
 
 
+		return true;
+
 	};
 	};
 
 
 })();
 })();

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

@@ -60,7 +60,7 @@ THREE.AnimationHandler = ( function () {
 			console.log( "THREE.AnimationHandler.add: Warning! " + name + " doesn't exists in library. Doing nothing." );
 			console.log( "THREE.AnimationHandler.add: Warning! " + name + " doesn't exists in library. Doing nothing." );
 
 
 		}
 		}
-			
+
 		library[ name ] = undefined;
 		library[ name ] = undefined;
 
 
 	};
 	};
@@ -95,9 +95,9 @@ THREE.AnimationHandler = ( function () {
 
 
 		if ( root instanceof THREE.SkinnedMesh ) {
 		if ( root instanceof THREE.SkinnedMesh ) {
 
 
-			for ( var b = 0; b < root.bones.length; b++ ) {
+			for ( var b = 0; b < root.skeleton.bones.length; b++ ) {
 
 
-				hierarchy.push( root.bones[ b ] );
+				hierarchy.push( root.skeleton.bones[ b ] );
 
 
 			}
 			}
 
 

+ 102 - 0
src/extras/helpers/SkeletonHelper.js

@@ -0,0 +1,102 @@
+/**
+ * @author Sean Griffin / http://twitter.com/sgrif
+ * @author Michael Guerrero / http://realitymeltdown.com
+ */
+
+THREE.SkeletonHelper = function ( skeleton, jointBoxSize, scaleRatio ) {
+
+  THREE.Object3D.call( this );
+
+  this.scaleRatio = ( scaleRatio !== undefined ) ? scaleRatio : 1;
+  this.skeleton = skeleton;
+
+  var jointBoxSize = ( jointBoxSize !== undefined ) ? jointBoxSize : 1;
+  var boxSize = jointBoxSize * this.scaleRatio;
+
+  for ( var i = 0; i < skeleton.bones.length; ++i ) {
+
+    var bone = skeleton.bones[ i ];
+    var boxGeometry = new THREE.BoxGeometry( boxSize, boxSize, boxSize );
+    var boxMaterial = new THREE.MeshBasicMaterial();
+
+    bone.helper = {};
+    bone.helper.box = new THREE.Mesh( boxGeometry, boxMaterial );
+    bone.helper.axes = new THREE.AxisHelper( jointBoxSize * 3 );
+
+    this.add( bone.helper.box );
+    this.add( bone.helper.axes );
+
+    if ( bone.parent instanceof THREE.Bone ) {
+
+      var lineMaterial = new THREE.LineBasicMaterial();
+      var lineGeometry = new THREE.Geometry();
+
+      lineMaterial.vertexColors = true;
+
+      lineGeometry.vertices.push( new THREE.Vector3() );
+      lineGeometry.vertices.push( new THREE.Vector3() );
+      lineGeometry.colors.push( new THREE.Color( 1, 1, 0 ) );
+      lineGeometry.colors.push( new THREE.Color( 0, 0, 0 ) );
+
+      bone.helper.line = new THREE.Line( lineGeometry, lineMaterial );
+      this.add( bone.helper.line);
+
+    }
+
+  }
+
+  this.update();
+};
+
+
+THREE.SkeletonHelper.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.SkeletonHelper.prototype.update = function () {
+
+  for ( var i = 0; i < this.skeleton.bones.length; ++i ) {
+
+    var bone = this.skeleton.bones[ i ];
+
+    if ( this.visible && bone.parent instanceof THREE.Bone ) {
+
+      bone.skinMatrix.decompose( bone.helper.box.position, bone.helper.box.quaternion, bone.helper.box.scale );
+      bone.helper.box.position.multiplyScalar( this.scaleRatio );
+
+      bone.helper.axes.quaternion = bone.helper.box.quaternion;
+      bone.helper.axes.position = bone.helper.box.position;
+      bone.helper.axes.scale = bone.helper.box.scale;
+
+      bone.helper.line.geometry.vertices[0].setFromMatrixPosition( bone.skinMatrix );
+      bone.helper.line.geometry.vertices[0].multiplyScalar( this.scaleRatio );
+
+      bone.helper.line.geometry.vertices[1].setFromMatrixPosition( bone.parent.skinMatrix );
+      bone.helper.line.geometry.vertices[1].multiplyScalar( this.scaleRatio );
+
+      bone.helper.line.geometry.verticesNeedUpdate = true;
+
+    }
+
+  }
+
+};
+
+THREE.SkeletonHelper.prototype.setVisible = function ( shouldBeVisible ) {
+
+  for ( var i = 0; i < this.skeleton.bones.length; ++i ) {
+
+    var bone = this.skeleton.bones[ i ];
+    if ( bone.helper ) {
+
+      bone.helper.box.visible = shouldBeVisible;
+      bone.helper.axes.visible = shouldBeVisible;
+
+      if ( bone.parent instanceof THREE.Bone ) {
+
+         bone.helper.line.visible = shouldBeVisible;
+
+      }
+
+    }
+
+  }
+}

+ 10 - 0
src/objects/Bone.js

@@ -10,6 +10,10 @@ THREE.Bone = function( belongsToSkin ) {
 	this.skin = belongsToSkin;
 	this.skin = belongsToSkin;
 	this.skinMatrix = new THREE.Matrix4();
 	this.skinMatrix = new THREE.Matrix4();
 
 
+	this.accumulatedRotWeight = 0;
+	this.accumulatedPosWeight = 0;
+	this.accumulatedSclWeight = 0;
+
 };
 };
 
 
 THREE.Bone.prototype = Object.create( THREE.Object3D.prototype );
 THREE.Bone.prototype = Object.create( THREE.Object3D.prototype );
@@ -41,6 +45,12 @@ THREE.Bone.prototype.update = function ( parentSkinMatrix, forceUpdate ) {
 		this.matrixWorldNeedsUpdate = false;
 		this.matrixWorldNeedsUpdate = false;
 		forceUpdate = true;
 		forceUpdate = true;
 
 
+		// Reset weights to be re-accumulated in the next frame
+
+		this.accumulatedRotWeight = 0;
+		this.accumulatedPosWeight = 0;
+		this.accumulatedSclWeight = 0;
+
 	}
 	}
 
 
 	// update children
 	// update children

+ 135 - 0
src/objects/Skeleton.js

@@ -0,0 +1,135 @@
+/**
+ * @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 );
+
+  }
+
+};

+ 17 - 122
src/objects/SkinnedMesh.js

@@ -7,125 +7,30 @@ THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
 
 
 	THREE.Mesh.call( this, geometry, material );
 	THREE.Mesh.call( this, geometry, material );
 
 
-	//
+	this.skeleton = new THREE.Skeleton( this.geometry && this.geometry.bones, useVertexTexture );
 
 
-	this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;
+  // Add root level bones as children of the mesh
 
 
-	// init bones
+	for ( var b = 0; b < this.skeleton.bones.length; ++b ) {
 
 
-	this.identityMatrix = new THREE.Matrix4();
-
-	this.bones = [];
-	this.boneMatrices = [];
-
-	var b, bone, gbone, p, q, s;
-
-	if ( this.geometry && this.geometry.bones !== undefined ) {
-
-		for ( b = 0; b < this.geometry.bones.length; b ++ ) {
-
-			gbone = this.geometry.bones[ 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 ( b = 0; b < this.bones.length; b ++ ) {
-
-			gbone = this.geometry.bones[ b ];
-			bone = this.bones[ b ];
-
-			if ( gbone.parent === -1 ) {
-
-				this.add( bone );
-
-			} else {
-
-				this.bones[ gbone.parent ].add( bone );
-
-			}
-
-		}
-
-		//
-
-		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;
+		var bone = this.skeleton.bones[ b ];
 
 
-			this.boneTextureWidth = size;
-			this.boneTextureHeight = size;
+		if ( bone.parent === undefined ) {
 
 
-			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.add( bone );
 
 
 		}
 		}
 
 
-		this.pose();
-
 	}
 	}
 
 
-};
-
-THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
-
-THREE.SkinnedMesh.prototype.addBone = function( bone ) {
-
-	if ( bone === undefined ) {
-
-		bone = new THREE.Bone( this );
+	this.identityMatrix = new THREE.Matrix4();
 
 
-	}
+	this.pose();
 
 
-	this.bones.push( bone );
+};
 
 
-	return bone;
 
 
-};
+THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
 
 
 THREE.SkinnedMesh.prototype.updateMatrixWorld = function () {
 THREE.SkinnedMesh.prototype.updateMatrixWorld = function () {
 
 
@@ -175,25 +80,15 @@ THREE.SkinnedMesh.prototype.updateMatrixWorld = function () {
 
 
 		// make a snapshot of the bones' rest position
 		// make a snapshot of the bones' rest position
 
 
-		if ( this.boneInverses == undefined ) {
+		if ( this.skeleton.boneInverses === undefined ) {
 
 
-			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 );
-
-			}
+			this.skeleton.calculateInverses();
 
 
 		}
 		}
 
 
 		// flatten bone matrices to array
 		// flatten bone matrices to array
 
 
-		for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+		for ( var b = 0, bl = this.skeleton.bones.length; b < bl; b ++ ) {
 
 
 			// compute the offset between the current and the original transform;
 			// compute the offset between the current and the original transform;
 
 
@@ -201,14 +96,14 @@ THREE.SkinnedMesh.prototype.updateMatrixWorld = function () {
 			// was already representing the offset; however, this requires some
 			// was already representing the offset; however, this requires some
 			// major changes to the animation system
 			// major changes to the animation system
 
 
-			offsetMatrix.multiplyMatrices( this.bones[ b ].skinMatrix, this.boneInverses[ b ] );
-			offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );
+			offsetMatrix.multiplyMatrices( this.skeleton.bones[ b ].skinMatrix, this.skeleton.boneInverses[ b ] );
+			offsetMatrix.flattenToArrayOffset( this.skeleton.boneMatrices, b * 16 );
 
 
 		}
 		}
 
 
-		if ( this.useVertexTexture ) {
+		if ( this.skeleton.useVertexTexture ) {
 
 
-			this.boneTexture.needsUpdate = true;
+			this.skeleton.boneTexture.needsUpdate = true;
 
 
 		}
 		}
 
 

+ 79 - 79
src/renderers/WebGLRenderer.js

@@ -186,7 +186,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 	var _glExtensionCompressedTextureS3TC;
 	var _glExtensionCompressedTextureS3TC;
 	var _glExtensionElementIndexUint;
 	var _glExtensionElementIndexUint;
 	var _glExtensionFragDepth;
 	var _glExtensionFragDepth;
-	
+
 
 
 	initGL();
 	initGL();
 
 
@@ -606,7 +606,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 				if ( attributes[ key ].buffer !== undefined ) {
 				if ( attributes[ key ].buffer !== undefined ) {
 
 
 					_gl.deleteBuffer( attributes[ key ].buffer );
 					_gl.deleteBuffer( attributes[ key ].buffer );
-		
+
 				}
 				}
 
 
 			}
 			}
@@ -787,7 +787,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 					attribute.__webglInitialized = true;
 					attribute.__webglInitialized = true;
 
 
-					var size = 1;		// "f" and "i"
+					var size = 1;   // "f" and "i"
 
 
 					if ( attribute.type === "v2" ) size = 2;
 					if ( attribute.type === "v2" ) size = 2;
 					else if ( attribute.type === "v3" ) size = 3;
 					else if ( attribute.type === "v3" ) size = 3;
@@ -967,7 +967,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 					attribute.__webglInitialized = true;
 					attribute.__webglInitialized = true;
 
 
-					var size = 1;		// "f" and "i"
+					var size = 1;   // "f" and "i"
 
 
 					if( attribute.type === "v2" ) size = 2;
 					if( attribute.type === "v2" ) size = 2;
 					else if( attribute.type === "v3" ) size = 3;
 					else if( attribute.type === "v3" ) size = 3;
@@ -1049,11 +1049,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 		// material must use some texture to require uvs
 		// material must use some texture to require uvs
 
 
 		if ( material.map ||
 		if ( material.map ||
-		     material.lightMap ||
-		     material.bumpMap ||
-		     material.normalMap ||
-		     material.specularMap ||
-		     material instanceof THREE.ShaderMaterial ) {
+				 material.lightMap ||
+				 material.bumpMap ||
+				 material.normalMap ||
+				 material.specularMap ||
+				 material instanceof THREE.ShaderMaterial ) {
 
 
 			return true;
 			return true;
 
 
@@ -1179,7 +1179,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 							value = customAttribute.value[ index ];
 							value = customAttribute.value[ index ];
 
 
-							customAttribute.array[ offset ] 	= value.x;
+							customAttribute.array[ offset ]   = value.x;
 							customAttribute.array[ offset + 1 ] = value.y;
 							customAttribute.array[ offset + 1 ] = value.y;
 
 
 							offset += 2;
 							offset += 2;
@@ -1212,7 +1212,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 								value = customAttribute.value[ index ];
 								value = customAttribute.value[ index ];
 
 
-								customAttribute.array[ offset ] 	= value.x;
+								customAttribute.array[ offset ]   = value.x;
 								customAttribute.array[ offset + 1 ] = value.y;
 								customAttribute.array[ offset + 1 ] = value.y;
 								customAttribute.array[ offset + 2 ] = value.z;
 								customAttribute.array[ offset + 2 ] = value.z;
 
 
@@ -1287,7 +1287,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 					if ( customAttribute.needsUpdate &&
 					if ( customAttribute.needsUpdate &&
 						 ( customAttribute.boundTo === undefined ||
 						 ( customAttribute.boundTo === undefined ||
-						   customAttribute.boundTo === "vertices") ) {
+							 customAttribute.boundTo === "vertices") ) {
 
 
 						cal = customAttribute.value.length;
 						cal = customAttribute.value.length;
 
 
@@ -1307,7 +1307,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 								value = customAttribute.value[ ca ];
 								value = customAttribute.value[ ca ];
 
 
-								customAttribute.array[ offset ] 	= value.x;
+								customAttribute.array[ offset ]   = value.x;
 								customAttribute.array[ offset + 1 ] = value.y;
 								customAttribute.array[ offset + 1 ] = value.y;
 
 
 								offset += 2;
 								offset += 2;
@@ -1322,7 +1322,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 									value = customAttribute.value[ ca ];
 									value = customAttribute.value[ ca ];
 
 
-									customAttribute.array[ offset ] 	= value.r;
+									customAttribute.array[ offset ]   = value.r;
 									customAttribute.array[ offset + 1 ] = value.g;
 									customAttribute.array[ offset + 1 ] = value.g;
 									customAttribute.array[ offset + 2 ] = value.b;
 									customAttribute.array[ offset + 2 ] = value.b;
 
 
@@ -1336,7 +1336,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 									value = customAttribute.value[ ca ];
 									value = customAttribute.value[ ca ];
 
 
-									customAttribute.array[ offset ] 	= value.x;
+									customAttribute.array[ offset ]   = value.x;
 									customAttribute.array[ offset + 1 ] = value.y;
 									customAttribute.array[ offset + 1 ] = value.y;
 									customAttribute.array[ offset + 2 ] = value.z;
 									customAttribute.array[ offset + 2 ] = value.z;
 
 
@@ -1489,7 +1489,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 				if ( customAttribute.needsUpdate &&
 				if ( customAttribute.needsUpdate &&
 					 ( customAttribute.boundTo === undefined ||
 					 ( customAttribute.boundTo === undefined ||
-					   customAttribute.boundTo === "vertices" ) ) {
+						 customAttribute.boundTo === "vertices" ) ) {
 
 
 					offset = 0;
 					offset = 0;
 
 
@@ -1509,7 +1509,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 							value = customAttribute.value[ ca ];
 							value = customAttribute.value[ ca ];
 
 
-							customAttribute.array[ offset ] 	= value.x;
+							customAttribute.array[ offset ]   = value.x;
 							customAttribute.array[ offset + 1 ] = value.y;
 							customAttribute.array[ offset + 1 ] = value.y;
 
 
 							offset += 2;
 							offset += 2;
@@ -1524,7 +1524,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 								value = customAttribute.value[ ca ];
 								value = customAttribute.value[ ca ];
 
 
-								customAttribute.array[ offset ] 	= value.r;
+								customAttribute.array[ offset ]   = value.r;
 								customAttribute.array[ offset + 1 ] = value.g;
 								customAttribute.array[ offset + 1 ] = value.g;
 								customAttribute.array[ offset + 2 ] = value.b;
 								customAttribute.array[ offset + 2 ] = value.b;
 
 
@@ -1538,7 +1538,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 								value = customAttribute.value[ ca ];
 								value = customAttribute.value[ ca ];
 
 
-								customAttribute.array[ offset ] 	= value.x;
+								customAttribute.array[ offset ]   = value.x;
 								customAttribute.array[ offset + 1 ] = value.y;
 								customAttribute.array[ offset + 1 ] = value.y;
 								customAttribute.array[ offset + 2 ] = value.z;
 								customAttribute.array[ offset + 2 ] = value.z;
 
 
@@ -1554,7 +1554,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 							value = customAttribute.value[ ca ];
 							value = customAttribute.value[ ca ];
 
 
-							customAttribute.array[ offset ] 	 = value.x;
+							customAttribute.array[ offset ]    = value.x;
 							customAttribute.array[ offset + 1  ] = value.y;
 							customAttribute.array[ offset + 1  ] = value.y;
 							customAttribute.array[ offset + 2  ] = value.z;
 							customAttribute.array[ offset + 2  ] = value.z;
 							customAttribute.array[ offset + 3  ] = value.w;
 							customAttribute.array[ offset + 3  ] = value.w;
@@ -1717,7 +1717,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 					vka = morphTargetsArrays[ vk ];
 					vka = morphTargetsArrays[ vk ];
 
 
-					vka[ offset_morphTarget ] 	  = v1.x;
+					vka[ offset_morphTarget ]     = v1.x;
 					vka[ offset_morphTarget + 1 ] = v1.y;
 					vka[ offset_morphTarget + 1 ] = v1.y;
 					vka[ offset_morphTarget + 2 ] = v1.z;
 					vka[ offset_morphTarget + 2 ] = v1.z;
 
 
@@ -1751,7 +1751,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 						nka = morphNormalsArrays[ vk ];
 						nka = morphNormalsArrays[ vk ];
 
 
-						nka[ offset_morphTarget ] 	  = n1.x;
+						nka[ offset_morphTarget ]     = n1.x;
 						nka[ offset_morphTarget + 1 ] = n1.y;
 						nka[ offset_morphTarget + 1 ] = n1.y;
 						nka[ offset_morphTarget + 2 ] = n1.z;
 						nka[ offset_morphTarget + 2 ] = n1.z;
 
 
@@ -1789,7 +1789,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
 
-				face = obj_faces[ chunk_faces3[ f ]	];
+				face = obj_faces[ chunk_faces3[ f ] ];
 
 
 				// weights
 				// weights
 
 
@@ -1853,7 +1853,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
 
-				face = obj_faces[ chunk_faces3[ f ]	];
+				face = obj_faces[ chunk_faces3[ f ] ];
 
 
 				vertexColors = face.vertexColors;
 				vertexColors = face.vertexColors;
 				faceColor = face.color;
 				faceColor = face.color;
@@ -1901,7 +1901,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
 
-				face = obj_faces[ chunk_faces3[ f ]	];
+				face = obj_faces[ chunk_faces3[ f ] ];
 
 
 				vertexTangents = face.vertexTangents;
 				vertexTangents = face.vertexTangents;
 
 
@@ -1937,7 +1937,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
 
-				face = obj_faces[ chunk_faces3[ f ]	];
+				face = obj_faces[ chunk_faces3[ f ] ];
 
 
 				vertexNormals = face.vertexNormals;
 				vertexNormals = face.vertexNormals;
 				faceNormal = face.normal;
 				faceNormal = face.normal;
@@ -2045,7 +2045,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
 
-				faceArray[ offset_face ] 	 = vertexIndex;
+				faceArray[ offset_face ]   = vertexIndex;
 				faceArray[ offset_face + 1 ] = vertexIndex + 1;
 				faceArray[ offset_face + 1 ] = vertexIndex + 1;
 				faceArray[ offset_face + 2 ] = vertexIndex + 2;
 				faceArray[ offset_face + 2 ] = vertexIndex + 2;
 
 
@@ -2091,9 +2091,9 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
 
-							face = obj_faces[ chunk_faces3[ f ]	];
+							face = obj_faces[ chunk_faces3[ f ] ];
 
 
-							customAttribute.array[ offset_custom ] 	   = customAttribute.value[ face.a ];
+							customAttribute.array[ offset_custom ]     = customAttribute.value[ face.a ];
 							customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ];
 							customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ];
 							customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ];
 							customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ];
 
 
@@ -2107,7 +2107,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 							value = customAttribute.value[ chunk_faces3[ f ] ];
 							value = customAttribute.value[ chunk_faces3[ f ] ];
 
 
-							customAttribute.array[ offset_custom ] 	   = value;
+							customAttribute.array[ offset_custom ]     = value;
 							customAttribute.array[ offset_custom + 1 ] = value;
 							customAttribute.array[ offset_custom + 1 ] = value;
 							customAttribute.array[ offset_custom + 2 ] = value;
 							customAttribute.array[ offset_custom + 2 ] = value;
 
 
@@ -2123,13 +2123,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
 
-							face = obj_faces[ chunk_faces3[ f ]	];
+							face = obj_faces[ chunk_faces3[ f ] ];
 
 
 							v1 = customAttribute.value[ face.a ];
 							v1 = customAttribute.value[ face.a ];
 							v2 = customAttribute.value[ face.b ];
 							v2 = customAttribute.value[ face.b ];
 							v3 = customAttribute.value[ face.c ];
 							v3 = customAttribute.value[ face.c ];
 
 
-							customAttribute.array[ offset_custom ] 	   = v1.x;
+							customAttribute.array[ offset_custom ]     = v1.x;
 							customAttribute.array[ offset_custom + 1 ] = v1.y;
 							customAttribute.array[ offset_custom + 1 ] = v1.y;
 
 
 							customAttribute.array[ offset_custom + 2 ] = v2.x;
 							customAttribute.array[ offset_custom + 2 ] = v2.x;
@@ -2152,7 +2152,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 							v2 = value;
 							v2 = value;
 							v3 = value;
 							v3 = value;
 
 
-							customAttribute.array[ offset_custom ] 	   = v1.x;
+							customAttribute.array[ offset_custom ]     = v1.x;
 							customAttribute.array[ offset_custom + 1 ] = v1.y;
 							customAttribute.array[ offset_custom + 1 ] = v1.y;
 
 
 							customAttribute.array[ offset_custom + 2 ] = v2.x;
 							customAttribute.array[ offset_custom + 2 ] = v2.x;
@@ -2185,13 +2185,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
 
-							face = obj_faces[ chunk_faces3[ f ]	];
+							face = obj_faces[ chunk_faces3[ f ] ];
 
 
 							v1 = customAttribute.value[ face.a ];
 							v1 = customAttribute.value[ face.a ];
 							v2 = customAttribute.value[ face.b ];
 							v2 = customAttribute.value[ face.b ];
 							v3 = customAttribute.value[ face.c ];
 							v3 = customAttribute.value[ face.c ];
 
 
-							customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom ]     = v1[ pp[ 0 ] ];
 							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
 							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
 							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
 							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
 
 
@@ -2217,7 +2217,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 							v2 = value;
 							v2 = value;
 							v3 = value;
 							v3 = value;
 
 
-							customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom ]     = v1[ pp[ 0 ] ];
 							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
 							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
 							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
 							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
 
 
@@ -2243,7 +2243,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 							v2 = value[ 1 ];
 							v2 = value[ 1 ];
 							v3 = value[ 2 ];
 							v3 = value[ 2 ];
 
 
-							customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
+							customAttribute.array[ offset_custom ]     = v1[ pp[ 0 ] ];
 							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
 							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
 							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
 							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
 
 
@@ -2267,13 +2267,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
 
 
-							face = obj_faces[ chunk_faces3[ f ]	];
+							face = obj_faces[ chunk_faces3[ f ] ];
 
 
 							v1 = customAttribute.value[ face.a ];
 							v1 = customAttribute.value[ face.a ];
 							v2 = customAttribute.value[ face.b ];
 							v2 = customAttribute.value[ face.b ];
 							v3 = customAttribute.value[ face.c ];
 							v3 = customAttribute.value[ face.c ];
 
 
-							customAttribute.array[ offset_custom  ] 	= v1.x;
+							customAttribute.array[ offset_custom  ]   = v1.x;
 							customAttribute.array[ offset_custom + 1  ] = v1.y;
 							customAttribute.array[ offset_custom + 1  ] = v1.y;
 							customAttribute.array[ offset_custom + 2  ] = v1.z;
 							customAttribute.array[ offset_custom + 2  ] = v1.z;
 							customAttribute.array[ offset_custom + 3  ] = v1.w;
 							customAttribute.array[ offset_custom + 3  ] = v1.w;
@@ -2302,7 +2302,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 							v2 = value;
 							v2 = value;
 							v3 = value;
 							v3 = value;
 
 
-							customAttribute.array[ offset_custom  ] 	= v1.x;
+							customAttribute.array[ offset_custom  ]   = v1.x;
 							customAttribute.array[ offset_custom + 1  ] = v1.y;
 							customAttribute.array[ offset_custom + 1  ] = v1.y;
 							customAttribute.array[ offset_custom + 2  ] = v1.z;
 							customAttribute.array[ offset_custom + 2  ] = v1.z;
 							customAttribute.array[ offset_custom + 3  ] = v1.w;
 							customAttribute.array[ offset_custom + 3  ] = v1.w;
@@ -2331,7 +2331,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 							v2 = value[ 1 ];
 							v2 = value[ 1 ];
 							v3 = value[ 2 ];
 							v3 = value[ 2 ];
 
 
-							customAttribute.array[ offset_custom  ] 	= v1.x;
+							customAttribute.array[ offset_custom  ]   = v1.x;
 							customAttribute.array[ offset_custom + 1  ] = v1.y;
 							customAttribute.array[ offset_custom + 1  ] = v1.y;
 							customAttribute.array[ offset_custom + 2  ] = v1.z;
 							customAttribute.array[ offset_custom + 2  ] = v1.z;
 							customAttribute.array[ offset_custom + 3  ] = v1.w;
 							customAttribute.array[ offset_custom + 3  ] = v1.w;
@@ -2462,7 +2462,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 					ny = ( nay + nby + ncy ) / 3;
 					ny = ( nay + nby + ncy ) / 3;
 					nz = ( naz + nbz + ncz ) / 3;
 					nz = ( naz + nbz + ncz ) / 3;
 
 
-					normalArray[ i ] 	 = nx;
+					normalArray[ i ]   = nx;
 					normalArray[ i + 1 ] = ny;
 					normalArray[ i + 1 ] = ny;
 					normalArray[ i + 2 ] = nz;
 					normalArray[ i + 2 ] = nz;
 
 
@@ -2546,7 +2546,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		}
 		}
 
 
 		disableUnusedAttributes();
 		disableUnusedAttributes();
-		
+
 	}
 	}
 
 
 	this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) {
 	this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) {
@@ -2589,17 +2589,17 @@ THREE.WebGLRenderer = function ( parameters ) {
 				// indexed triangles
 				// indexed triangles
 
 
 				var type, size;
 				var type, size;
-			
+
 				if ( index.array instanceof Uint32Array ) {
 				if ( index.array instanceof Uint32Array ) {
-					
+
 					type = _gl.UNSIGNED_INT;
 					type = _gl.UNSIGNED_INT;
 					size = 4;
 					size = 4;
-					
+
 				} else {
 				} else {
-					
+
 					type = _gl.UNSIGNED_SHORT;
 					type = _gl.UNSIGNED_SHORT;
 					size = 2;
 					size = 2;
-					
+
 				}
 				}
 
 
 				var offsets = geometry.offsets;
 				var offsets = geometry.offsets;
@@ -2698,23 +2698,23 @@ THREE.WebGLRenderer = function ( parameters ) {
 			setLineWidth( material.linewidth );
 			setLineWidth( material.linewidth );
 
 
 			var index = geometryAttributes[ "index" ];
 			var index = geometryAttributes[ "index" ];
-			
+
 			if ( index ) {
 			if ( index ) {
 
 
 				// indexed lines
 				// indexed lines
 
 
 				var type, size;
 				var type, size;
-				
+
 				if ( index.array instanceof Uint32Array ){
 				if ( index.array instanceof Uint32Array ){
-					
+
 					type = _gl.UNSIGNED_INT;
 					type = _gl.UNSIGNED_INT;
 					size = 4;
 					size = 4;
-					
+
 				} else {
 				} else {
-					
+
 					type = _gl.UNSIGNED_SHORT;
 					type = _gl.UNSIGNED_SHORT;
 					size = 2;
 					size = 2;
-					
+
 				}
 				}
 
 
 				var offsets = geometry.offsets;
 				var offsets = geometry.offsets;
@@ -2727,7 +2727,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 						_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 						_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 
 
 					}
 					}
-					
+
 					_gl.drawElements( _gl.LINES, index.array.length, type, 0 ); // 2 bytes per Uint16Array
 					_gl.drawElements( _gl.LINES, index.array.length, type, 0 ); // 2 bytes per Uint16Array
 
 
 					_this.info.render.calls ++;
 					_this.info.render.calls ++;
@@ -2753,7 +2753,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 						}
 						}
 
 
 						// render indexed lines
 						// render indexed lines
-						
+
 						_gl.drawElements( _gl.LINES, offsets[ i ].count, type, offsets[ i ].start * size ); // 2 bytes per Uint16Array
 						_gl.drawElements( _gl.LINES, offsets[ i ].count, type, offsets[ i ].start * size ); // 2 bytes per Uint16Array
 
 
 						_this.info.render.calls ++;
 						_this.info.render.calls ++;
@@ -2970,7 +2970,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 			var type = geometryGroup.__typeArray === Uint32Array ? _gl.UNSIGNED_INT : _gl.UNSIGNED_SHORT;
 			var type = geometryGroup.__typeArray === Uint32Array ? _gl.UNSIGNED_INT : _gl.UNSIGNED_SHORT;
 
 
 			// wireframe
 			// wireframe
-			
+
 			if ( material.wireframe ) {
 			if ( material.wireframe ) {
 
 
 				setLineWidth( material.wireframeLinewidth );
 				setLineWidth( material.wireframeLinewidth );
@@ -4106,7 +4106,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 			skinning: material.skinning,
 			skinning: material.skinning,
 			maxBones: maxBones,
 			maxBones: maxBones,
-			useVertexTexture: _supportsBoneTextures && object && object.useVertexTexture,
+			useVertexTexture: _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture,
 
 
 			morphTargets: material.morphTargets,
 			morphTargets: material.morphTargets,
 			morphNormals: material.morphNormals,
 			morphNormals: material.morphNormals,
@@ -4319,26 +4319,26 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 		if ( material.skinning ) {
 		if ( material.skinning ) {
 
 
-			if ( _supportsBoneTextures && object.useVertexTexture ) {
+			if ( _supportsBoneTextures && object.skeleton.useVertexTexture ) {
 
 
 				if ( p_uniforms.boneTexture !== null ) {
 				if ( p_uniforms.boneTexture !== null ) {
 
 
 					var textureUnit = getTextureUnit();
 					var textureUnit = getTextureUnit();
 
 
 					_gl.uniform1i( p_uniforms.boneTexture, textureUnit );
 					_gl.uniform1i( p_uniforms.boneTexture, textureUnit );
-					_this.setTexture( object.boneTexture, textureUnit );
+					_this.setTexture( object.skeleton.boneTexture, textureUnit );
 
 
 				}
 				}
 
 
 				if ( p_uniforms.boneTextureWidth !== null ) {
 				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 ) {
 				if ( p_uniforms.boneTextureHeight !== null ) {
 
 
-					_gl.uniform1i( p_uniforms.boneTextureHeight, object.boneTextureHeight );
+					_gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight );
 
 
 				}
 				}
 
 
@@ -4346,7 +4346,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 				if ( p_uniforms.boneGlobalMatrices !== null ) {
 				if ( p_uniforms.boneGlobalMatrices !== null ) {
 
 
-					_gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.boneMatrices );
+					_gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices );
 
 
 				}
 				}
 
 
@@ -4510,10 +4510,10 @@ THREE.WebGLRenderer = function ( parameters ) {
 		}
 		}
 
 
 		// uv repeat and offset setting priorities
 		// uv repeat and offset setting priorities
-		//	1. color map
-		//	2. specular map
-		//	3. normal map
-		//	4. bump map
+		//  1. color map
+		//  2. specular map
+		//  3. normal map
+		//  4. bump map
 
 
 		var uvScaleMap;
 		var uvScaleMap;
 
 
@@ -4807,7 +4807,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 					offset = i * 2;
 					offset = i * 2;
 
 
-					uniform._array[ offset ] 	 = value[ i ].x;
+					uniform._array[ offset ]   = value[ i ].x;
 					uniform._array[ offset + 1 ] = value[ i ].y;
 					uniform._array[ offset + 1 ] = value[ i ].y;
 
 
 				}
 				}
@@ -4826,7 +4826,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 					offset = i * 3;
 					offset = i * 3;
 
 
-					uniform._array[ offset ] 	 = value[ i ].x;
+					uniform._array[ offset ]   = value[ i ].x;
 					uniform._array[ offset + 1 ] = value[ i ].y;
 					uniform._array[ offset + 1 ] = value[ i ].y;
 					uniform._array[ offset + 2 ] = value[ i ].z;
 					uniform._array[ offset + 2 ] = value[ i ].z;
 
 
@@ -4846,7 +4846,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 					offset = i * 4;
 					offset = i * 4;
 
 
-					uniform._array[ offset ] 	 = value[ i ].x;
+					uniform._array[ offset ]   = value[ i ].x;
 					uniform._array[ offset + 1 ] = value[ i ].y;
 					uniform._array[ offset + 1 ] = value[ i ].y;
 					uniform._array[ offset + 2 ] = value[ i ].z;
 					uniform._array[ offset + 2 ] = value[ i ].z;
 					uniform._array[ offset + 3 ] = value[ i ].w;
 					uniform._array[ offset + 3 ] = value[ i ].w;
@@ -5663,7 +5663,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 						_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );
 						_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );
 
 
 					} else {
 					} else {
-						
+
 						var mipmap, mipmaps = cubeImage[ i ].mipmaps;
 						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 ++ ) {
@@ -5995,7 +5995,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 	function allocateBones ( object ) {
 	function allocateBones ( object ) {
 
 
-		if ( _supportsBoneTextures && object && object.useVertexTexture ) {
+		if ( _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture ) {
 
 
 			return 1024;
 			return 1024;
 
 
@@ -6005,7 +6005,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 			// ( for example when prebuilding shader
 			// ( for example when prebuilding shader
 			//   to be used with multiple objects )
 			//   to be used with multiple objects )
 			//
 			//
-			// 	- leave some extra space for other uniforms
+			//  - leave some extra space for other uniforms
 			//  - limit here is ANGLE's 254 max uniform vectors
 			//  - limit here is ANGLE's 254 max uniform vectors
 			//    (up to 54 should be safe)
 			//    (up to 54 should be safe)
 
 
@@ -6016,11 +6016,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 			if ( object !== undefined && object instanceof THREE.SkinnedMesh ) {
 			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)" );
 
 
 				}
 				}
 
 
@@ -6113,8 +6113,8 @@ THREE.WebGLRenderer = function ( parameters ) {
 		_glExtensionCompressedTextureS3TC = _gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
 		_glExtensionCompressedTextureS3TC = _gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
 
 
 		_glExtensionElementIndexUint = _gl.getExtension( 'OES_element_index_uint' );
 		_glExtensionElementIndexUint = _gl.getExtension( 'OES_element_index_uint' );
-		
-		
+
+
 		if ( _glExtensionTextureFloat === null ) {
 		if ( _glExtensionTextureFloat === null ) {
 
 
 			console.log( 'THREE.WebGLRenderer: Float textures not supported.' );
 			console.log( 'THREE.WebGLRenderer: Float textures not supported.' );
@@ -6184,7 +6184,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA );
 		_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA );
 
 
 		_gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
 		_gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
-		
+
 		_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
 		_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
 
 
 	};
 	};

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

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

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

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

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

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

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

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

部分文件因为文件数量过多而无法显示