提交 9aab3c6b 编写于 作者: D Don McCurdy

Provide AnimationClip objects from GLTFLoader.

Replaces GLTFLoader.Animations, GLTFAnimation and GLTFInterpolator, and
adds support for AnimationMixer / AnimationAction controls.
上级 f7d6b239
......@@ -54,14 +54,14 @@ THREE.GLTFLoader = ( function () {
} );
parser.parse( function ( scene, cameras, animations ) {
parser.parse( function ( scene, cameras, clips ) {
console.timeEnd( 'GLTFLoader' );
var glTF = {
"scene": scene,
"cameras": cameras,
"animations": animations
"clips": clips
};
callback( glTF );
......@@ -241,233 +241,66 @@ THREE.GLTFLoader = ( function () {
};
/* GLTFANIMATION */
/* ANIMATION */
GLTFLoader.Animations = new GLTFRegistry();
// Construction/initialization
function GLTFAnimation( interps ) {
this.running = false;
this.loop = false;
this.duration = 0;
this.startTime = 0;
this.interps = [];
this.uuid = THREE.Math.generateUUID();
if ( interps ) {
this.createInterpolators( interps );
}
}
GLTFAnimation.prototype.createInterpolators = function ( interps ) {
function createClip( name, interps ) {
var tracks = [];
for ( var i = 0, len = interps.length; i < len; i ++ ) {
var interp = new GLTFInterpolator( interps[ i ] );
this.interps.push( interp );
this.duration = Math.max( this.duration, interp.duration );
}
};
// Start/stop
GLTFAnimation.prototype.play = function () {
if ( this.running )
return;
this.startTime = Date.now();
this.running = true;
GLTFLoader.Animations.add( this.uuid, this );
};
GLTFAnimation.prototype.stop = function () {
this.running = false;
GLTFLoader.Animations.remove( this.uuid );
};
// Update - drive key frame evaluation
GLTFAnimation.prototype.update = function () {
if ( ! this.running ) return;
var now = Date.now();
var deltat = ( now - this.startTime ) / 1000;
var t = deltat % this.duration;
var nCycles = Math.floor( deltat / this.duration );
var interps = this.interps;
if ( nCycles >= 1 && ! this.loop ) {
this.running = false;
for ( var i = 0, l = interps.length; i < l; i ++ ) {
interps[ i ].interp( this.duration );
}
this.stop();
} else {
for ( var i = 0, l = interps.length; i < l; i ++ ) {
interps[ i ].interp( t );
}
}
};
/* GLTFINTERPOLATOR */
function GLTFInterpolator( param ) {
this.keys = param.keys;
this.values = param.values;
this.count = param.count;
this.type = param.type;
this.path = param.path;
this.isRot = false;
var node = param.target;
node.updateMatrix();
node.matrixAutoUpdate = true;
this.targetNode = node;
switch ( param.path ) {
case "translation" :
this.target = node.position;
this.originalValue = node.position.clone();
break;
case "rotation" :
validateInterpolator( interps[ i ] );
this.target = node.quaternion;
this.originalValue = node.quaternion.clone();
this.isRot = true;
break;
case "scale" :
this.target = node.scale;
this.originalValue = node.scale.clone();
break;
interps[ i ].target.updateMatrix();
interps[ i ].target.matrixAutoUpdate = true;
tracks.push( new THREE.KeyframeTrack(
interps[ i ].name,
interps[ i ].times,
interps[ i ].values,
interps[ i ].type
) );
}
this.duration = this.keys[ this.count - 1 ];
this.vec1 = new THREE.Vector3();
this.vec2 = new THREE.Vector3();
this.vec3 = new THREE.Vector3();
this.quat1 = new THREE.Quaternion();
this.quat2 = new THREE.Quaternion();
this.quat3 = new THREE.Quaternion();
return new THREE.AnimationClip( name, undefined, tracks );
}
//Interpolation and tweening methods
GLTFInterpolator.prototype.interp = function ( t ) {
if ( t == this.keys[ 0 ] ) {
if ( this.isRot ) {
this.quat3.fromArray( this.values );
} else {
this.vec3.fromArray( this.values );
}
} else if ( t < this.keys[ 0 ] ) {
/**
* Interp times are frequently non-sequential in the monster and cesium man
* models. That's probably a sign that the exporter is misbehaving, but to
* keep this backward-compatible we can swallow the errors.
*/
function validateInterpolator( interp ) {
if ( this.isRot ) {
var times = [];
var values = [];
var prevTime = null;
var currTime = null;
this.quat1.copy( this.originalValue );
this.quat2.fromArray( this.values );
THREE.Quaternion.slerp( this.quat1, this.quat2, this.quat3, t / this.keys[ 0 ] );
var stride = interp.values.length / interp.times.length;
} else {
this.vec3.copy( this.originalValue );
this.vec2.fromArray( this.values );
this.vec3.lerp( this.vec2, t / this.keys[ 0 ] );
}
} else if ( t >= this.keys[ this.count - 1 ] ) {
if ( this.isRot ) {
this.quat3.fromArray( this.values, ( this.count - 1 ) * 4 );
for ( var i = 0; i < interp.times.length; i ++ ) {
} else {
this.vec3.fromArray( this.values, ( this.count - 1 ) * 3 );
}
} else {
currTime = interp.times[ i ];
for ( var i = 0; i < this.count - 1; i ++ ) {
if (prevTime !== null && prevTime <= currTime) {
var key1 = this.keys[ i ];
var key2 = this.keys[ i + 1 ];
times.push( currTime );
if ( t >= key1 && t <= key2 ) {
for ( var j = 0; j < stride; j++ ) {
if ( this.isRot ) {
this.quat1.fromArray( this.values, i * 4 );
this.quat2.fromArray( this.values, ( i + 1 ) * 4 );
THREE.Quaternion.slerp( this.quat1, this.quat2, this.quat3, ( t - key1 ) / ( key2 - key1 ) );
} else {
this.vec3.fromArray( this.values, i * 3 );
this.vec2.fromArray( this.values, ( i + 1 ) * 3 );
this.vec3.lerp( this.vec2, ( t - key1 ) / ( key2 - key1 ) );
}
values.push( interp.values[ i * stride + j ] );
}
}
prevTime = currTime;
}
if ( this.target ) {
if ( this.isRot ) {
this.target.copy( this.quat3 );
} else {
this.target.copy( this.vec3 );
}
}
};
interp.times = new Float32Array( times );
interp.values = new Float32Array( values );
}
/*********************************/
/********** INTERNALS ************/
......@@ -539,6 +372,16 @@ THREE.GLTFLoader = ( function () {
'MAT4': 16
};
var PATH_PROPERTIES = {
scale: 'scale',
translation: 'position',
rotation: 'quaternion'
};
var INTERPOLATION = {
LINEAR: THREE.InterpolateLinear
};
/* UTILITY FUNCTIONS */
function _each( object, callback, thisObj ) {
......@@ -798,7 +641,7 @@ THREE.GLTFLoader = ( function () {
"scenes",
"cameras",
"animations"
"clips"
] ).then( function ( dependencies ) {
......@@ -813,16 +656,16 @@ THREE.GLTFLoader = ( function () {
}
var animations = [];
var clips = [];
for ( var name in dependencies.animations ) {
for ( var name in dependencies.clips ) {
var animation = dependencies.animations[ name ];
animations.push( animation );
var clip = dependencies.clips[ name ];
clips.push( clip );
}
callback( scene, cameras, animations );
callback( scene, cameras, clips);
}.bind( this ) );
......@@ -1435,7 +1278,9 @@ THREE.GLTFLoader = ( function () {
};
GLTFParser.prototype.loadAnimations = function () {
GLTFParser.prototype.loadClips = function () {
var scope = this;
......@@ -1470,12 +1315,11 @@ THREE.GLTFLoader = ( function () {
if ( node ) {
var interp = {
keys: inputAccessor.array,
times: inputAccessor.array,
values: outputAccessor.array,
count: inputAccessor.count,
target: node,
path: target.path,
type: sampler.interpolation
type: INTERPOLATION[ sampler.interpolation ],
name: node.name + '.' + PATH_PROPERTIES[ target.path ]
};
interps.push( interp );
......@@ -1486,10 +1330,7 @@ THREE.GLTFLoader = ( function () {
}
var _animation = new GLTFAnimation( interps );
_animation.name = "animation_" + animationId;
return _animation;
return createClip( "animation_" + animationId, interps );
} );
......
......@@ -137,6 +137,8 @@
var cameraNames = [];
var defaultCamera = null;
var gltf = null;
var mixer = null;
var clock = new THREE.Clock();
function onload() {
......@@ -312,17 +314,18 @@
}
if (gltf.animations && gltf.animations.length) {
if (gltf.clips && gltf.clips.length) {
var i, len = gltf.animations.length;
mixer = new THREE.AnimationMixer(object);
var i, len = gltf.clips.length;
for (i = 0; i < len; i++) {
var animation = gltf.animations[i];
animation.loop = true;
var clip = gltf.clips[i];
// There's .3333 seconds junk at the tail of the Monster animation that
// keeps it from looping cleanly. Clip it at 3 seconds
if (sceneInfo.animationTime)
animation.duration = sceneInfo.animationTime;
animation.play();
clip.duration = sceneInfo.animationTime;
mixer.clipAction(clip).play();
}
}
......@@ -353,7 +356,7 @@
function animate() {
requestAnimationFrame( animate );
THREE.GLTFLoader.Animations.update();
if (mixer) mixer.update(clock.getDelta());
THREE.GLTFLoader.Shaders.update(scene, camera);
if (cameraIndex == 0)
orbitControls.update();
......@@ -513,16 +516,17 @@
function toggleAnimations() {
var i, len = gltf.animations.length;
var i, len = gltf.clips.length;
for (i = 0; i < len; i++) {
var animation = gltf.animations[i];
var clip = gltf.clips[i];
var action = mixer.existingAction( clip );
if (animation.running) {
animation.stop();
if (action.isRunning()) {
action.stop();
} else {
animation.play();
action.play();
}
}
......@@ -549,17 +553,10 @@
cameraNames = [];
defaultCamera = null;
if (!loader || !gltf.animations)
if (!loader || !mixer)
return;
var i, len = gltf.animations.length;
for (i = 0; i < len; i++) {
var animation = gltf.animations[i];
if (animation.running) {
animation.stop();
}
}
mixer.stopAllAction();
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册