|
@@ -237,67 +237,186 @@ THREE.MMDLoader.prototype.pourVmdIntoCamera = function ( camera, vmd, name ) {
|
|
|
|
|
|
var orderedMotions = helper.createOrderedMotionArray( vmd.cameras );
|
|
|
|
|
|
- var q = new THREE.Quaternion();
|
|
|
- var e = new THREE.Euler();
|
|
|
- var pkeys = [];
|
|
|
- var ckeys = [];
|
|
|
- var ukeys = [];
|
|
|
- var fkeys = [];
|
|
|
+ var times = [];
|
|
|
+ var centers = [];
|
|
|
+ var quaternions = [];
|
|
|
+ var positions = [];
|
|
|
+ var fovs = [];
|
|
|
+
|
|
|
+ var cInterpolations = [];
|
|
|
+ var qInterpolations = [];
|
|
|
+ var pInterpolations = [];
|
|
|
+ var fInterpolations = [];
|
|
|
+
|
|
|
+ var quaternion = new THREE.Quaternion();
|
|
|
+ var euler = new THREE.Euler();
|
|
|
+ var position = new THREE.Vector3();
|
|
|
+ var center = new THREE.Vector3();
|
|
|
+
|
|
|
+ var pushVector3 = function ( array, vec ) {
|
|
|
+
|
|
|
+ array.push( vec.x );
|
|
|
+ array.push( vec.y );
|
|
|
+ array.push( vec.z );
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ var pushQuaternion = function ( array, q ) {
|
|
|
+
|
|
|
+ array.push( q.x );
|
|
|
+ array.push( q.y );
|
|
|
+ array.push( q.z );
|
|
|
+ array.push( q.w );
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ var pushInterpolation = function ( array, interpolation, index ) {
|
|
|
+
|
|
|
+ array.push( interpolation[ index * 4 + 0 ] / 127 ); // x1
|
|
|
+ array.push( interpolation[ index * 4 + 1 ] / 127 ); // x2
|
|
|
+ array.push( interpolation[ index * 4 + 2 ] / 127 ); // y1
|
|
|
+ array.push( interpolation[ index * 4 + 3 ] / 127 ); // y2
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ var createTrack = function ( node, type, times, values, interpolations ) {
|
|
|
+
|
|
|
+ /*
|
|
|
+ * optimizes here not to let KeyframeTrackPrototype optimize
|
|
|
+ * because KeyframeTrackPrototype optimizes times and values but
|
|
|
+ * doesn't optimize interpolations.
|
|
|
+ */
|
|
|
+ if ( times.length > 2 ) {
|
|
|
+
|
|
|
+ times = times.slice();
|
|
|
+ values = values.slice();
|
|
|
+ interpolations = interpolations.slice();
|
|
|
+
|
|
|
+ var stride = values.length / times.length;
|
|
|
+ var interpolateStride = ( stride === 3 ) ? 12 : 4; // 3: Vector3, others: Quaternion or Number
|
|
|
+
|
|
|
+ var aheadIndex = 2;
|
|
|
+ var index = 1;
|
|
|
+
|
|
|
+ for ( aheadIndex = 2, endIndex = times.length; aheadIndex < endIndex; aheadIndex ++ ) {
|
|
|
+
|
|
|
+ for ( var i = 0; i < stride; i ++ ) {
|
|
|
+
|
|
|
+ if ( values[ index * stride + i ] !== values[ ( index - 1 ) * stride + i ] ||
|
|
|
+ values[ index * stride + i ] !== values[ aheadIndex * stride + i ] ) {
|
|
|
+
|
|
|
+ index ++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( aheadIndex > index ) {
|
|
|
+
|
|
|
+ times[ index ] = times[ aheadIndex ];
|
|
|
+
|
|
|
+ for ( var i = 0; i < stride; i ++ ) {
|
|
|
+
|
|
|
+ values[ index * stride + i ] = values[ aheadIndex * stride + i ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ for ( var i = 0; i < interpolateStride; i ++ ) {
|
|
|
+
|
|
|
+ interpolations[ index * interpolateStride + i ] = interpolations[ aheadIndex * interpolateStride + i ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ times.length = index + 1;
|
|
|
+ values.length = ( index + 1 ) * stride;
|
|
|
+ interpolations.length = ( index + 1 ) * interpolateStride;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return new THREE.MMDLoader[ type ]( node, times, values, interpolations );
|
|
|
+
|
|
|
+ };
|
|
|
|
|
|
for ( var i = 0; i < orderedMotions.length; i++ ) {
|
|
|
|
|
|
var m = orderedMotions[ i ];
|
|
|
- var t = m.frameNum / 30;
|
|
|
- var p = m.position;
|
|
|
- var r = m.rotation;
|
|
|
- var d = m.distance;
|
|
|
- var f = m.fov;
|
|
|
|
|
|
- var position = new THREE.Vector3( 0, 0, -d );
|
|
|
- var center = new THREE.Vector3( p[ 0 ], p[ 1 ], p[ 2 ] );
|
|
|
- var up = new THREE.Vector3( 0, 1, 0 );
|
|
|
+ var time = m.frameNum / 30;
|
|
|
+ var pos = m.position;
|
|
|
+ var rot = m.rotation;
|
|
|
+ var distance = m.distance;
|
|
|
+ var fov = m.fov;
|
|
|
+ var interpolation = m.interpolation;
|
|
|
+
|
|
|
+ position.set( 0, 0, -distance );
|
|
|
+ center.set( pos[ 0 ], pos[ 1 ], pos[ 2 ] );
|
|
|
|
|
|
- e.set( -r[ 0 ], -r[ 1 ], -r[ 2 ] );
|
|
|
- q.setFromEuler( e );
|
|
|
+ euler.set( -rot[ 0 ], -rot[ 1 ], -rot[ 2 ] );
|
|
|
+ quaternion.setFromEuler( euler );
|
|
|
|
|
|
position.add( center );
|
|
|
- position.applyQuaternion( q );
|
|
|
+ position.applyQuaternion( quaternion );
|
|
|
|
|
|
- up.applyQuaternion( q );
|
|
|
+ /*
|
|
|
+ * Note: This is a workaround not to make Animation system calculate lerp
|
|
|
+ * if the diff from the last frame is 1 frame (in 30fps).
|
|
|
+ */
|
|
|
+ if ( times.length > 0 && time < times[ times.length - 1 ] + ( 1 / 30 ) * 1.5 ) {
|
|
|
|
|
|
- helper.pushAnimationKey( pkeys, t, position, true );
|
|
|
- helper.pushAnimationKey( ckeys, t, center, true );
|
|
|
- helper.pushAnimationKey( ukeys, t, up, true );
|
|
|
- helper.pushAnimationKey( fkeys, t, f, true );
|
|
|
+ times[ times.length - 1 ] = time - 1e-13;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- helper.insertAnimationKeyAtTimeZero( pkeys, new THREE.Vector3( 0, 0, 0 ) );
|
|
|
- helper.insertAnimationKeyAtTimeZero( ckeys, new THREE.Vector3( 0, 0, 0 ) );
|
|
|
- helper.insertAnimationKeyAtTimeZero( ukeys, new THREE.Vector3( 0, 0, 0 ) );
|
|
|
- helper.insertAnimationKeyAtTimeZero( fkeys, 45 );
|
|
|
+ times.push( time );
|
|
|
|
|
|
- helper.insertStartAnimationKey( pkeys );
|
|
|
- helper.insertStartAnimationKey( ckeys );
|
|
|
- helper.insertStartAnimationKey( ukeys );
|
|
|
- helper.insertStartAnimationKey( fkeys );
|
|
|
+ pushVector3( centers, center );
|
|
|
+ pushQuaternion( quaternions, quaternion );
|
|
|
+ pushVector3( positions, position );
|
|
|
|
|
|
- var tracks = [];
|
|
|
+ fovs.push( fov );
|
|
|
+
|
|
|
+ for ( var j = 0; j < 3; j ++ ) {
|
|
|
|
|
|
- tracks.push( helper.generateTrackFromAnimationKeys( pkeys, 'VectorKeyframeTrackEx', '.position' ) );
|
|
|
- tracks.push( helper.generateTrackFromAnimationKeys( ckeys, 'VectorKeyframeTrackEx', '.center' ) );
|
|
|
- tracks.push( helper.generateTrackFromAnimationKeys( ukeys, 'VectorKeyframeTrackEx', '.up' ) );
|
|
|
- tracks.push( helper.generateTrackFromAnimationKeys( fkeys, 'NumberKeyframeTrackEx', '.fov' ) );
|
|
|
+ pushInterpolation( cInterpolations, interpolation, j );
|
|
|
|
|
|
- camera.center = new THREE.Vector3( 0, 0, 0 );
|
|
|
+ }
|
|
|
|
|
|
- if ( camera.animations === undefined ) {
|
|
|
+ pushInterpolation( qInterpolations, interpolation, 3 );
|
|
|
|
|
|
- camera.animations = [];
|
|
|
+ // use same one parameter for x, y, z axis.
|
|
|
+ for ( var j = 0; j < 3; j ++ ) {
|
|
|
+
|
|
|
+ pushInterpolation( pInterpolations, interpolation, 4 );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ pushInterpolation( fInterpolations, interpolation, 5 );
|
|
|
|
|
|
}
|
|
|
|
|
|
- camera.animations.push( new THREE.AnimationClip( name === undefined ? THREE.Math.generateUUID() : name, -1, tracks ) );
|
|
|
+ if ( times.length === 0 ) return;
|
|
|
+
|
|
|
+ var tracks = [];
|
|
|
+
|
|
|
+ tracks.push( createTrack( '.center', 'VectorKeyframeTrackEx', times, centers, cInterpolations ) );
|
|
|
+ tracks.push( createTrack( '.quaternion', 'QuaternionKeyframeTrackEx', times, quaternions, qInterpolations ) );
|
|
|
+ tracks.push( createTrack( '.position', 'VectorKeyframeTrackEx', times, positions, pInterpolations ) );
|
|
|
+ tracks.push( createTrack( '.fov', 'NumberKeyframeTrackEx', times, fovs, fInterpolations ) );
|
|
|
+
|
|
|
+ var clip = new THREE.AnimationClip( name === undefined ? THREE.Math.generateUUID() : name, -1, tracks );
|
|
|
+
|
|
|
+ if ( clip !== null ) {
|
|
|
+
|
|
|
+ if ( camera.center === undefined ) camera.center = new THREE.Vector3( 0, 0, 0 );
|
|
|
+ if ( camera.animations === undefined ) camera.animations = [];
|
|
|
+ camera.animations.push( clip );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
};
|
|
|
|
|
@@ -377,7 +496,6 @@ THREE.MMDLoader.prototype.parseModel = function ( buffer, modelExtension ) {
|
|
|
|
|
|
THREE.MMDLoader.prototype.parsePmd = function ( buffer ) {
|
|
|
|
|
|
- var scope = this;
|
|
|
var pmd = {};
|
|
|
var dv = new THREE.MMDLoader.DataView( buffer );
|
|
|
var helper = new THREE.MMDLoader.DataCreationHelper();
|
|
@@ -872,7 +990,6 @@ THREE.MMDLoader.prototype.parsePmd = function ( buffer ) {
|
|
|
|
|
|
THREE.MMDLoader.prototype.parsePmx = function ( buffer ) {
|
|
|
|
|
|
- var scope = this;
|
|
|
var pmx = {};
|
|
|
var dv = new THREE.MMDLoader.DataView( buffer );
|
|
|
var helper = new THREE.MMDLoader.DataCreationHelper();
|
|
@@ -1421,7 +1538,6 @@ THREE.MMDLoader.prototype.parsePmx = function ( buffer ) {
|
|
|
|
|
|
THREE.MMDLoader.prototype.parseVmd = function ( buffer ) {
|
|
|
|
|
|
- var scope = this;
|
|
|
var vmd = {};
|
|
|
var dv = new THREE.MMDLoader.DataView( buffer );
|
|
|
var helper = new THREE.MMDLoader.DataCreationHelper();
|
|
@@ -1454,7 +1570,6 @@ THREE.MMDLoader.prototype.parseVmd = function ( buffer ) {
|
|
|
p.position = dv.getFloat32Array( 3 );
|
|
|
p.rotation = dv.getFloat32Array( 4 );
|
|
|
p.interpolation = dv.getUint8Array( 64 );
|
|
|
-
|
|
|
return p;
|
|
|
|
|
|
};
|
|
@@ -1937,11 +2052,18 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
|
|
|
param.isLocal = grant.isLocal;
|
|
|
param.affectRotation = grant.affectRotation;
|
|
|
param.affectPosition = grant.affectPosition;
|
|
|
+ param.transformationClass = b.transformationClass;
|
|
|
|
|
|
grants.push( param );
|
|
|
|
|
|
}
|
|
|
|
|
|
+ grants.sort( function ( a, b ) {
|
|
|
+
|
|
|
+ return a.transformationClass - b.transformationClass;
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
geometry.grants = grants;
|
|
|
|
|
|
};
|
|
@@ -2714,8 +2836,6 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
|
|
|
|
|
|
THREE.MMDLoader.prototype.createAnimation = function ( mesh, vmd, name ) {
|
|
|
|
|
|
- var scope = this;
|
|
|
-
|
|
|
var helper = new THREE.MMDLoader.DataCreationHelper();
|
|
|
|
|
|
var initMotionAnimations = function () {
|
|
@@ -2729,104 +2849,107 @@ THREE.MMDLoader.prototype.createAnimation = function ( mesh, vmd, name ) {
|
|
|
var bones = mesh.geometry.bones;
|
|
|
var orderedMotions = helper.createOrderedMotionArrays( bones, vmd.motions, 'boneName' );
|
|
|
|
|
|
- var animation = {
|
|
|
- name: name === undefined ? THREE.Math.generateUUID() : name,
|
|
|
- fps: 30,
|
|
|
- hierarchy: []
|
|
|
+ var tracks = [];
|
|
|
+
|
|
|
+ var pushInterpolation = function ( array, interpolation, index ) {
|
|
|
+
|
|
|
+ array.push( interpolation[ index + 0 ] / 127 ); // x1
|
|
|
+ array.push( interpolation[ index + 8 ] / 127 ); // x2
|
|
|
+ array.push( interpolation[ index + 4 ] / 127 ); // y1
|
|
|
+ array.push( interpolation[ index + 12 ] / 127 ); // y2
|
|
|
+
|
|
|
};
|
|
|
|
|
|
for ( var i = 0; i < orderedMotions.length; i++ ) {
|
|
|
|
|
|
- animation.hierarchy.push(
|
|
|
- {
|
|
|
- parent: bones[ i ].parent,
|
|
|
- keys: []
|
|
|
- }
|
|
|
- );
|
|
|
+ var times = [];
|
|
|
+ var positions = [];
|
|
|
+ var rotations = [];
|
|
|
+ var pInterpolations = [];
|
|
|
+ var rInterpolations = [];
|
|
|
|
|
|
- var array = orderedMotions[ i ];
|
|
|
- var keys = animation.hierarchy[ i ].keys;
|
|
|
var bone = bones[ i ];
|
|
|
+ var array = orderedMotions[ i ];
|
|
|
|
|
|
for ( var j = 0; j < array.length; j++ ) {
|
|
|
|
|
|
- var t = array[ j ].frameNum / 30;
|
|
|
- var p = array[ j ].position;
|
|
|
- var r = array[ j ].rotation;
|
|
|
+ var time = array[ j ].frameNum / 30;
|
|
|
+ var pos = array[ j ].position;
|
|
|
+ var rot = array[ j ].rotation;
|
|
|
+ var interpolation = array[ j ].interpolation;
|
|
|
|
|
|
- helper.pushBoneAnimationKey( keys, t, bone, p, r );
|
|
|
+ times.push( time );
|
|
|
|
|
|
- }
|
|
|
+ for ( var k = 0; k < 3; k ++ ) {
|
|
|
|
|
|
- helper.insertBoneAnimationKeyAtTimeZero( keys, bone );
|
|
|
- helper.insertStartBoneAnimationKey( keys );
|
|
|
+ positions.push( bone.pos[ k ] + pos[ k ] );
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- var clip = THREE.AnimationClip.parseAnimation( animation, mesh.geometry.bones );
|
|
|
+ for ( var k = 0; k < 4; k ++ ) {
|
|
|
|
|
|
- if ( clip !== null ) {
|
|
|
+ rotations.push( rot[ k ] );
|
|
|
|
|
|
- if ( mesh.geometry.animations === undefined ) {
|
|
|
+ }
|
|
|
|
|
|
- mesh.geometry.animations = [];
|
|
|
+ for ( var k = 0; k < 3; k ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ pushInterpolation( pInterpolations, interpolation, k );
|
|
|
|
|
|
- mesh.geometry.animations.push( clip );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ pushInterpolation( rInterpolations, interpolation, 3 );
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
- var initMorphAnimations = function () {
|
|
|
+ if ( times.length === 0 ) continue;
|
|
|
|
|
|
- if ( vmd.metadata.morphCount === 0 ) {
|
|
|
+ var boneName = '.bones[' + bone.name + ']';
|
|
|
|
|
|
- return;
|
|
|
+ tracks.push( new THREE.MMDLoader.VectorKeyframeTrackEx( boneName + '.position', times, positions, pInterpolations ) );
|
|
|
+ tracks.push( new THREE.MMDLoader.QuaternionKeyframeTrackEx( boneName + '.quaternion', times, rotations, rInterpolations ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
- var orderedMorphs = helper.createOrderedMotionArrays( mesh.geometry.morphTargets, vmd.morphs, 'morphName' );
|
|
|
-
|
|
|
- var morphAnimation = {
|
|
|
- fps: 30,
|
|
|
- hierarchy: []
|
|
|
- };
|
|
|
+ var clip = new THREE.AnimationClip( name === undefined ? THREE.Math.generateUUID() : name, -1, tracks );
|
|
|
|
|
|
- for ( var i = 0; i < orderedMorphs.length; i++ ) {
|
|
|
+ if ( clip !== null ) {
|
|
|
|
|
|
- morphAnimation.hierarchy.push( { keys: [] } );
|
|
|
+ if ( mesh.geometry.animations === undefined ) mesh.geometry.animations = [];
|
|
|
+ mesh.geometry.animations.push( clip );
|
|
|
|
|
|
- var array = orderedMorphs[ i ];
|
|
|
- var keys = morphAnimation.hierarchy[ i ].keys;
|
|
|
+ }
|
|
|
|
|
|
- for ( var j = 0; j < array.length; j++ ) {
|
|
|
+ };
|
|
|
|
|
|
- var t = array[ j ].frameNum / 30;
|
|
|
- var w = array[ j ].weight;
|
|
|
+ var initMorphAnimations = function () {
|
|
|
|
|
|
- helper.pushAnimationKey( keys, t, w );
|
|
|
+ if ( vmd.metadata.morphCount === 0 ) {
|
|
|
|
|
|
- }
|
|
|
+ return;
|
|
|
|
|
|
}
|
|
|
|
|
|
- // TODO: should we use THREE.AnimationClip.CreateFromMorphTargetSequence() instead?
|
|
|
+ var orderedMorphs = helper.createOrderedMotionArrays( mesh.geometry.morphTargets, vmd.morphs, 'morphName' );
|
|
|
|
|
|
var tracks = [];
|
|
|
|
|
|
for ( var i = 0; i < orderedMorphs.length; i++ ) {
|
|
|
|
|
|
- var keys = morphAnimation.hierarchy[ i ].keys;
|
|
|
+ var times = [];
|
|
|
+ var values = [];
|
|
|
+ var array = orderedMorphs[ i ];
|
|
|
|
|
|
- if ( keys.length === 0 ) {
|
|
|
+ for ( var j = 0; j < array.length; j++ ) {
|
|
|
|
|
|
- continue;
|
|
|
+ times.push( array[ j ].frameNum / 30 );
|
|
|
+ values.push( array[ j ].weight );
|
|
|
|
|
|
}
|
|
|
|
|
|
- tracks.push( helper.generateTrackFromAnimationKeys( keys, 'NumberKeyframeTrackEx', '.morphTargetInfluences[' + i + ']' ) );
|
|
|
+ if ( times.length === 0 ) continue;
|
|
|
+
|
|
|
+ tracks.push( new THREE.NumberKeyframeTrack( '.morphTargetInfluences[' + i + ']', times, values ) );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -2834,12 +2957,7 @@ THREE.MMDLoader.prototype.createAnimation = function ( mesh, vmd, name ) {
|
|
|
|
|
|
if ( clip !== null ) {
|
|
|
|
|
|
- if ( mesh.geometry.animations === undefined ) {
|
|
|
-
|
|
|
- mesh.geometry.animations = [];
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
+ if ( mesh.geometry.animations === undefined ) mesh.geometry.animations = [];
|
|
|
mesh.geometry.animations.push( clip );
|
|
|
|
|
|
}
|
|
@@ -2943,6 +3061,7 @@ THREE.MMDLoader.prototype.leftToRightVmd = function ( vmd ) {
|
|
|
|
|
|
for ( var i = 0; i < vmd.metadata.cameraCount; i++ ) {
|
|
|
|
|
|
+ helper.leftToRightVector3( vmd.cameras[ i ].position );
|
|
|
helper.leftToRightEuler( vmd.cameras[ i ].rotation );
|
|
|
|
|
|
}
|
|
@@ -3142,180 +3261,220 @@ THREE.MMDLoader.DataCreationHelper.prototype = {
|
|
|
|
|
|
return result;
|
|
|
|
|
|
- },
|
|
|
+ }
|
|
|
|
|
|
- pushAnimationKey: function ( keys, time, value, preventInterpolation ) {
|
|
|
+};
|
|
|
|
|
|
- /*
|
|
|
- * Note: This is a workaround not to make Animation system calculate lerp
|
|
|
- * if the diff from the last frame is 1 frame (in 30fps).
|
|
|
- */
|
|
|
- if ( keys.length > 0 && preventInterpolation === true ) {
|
|
|
+/*
|
|
|
+ * extends existing KeyframeTrack for bone and camera animation.
|
|
|
+ * - use Float64Array for times
|
|
|
+ * - use Cubic Bezier curves interpolation
|
|
|
+ */
|
|
|
+THREE.MMDLoader.VectorKeyframeTrackEx = function ( name, times, values, interpolationParameterArray ) {
|
|
|
|
|
|
- var k = keys[ keys.length - 1 ];
|
|
|
+ this.interpolationParameters = new Float32Array( interpolationParameterArray );
|
|
|
|
|
|
- if ( time < k.time + ( 1 / 30 ) * 1.5 ) {
|
|
|
+ THREE.VectorKeyframeTrack.call( this, name, times, values );
|
|
|
|
|
|
- keys.push(
|
|
|
- {
|
|
|
- time: time - 1e-13,
|
|
|
- value: k.value.clone === undefined ? k.value : k.value.clone()
|
|
|
- }
|
|
|
- );
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
+THREE.MMDLoader.VectorKeyframeTrackEx.prototype = Object.create( THREE.VectorKeyframeTrack.prototype );
|
|
|
+THREE.MMDLoader.VectorKeyframeTrackEx.prototype.constructor = THREE.MMDLoader.VectorKeyframeTrackEx;
|
|
|
+THREE.MMDLoader.VectorKeyframeTrackEx.prototype.TimeBufferType = Float64Array;
|
|
|
|
|
|
- }
|
|
|
+THREE.MMDLoader.VectorKeyframeTrackEx.prototype.InterpolantFactoryMethodCubicBezier = function( result ) {
|
|
|
|
|
|
- keys.push(
|
|
|
- {
|
|
|
- time: time,
|
|
|
- value: value
|
|
|
- }
|
|
|
- );
|
|
|
+ return new THREE.MMDLoader.CubicBezierInterpolation( this.times, this.values, this.getValueSize(), result, this.interpolationParameters );
|
|
|
|
|
|
- },
|
|
|
+};
|
|
|
|
|
|
- insertAnimationKeyAtTimeZero: function ( keys, value ) {
|
|
|
+THREE.MMDLoader.VectorKeyframeTrackEx.prototype.setInterpolation = function( interpolation ) {
|
|
|
|
|
|
- if ( keys.length === 0 ) {
|
|
|
+ this.createInterpolant = this.InterpolantFactoryMethodCubicBezier;
|
|
|
|
|
|
- keys.push(
|
|
|
- {
|
|
|
- time: 0.0,
|
|
|
- value: value
|
|
|
- }
|
|
|
- );
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
+THREE.MMDLoader.QuaternionKeyframeTrackEx = function ( name, times, values, interpolationParameterArray ) {
|
|
|
|
|
|
- },
|
|
|
+ this.interpolationParameters = new Float32Array( interpolationParameterArray );
|
|
|
|
|
|
- insertStartAnimationKey: function ( keys ) {
|
|
|
+ THREE.QuaternionKeyframeTrack.call( this, name, times, values );
|
|
|
|
|
|
- var k = keys[ 0 ];
|
|
|
+};
|
|
|
|
|
|
- if ( k.time !== 0.0 ) {
|
|
|
+THREE.MMDLoader.QuaternionKeyframeTrackEx.prototype = Object.create( THREE.QuaternionKeyframeTrack.prototype );
|
|
|
+THREE.MMDLoader.QuaternionKeyframeTrackEx.prototype.constructor = THREE.MMDLoader.QuaternionKeyframeTrackEx;
|
|
|
+THREE.MMDLoader.QuaternionKeyframeTrackEx.prototype.TimeBufferType = Float64Array;
|
|
|
|
|
|
- keys.unshift(
|
|
|
- {
|
|
|
- time: 0.0,
|
|
|
- value: k.value.clone === undefined ? k.value : k.value.clone()
|
|
|
- }
|
|
|
- );
|
|
|
+THREE.MMDLoader.QuaternionKeyframeTrackEx.prototype.InterpolantFactoryMethodCubicBezier = function( result ) {
|
|
|
|
|
|
- }
|
|
|
+ return new THREE.MMDLoader.CubicBezierInterpolation( this.times, this.values, this.getValueSize(), result, this.interpolationParameters );
|
|
|
|
|
|
- },
|
|
|
+};
|
|
|
|
|
|
- pushBoneAnimationKey: function ( keys, time, bone, pos, rot ) {
|
|
|
+THREE.MMDLoader.QuaternionKeyframeTrackEx.prototype.setInterpolation = function( interpolation ) {
|
|
|
|
|
|
- keys.push(
|
|
|
- {
|
|
|
- time: time,
|
|
|
- pos: [ bone.pos[ 0 ] + pos[ 0 ],
|
|
|
- bone.pos[ 1 ] + pos[ 1 ],
|
|
|
- bone.pos[ 2 ] + pos[ 2 ] ],
|
|
|
- rot: [ rot[ 0 ], rot[ 1 ], rot[ 2 ], rot[ 3 ] ],
|
|
|
- scl: [ 1, 1, 1 ]
|
|
|
- }
|
|
|
- );
|
|
|
+ this.createInterpolant = this.InterpolantFactoryMethodCubicBezier;
|
|
|
|
|
|
- },
|
|
|
+};
|
|
|
|
|
|
- insertBoneAnimationKeyAtTimeZero: function ( keys, bone ) {
|
|
|
+THREE.MMDLoader.NumberKeyframeTrackEx = function ( name, times, values, interpolationParameterArray ) {
|
|
|
|
|
|
- if ( keys.length === 0 ) {
|
|
|
+ this.interpolationParameters = new Float32Array( interpolationParameterArray );
|
|
|
|
|
|
- keys.push(
|
|
|
- {
|
|
|
- time: 0.0,
|
|
|
- pos: [ bone.pos[ 0 ], bone.pos[ 1 ], bone.pos[ 2 ] ],
|
|
|
- rot: [ 0, 0, 0, 1 ],
|
|
|
- scl: [ 1, 1, 1 ]
|
|
|
- }
|
|
|
- );
|
|
|
+ THREE.NumberKeyframeTrack.call( this, name, times, values );
|
|
|
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- },
|
|
|
+THREE.MMDLoader.NumberKeyframeTrackEx.prototype = Object.create( THREE.NumberKeyframeTrack.prototype );
|
|
|
+THREE.MMDLoader.NumberKeyframeTrackEx.prototype.constructor = THREE.MMDLoader.NumberKeyframeTrackEx;
|
|
|
+THREE.MMDLoader.NumberKeyframeTrackEx.prototype.TimeBufferType = Float64Array;
|
|
|
|
|
|
- insertStartBoneAnimationKey: function ( keys ) {
|
|
|
+THREE.MMDLoader.NumberKeyframeTrackEx.prototype.InterpolantFactoryMethodCubicBezier = function( result ) {
|
|
|
|
|
|
- var k = keys[ 0 ];
|
|
|
+ return new THREE.MMDLoader.CubicBezierInterpolation( this.times, this.values, this.getValueSize(), result, this.interpolationParameters );
|
|
|
|
|
|
- if ( k.time !== 0.0 ) {
|
|
|
+};
|
|
|
|
|
|
- keys.unshift(
|
|
|
- {
|
|
|
- time: 0.0,
|
|
|
- pos: [ k.pos[ 0 ], k.pos[ 1 ], k.pos[ 2 ] ],
|
|
|
- rot: [ k.rot[ 0 ], k.rot[ 1 ], k.rot[ 2 ], k.rot[ 3 ] ],
|
|
|
- scl: [ k.scl[ 0 ], k.scl[ 1 ], k.scl[ 2 ] ]
|
|
|
- }
|
|
|
- );
|
|
|
+THREE.MMDLoader.NumberKeyframeTrackEx.prototype.setInterpolation = function( interpolation ) {
|
|
|
|
|
|
- }
|
|
|
+ this.createInterpolant = this.InterpolantFactoryMethodCubicBezier;
|
|
|
|
|
|
- },
|
|
|
+};
|
|
|
|
|
|
- /*
|
|
|
- * This method wraps r74 Animation key frame track API for r73 Animation.
|
|
|
- */
|
|
|
- generateTrackFromAnimationKeys: function ( keys, trackKey, name ) {
|
|
|
+THREE.MMDLoader.CubicBezierInterpolation = function ( parameterPositions, sampleValues, sampleSize, resultBuffer, params ) {
|
|
|
|
|
|
- var times = [];
|
|
|
- var values = [];
|
|
|
+ THREE.Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
|
|
|
|
|
|
- for ( var i = 0; i < keys.length; i++ ) {
|
|
|
+ this.params = params;
|
|
|
|
|
|
- var key = keys[ i ];
|
|
|
+}
|
|
|
|
|
|
- times.push( key.time );
|
|
|
+THREE.MMDLoader.CubicBezierInterpolation.prototype = Object.create( THREE.LinearInterpolant.prototype );
|
|
|
+THREE.MMDLoader.CubicBezierInterpolation.prototype.constructor = THREE.MMDLoader.CubicBezierInterpolation;
|
|
|
|
|
|
- if ( trackKey === 'VectorKeyframeTrackEx' ) {
|
|
|
+THREE.MMDLoader.CubicBezierInterpolation.prototype.interpolate_ = function( i1, t0, t, t1 ) {
|
|
|
|
|
|
- values.push( key.value.x );
|
|
|
- values.push( key.value.y );
|
|
|
- values.push( key.value.z );
|
|
|
+ var result = this.resultBuffer;
|
|
|
+ var values = this.sampleValues;
|
|
|
+ var stride = this.valueSize;
|
|
|
|
|
|
- } else {
|
|
|
+ var offset1 = i1 * stride;
|
|
|
+ var offset0 = offset1 - stride;
|
|
|
|
|
|
- values.push( key.value );
|
|
|
+ var weight1 = ( t - t0 ) / ( t1 - t0 );
|
|
|
|
|
|
- }
|
|
|
+ if ( stride === 4 ) { // Quaternion
|
|
|
+
|
|
|
+ var x1 = this.params[ i1 * 4 + 0 ];
|
|
|
+ var x2 = this.params[ i1 * 4 + 1 ];
|
|
|
+ var y1 = this.params[ i1 * 4 + 2 ];
|
|
|
+ var y2 = this.params[ i1 * 4 + 3 ];
|
|
|
+
|
|
|
+ var ratio = this._calculate( x1, x2, y1, y2, weight1 );
|
|
|
+
|
|
|
+ THREE.Quaternion.slerpFlat( result, 0, values, offset0, values, offset1, ratio );
|
|
|
+
|
|
|
+ } else if ( stride === 3 ) { // Vector3
|
|
|
+
|
|
|
+ for ( var i = 0; i !== stride; ++ i ) {
|
|
|
+
|
|
|
+ var x1 = this.params[ i1 * 12 + i * 4 + 0 ];
|
|
|
+ var x2 = this.params[ i1 * 12 + i * 4 + 1 ];
|
|
|
+ var y1 = this.params[ i1 * 12 + i * 4 + 2 ];
|
|
|
+ var y2 = this.params[ i1 * 12 + i * 4 + 3 ];
|
|
|
+
|
|
|
+ var ratio = this._calculate( x1, x2, y1, y2, weight1 );
|
|
|
+
|
|
|
+ result[ i ] = values[ offset0 + i ] * ( 1 - ratio ) + values[ offset1 + i ] * ratio;
|
|
|
|
|
|
}
|
|
|
|
|
|
- return new THREE.MMDLoader[ trackKey ]( name, times, values );
|
|
|
+ } else { // Number
|
|
|
+
|
|
|
+ var x1 = this.params[ i1 * 4 + 0 ];
|
|
|
+ var x2 = this.params[ i1 * 4 + 1 ];
|
|
|
+ var y1 = this.params[ i1 * 4 + 2 ];
|
|
|
+ var y2 = this.params[ i1 * 4 + 3 ];
|
|
|
+
|
|
|
+ var ratio = this._calculate( x1, x2, y1, y2, weight1 );
|
|
|
+
|
|
|
+ result[ 0 ] = values[ offset0 ] * ( 1 - ratio ) + values[ offset1 ] * ratio;
|
|
|
|
|
|
}
|
|
|
|
|
|
+ return result;
|
|
|
+
|
|
|
};
|
|
|
|
|
|
-/*
|
|
|
- * These two classes are for high precision of times and values.
|
|
|
- * TODO: Let Three.KeyframeTrack support type select on instance creation.
|
|
|
- */
|
|
|
-THREE.MMDLoader.VectorKeyframeTrackEx = function ( name, times, values, interpolation ) {
|
|
|
+THREE.MMDLoader.CubicBezierInterpolation.prototype._calculate = function( x1, x2, y1, y2, x ) {
|
|
|
|
|
|
- THREE.VectorKeyframeTrack.call( this, name, times, values, interpolation );
|
|
|
+ /*
|
|
|
+ * Cubic Bezier curves
|
|
|
+ * https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves
|
|
|
+ *
|
|
|
+ * B(t) = ( 1 - t ) ^ 3 * P0
|
|
|
+ * + 3 * ( 1 - t ) ^ 2 * t * P1
|
|
|
+ * + 3 * ( 1 - t ) * t^2 * P2
|
|
|
+ * + t ^ 3 * P3
|
|
|
+ * ( 0 <= t <= 1 )
|
|
|
+ *
|
|
|
+ * MMD uses Cubic Bezier curves for bone and camera animation interpolation.
|
|
|
+ * http://d.hatena.ne.jp/edvakf/20111016/1318716097
|
|
|
+ *
|
|
|
+ * x = ( 1 - t ) ^ 3 * x0
|
|
|
+ * + 3 * ( 1 - t ) ^ 2 * t * x1
|
|
|
+ * + 3 * ( 1 - t ) * t^2 * x2
|
|
|
+ * + t ^ 3 * x3
|
|
|
+ * y = ( 1 - t ) ^ 3 * y0
|
|
|
+ * + 3 * ( 1 - t ) ^ 2 * t * y1
|
|
|
+ * + 3 * ( 1 - t ) * t^2 * y2
|
|
|
+ * + t ^ 3 * y3
|
|
|
+ * ( x0 = 0, y0 = 0 )
|
|
|
+ * ( x3 = 1, y3 = 1 )
|
|
|
+ * ( 0 <= t, x1, x2, y1, y2 <= 1 )
|
|
|
+ *
|
|
|
+ * Here solves this equation with Bisection method,
|
|
|
+ * https://en.wikipedia.org/wiki/Bisection_method
|
|
|
+ * gets t, and then calculate y.
|
|
|
+ *
|
|
|
+ * f(t) = 3 * ( 1 - t ) ^ 2 * t * x1
|
|
|
+ * + 3 * ( 1 - t ) * t^2 * x2
|
|
|
+ * + t ^ 3 - x = 0
|
|
|
+ *
|
|
|
+ * (Another option: Newton's method
|
|
|
+ * https://en.wikipedia.org/wiki/Newton%27s_method)
|
|
|
+ */
|
|
|
|
|
|
-};
|
|
|
+ var c = 0.5;
|
|
|
+ var t = c;
|
|
|
+ var s = 1.0 - t;
|
|
|
+ var loop = 15;
|
|
|
+ var eps = 1e-5;
|
|
|
+ var math = Math;
|
|
|
|
|
|
-THREE.MMDLoader.VectorKeyframeTrackEx.prototype = Object.create( THREE.VectorKeyframeTrack.prototype );
|
|
|
-THREE.MMDLoader.VectorKeyframeTrackEx.prototype.constructor = THREE.MMDLoader.VectorKeyframeTrackEx;
|
|
|
-THREE.MMDLoader.VectorKeyframeTrackEx.prototype.TimeBufferType = Float64Array;
|
|
|
+ var sst3, stt3, ttt;
|
|
|
|
|
|
-THREE.MMDLoader.NumberKeyframeTrackEx = function ( name, times, values, interpolation ) {
|
|
|
+ for ( var i = 0; i < loop; i ++ ) {
|
|
|
|
|
|
- THREE.VectorKeyframeTrack.call( this, name, times, values, interpolation );
|
|
|
+ sst3 = 3.0 * s * s * t;
|
|
|
+ stt3 = 3.0 * s * t * t;
|
|
|
+ ttt = t * t * t;
|
|
|
|
|
|
-};
|
|
|
+ var ft = ( sst3 * x1 ) + ( stt3 * x2 ) + ( ttt ) - x;
|
|
|
|
|
|
-THREE.MMDLoader.NumberKeyframeTrackEx.prototype = Object.create( THREE.NumberKeyframeTrack.prototype );
|
|
|
-THREE.MMDLoader.NumberKeyframeTrackEx.prototype.constructor = THREE.MMDLoader.NumberKeyframeTrackEx;
|
|
|
-THREE.MMDLoader.NumberKeyframeTrackEx.prototype.TimeBufferType = Float64Array;
|
|
|
+ if ( math.abs( ft ) < eps ) break;
|
|
|
+
|
|
|
+ c /= 2.0;
|
|
|
+
|
|
|
+ t += ( ft < 0 ) ? c : -c;
|
|
|
+ s = 1.0 - t;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return ( sst3 * y1 ) + ( stt3 * y2 ) + ttt;
|
|
|
+
|
|
|
+};
|
|
|
|
|
|
THREE.MMDLoader.DataView = function ( buffer, littleEndian ) {
|
|
|
|
|
@@ -3839,7 +3998,6 @@ THREE.MMDGrantSolver.prototype = {
|
|
|
q.set( 0, 0, 0, 1 );
|
|
|
q.slerp( pb.quaternion, g.ratio );
|
|
|
b.quaternion.multiply( q );
|
|
|
- b.updateMatrixWorld( true );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -4306,6 +4464,8 @@ THREE.MMDHelper.prototype = {
|
|
|
// TODO: Let PerspectiveCamera automatically update?
|
|
|
this.camera.updateProjectionMatrix();
|
|
|
|
|
|
+ this.camera.up.set( 0, 1, 0 );
|
|
|
+ this.camera.up.applyQuaternion( this.camera.quaternion );
|
|
|
this.camera.lookAt( this.camera.center );
|
|
|
|
|
|
}
|