mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-11-16 19:17:57 +01:00
271 lines
5.0 KiB
JavaScript
Executable File
271 lines
5.0 KiB
JavaScript
Executable File
/**
|
|
* @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 ) {
|
|
|
|
var animName = geometry.animations[ i ].name;
|
|
scope.animations[ animName ] = new THREE.Animation( scope, geometry.animations[ i ] );
|
|
|
|
}
|
|
|
|
// Create the debug visualization
|
|
|
|
scope.skeletonHelper = new THREE.SkeletonHelper( scope );
|
|
scope.skeletonHelper.material.linewidth = 3;
|
|
scope.add( scope.skeletonHelper );
|
|
|
|
scope.showSkeleton( 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 ].stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
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.showSkeleton = function( boolean ) {
|
|
|
|
this.skeletonHelper.visible = boolean;
|
|
|
|
}
|
|
|
|
this.showModel = function( boolean ) {
|
|
|
|
this.visible = boolean;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
THREE.BlendCharacter.prototype = Object.create( THREE.SkinnedMesh.prototype );
|
|
THREE.BlendCharacter.prototype.constructor = THREE.BlendCharacter;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|