|
@@ -14,392 +14,391 @@ THREE.BVHLoader = function( manager ) {
|
|
|
|
|
|
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
|
|
|
|
|
|
-}
|
|
|
+};
|
|
|
|
|
|
-THREE.BVHLoader.prototype.load = function( url, onLoad, onProgress, onError ) {
|
|
|
+THREE.BVHLoader.prototype = {
|
|
|
|
|
|
- var scope = this;
|
|
|
+ constructor: THREE.BVHLoader,
|
|
|
|
|
|
- var loader = new THREE.XHRLoader( scope.manager );
|
|
|
- loader.setResponseType( 'arraybuffer' );
|
|
|
- loader.load( url, function( buffer ) {
|
|
|
+ load: function ( url, onLoad, onProgress, onError ) {
|
|
|
|
|
|
- onLoad( scope.parse( buffer ) );
|
|
|
+ var scope = this;
|
|
|
|
|
|
- }, onProgress, onError );
|
|
|
+ var loader = new THREE.XHRLoader( scope.manager );
|
|
|
+ loader.setResponseType( 'arraybuffer' );
|
|
|
+ loader.load( url, function( buffer ) {
|
|
|
|
|
|
-}
|
|
|
+ onLoad( scope.parse( buffer ) );
|
|
|
|
|
|
+ }, onProgress, onError );
|
|
|
|
|
|
-THREE.BVHLoader.prototype.parse = function( buffer ) {
|
|
|
+ },
|
|
|
|
|
|
- // convert buffer to ASCII string
|
|
|
- var text = "";
|
|
|
- var raw = new Uint8Array( buffer );
|
|
|
- for ( var i = 0; i < raw.length; ++ i ) {
|
|
|
+ parse: function ( buffer ) {
|
|
|
|
|
|
- text += String.fromCharCode( raw[ i ] );
|
|
|
+ /*
|
|
|
+ reads a string array (lines) from a BVH file
|
|
|
+ and outputs a skeleton structure including motion data
|
|
|
|
|
|
- }
|
|
|
+ returns thee root node:
|
|
|
+ { name: "", channels: [], children: [] }
|
|
|
+ */
|
|
|
+ function readBvh( lines ) {
|
|
|
|
|
|
- var lines = text.split( /[\r\n]+/g );
|
|
|
+ // read model structure
|
|
|
+ if ( nextLine( lines ) !== "HIERARCHY" ) {
|
|
|
|
|
|
- var bones = this._readBvh( lines );
|
|
|
+ throw "HIERARCHY expected";
|
|
|
|
|
|
- var threeBones = [];
|
|
|
- this._toTHREEBone( bones[ 0 ], threeBones );
|
|
|
+ }
|
|
|
|
|
|
- var threeClip = this._toTHREEAnimation( bones );
|
|
|
+ var list = []; // collects flat array of all bones
|
|
|
+ var root = readNode( lines, nextLine( lines ), list );
|
|
|
|
|
|
- return {
|
|
|
- skeleton: new THREE.Skeleton( threeBones ),
|
|
|
- clip: threeClip
|
|
|
- };
|
|
|
+ // read motion data
|
|
|
+ if ( nextLine( lines ) != "MOTION" ) {
|
|
|
|
|
|
+ throw "MOTION expected";
|
|
|
|
|
|
-}
|
|
|
+ }
|
|
|
|
|
|
-/*
|
|
|
- reads a string array (lines) from a BVH file
|
|
|
- and outputs a skeleton structure including motion data
|
|
|
+ // number of frames
|
|
|
+ var tokens = nextLine( lines ).split( /[\s]+/ );
|
|
|
+ var numFrames = parseInt( tokens[ 1 ] );
|
|
|
+ if ( isNaN( numFrames ) ) {
|
|
|
|
|
|
- returns thee root node:
|
|
|
- { name: "", channels: [], children: [] }
|
|
|
-*/
|
|
|
-THREE.BVHLoader.prototype._readBvh = function( lines ) {
|
|
|
+ throw "Failed to read number of frames.";
|
|
|
|
|
|
- // read model structure
|
|
|
- if ( this._nextLine( lines ) !== "HIERARCHY" ) {
|
|
|
+ }
|
|
|
|
|
|
- throw "HIERARCHY expected";
|
|
|
+ // frame time
|
|
|
+ tokens = nextLine( lines ).split( /[\s]+/ );
|
|
|
+ var frameTime = parseFloat( tokens[ 2 ] );
|
|
|
+ if ( isNaN( frameTime ) ) {
|
|
|
|
|
|
- }
|
|
|
+ throw "Failed to read frame time.";
|
|
|
|
|
|
- var list = []; // collects flat array of all bones
|
|
|
- var root = this._readNode( lines, this._nextLine( lines ), list );
|
|
|
+ }
|
|
|
|
|
|
- // read motion data
|
|
|
- if ( this._nextLine( lines ) != "MOTION" ) {
|
|
|
+ // read frame data line by line
|
|
|
+ for ( var i = 0; i < numFrames; ++ i ) {
|
|
|
|
|
|
- throw "MOTION expected";
|
|
|
+ tokens = nextLine( lines ).split( /[\s]+/ );
|
|
|
|
|
|
- }
|
|
|
+ readFrameData( tokens, i * frameTime, root, list );
|
|
|
|
|
|
- // number of frames
|
|
|
- var tokens = this._nextLine( lines ).split( /[\s]+/ );
|
|
|
- var numFrames = parseInt( tokens[ 1 ] );
|
|
|
- if ( isNaN( numFrames ) ) {
|
|
|
+ }
|
|
|
|
|
|
- throw "Failed to read number of frames.";
|
|
|
+ return list;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- // frame time
|
|
|
- tokens = this._nextLine( lines ).split( /[\s]+/ );
|
|
|
- var frameTime = parseFloat( tokens[ 2 ] );
|
|
|
- if ( isNaN( frameTime ) ) {
|
|
|
+ /*
|
|
|
+ Recursively reads data from a single frame into the bone hierarchy.
|
|
|
+ The passed bone hierarchy has to be structured in the same order as the BVH file.
|
|
|
+ keyframe data is stored in bone.frames.
|
|
|
|
|
|
- throw "Failed to read frame time.";
|
|
|
+ - data: splitted string array (frame values), values are shift()ed so
|
|
|
+ this should be empty after parsing the whole hierarchy.
|
|
|
+ - frameTime: playback time for this keyframe.
|
|
|
+ - bone: the bone to read frame data from.
|
|
|
+ */
|
|
|
+ function readFrameData( data, frameTime, bone ) {
|
|
|
|
|
|
- }
|
|
|
+ // end sites have no motion data
|
|
|
+ if ( bone.type === "ENDSITE" ) {
|
|
|
|
|
|
- // read frame data line by line
|
|
|
- for ( var i = 0; i < numFrames; ++ i ) {
|
|
|
+ return;
|
|
|
|
|
|
- tokens = this._nextLine( lines ).split( /[\s]+/ );
|
|
|
+ }
|
|
|
|
|
|
- this._readFrameData( tokens, i * frameTime, root, list );
|
|
|
+ // add keyframe
|
|
|
+ var keyframe = {
|
|
|
+ time: frameTime,
|
|
|
+ position: { x: 0, y: 0, z: 0 },
|
|
|
+ rotation: new THREE.Quaternion(),
|
|
|
+ };
|
|
|
|
|
|
- }
|
|
|
+ bone.frames.push( keyframe );
|
|
|
|
|
|
- return list;
|
|
|
+ var quat = new THREE.Quaternion();
|
|
|
|
|
|
-}
|
|
|
+ var vx = new THREE.Vector3( 1, 0, 0 );
|
|
|
+ var vy = new THREE.Vector3( 0, 1, 0 );
|
|
|
+ var vz = new THREE.Vector3( 0, 0, 1 );
|
|
|
+
|
|
|
+ // parse values for each channel in node
|
|
|
+ for ( var i = 0; i < bone.channels.length; ++ i ) {
|
|
|
+
|
|
|
+ switch ( bone.channels[ i ] ) {
|
|
|
+
|
|
|
+ case "Xposition":
|
|
|
+ keyframe.position.x = parseFloat( data.shift().trim() );
|
|
|
+ break;
|
|
|
+ case "Yposition":
|
|
|
+ keyframe.position.y = parseFloat( data.shift().trim() );
|
|
|
+ break;
|
|
|
+ case "Zposition":
|
|
|
+ keyframe.position.z = parseFloat( data.shift().trim() );
|
|
|
+ break;
|
|
|
+ case "Xrotation":
|
|
|
+ quat.setFromAxisAngle( vx, parseFloat( data.shift().trim() ) * Math.PI / 180 );
|
|
|
+ keyframe.rotation.multiply( quat );
|
|
|
+ break;
|
|
|
+ case "Yrotation":
|
|
|
+ quat.setFromAxisAngle( vy, parseFloat( data.shift().trim() ) * Math.PI / 180 );
|
|
|
+ keyframe.rotation.multiply( quat );
|
|
|
+ break;
|
|
|
+ case "Zrotation":
|
|
|
+ quat.setFromAxisAngle( vz, parseFloat( data.shift().trim() ) * Math.PI / 180 );
|
|
|
+ keyframe.rotation.multiply( quat );
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw "invalid channel type";
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // parse child nodes
|
|
|
+ for ( var i = 0; i < bone.children.length; ++ i ) {
|
|
|
+
|
|
|
+ readFrameData( data, frameTime, bone.children[ i ] );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
-/*
|
|
|
- Recursively reads data from a single frame into the bone hierarchy.
|
|
|
- The passed bone hierarchy has to be structured in the same order as the BVH file.
|
|
|
- keyframe data is stored in bone.frames.
|
|
|
+ }
|
|
|
|
|
|
- - data: splitted string array (frame values), values are shift()ed so
|
|
|
- this should be empty after parsing the whole hierarchy.
|
|
|
- - frameTime: playback time for this keyframe.
|
|
|
- - bone: the bone to read frame data from.
|
|
|
-*/
|
|
|
-THREE.BVHLoader.prototype._readFrameData = function( data, frameTime, bone ) {
|
|
|
+ /*
|
|
|
+ Recursively parses the HIERACHY section of the BVH file
|
|
|
|
|
|
- // end sites have no motion data
|
|
|
- if ( bone.type === "ENDSITE" ) {
|
|
|
+ - lines: all lines of the file. lines are consumed as we go along.
|
|
|
+ - firstline: line containing the node type and name e.g. "JOINT hip"
|
|
|
+ - list: collects a flat list of nodes
|
|
|
|
|
|
- return;
|
|
|
+ returns: a BVH node including children
|
|
|
+ */
|
|
|
+ function readNode( lines, firstline, list ) {
|
|
|
|
|
|
- }
|
|
|
+ var node = { name: "", type: "", frames: [] };
|
|
|
+ list.push( node );
|
|
|
|
|
|
- // add keyframe
|
|
|
- var keyframe = {
|
|
|
- time: frameTime,
|
|
|
- position: { x: 0, y: 0, z: 0 },
|
|
|
- rotation: new THREE.Quaternion(),
|
|
|
- };
|
|
|
-
|
|
|
- bone.frames.push( keyframe );
|
|
|
-
|
|
|
- var vx = new THREE.Vector3( 1, 0, 0 );
|
|
|
- var vy = new THREE.Vector3( 0, 1, 0 );
|
|
|
- var vz = new THREE.Vector3( 0, 0, 1 );
|
|
|
-
|
|
|
- // parse values for each channel in node
|
|
|
- for ( var i = 0; i < bone.channels.length; ++ i ) {
|
|
|
-
|
|
|
- switch ( bone.channels[ i ] ) {
|
|
|
-
|
|
|
- case "Xposition":
|
|
|
- keyframe.position.x = parseFloat( data.shift().trim() );
|
|
|
- break;
|
|
|
- case "Yposition":
|
|
|
- keyframe.position.y = parseFloat( data.shift().trim() );
|
|
|
- break;
|
|
|
- case "Zposition":
|
|
|
- keyframe.position.z = parseFloat( data.shift().trim() );
|
|
|
- break;
|
|
|
- case "Xrotation":
|
|
|
- var quat = new THREE.Quaternion();
|
|
|
- quat.setFromAxisAngle( vx, parseFloat( data.shift().trim() ) * Math.PI / 180 );
|
|
|
+ // parse node type and name.
|
|
|
+ var tokens = firstline.split( /[\s]+/ );
|
|
|
|
|
|
- keyframe.rotation.multiply( quat );
|
|
|
- break;
|
|
|
- case "Yrotation":
|
|
|
- var quat = new THREE.Quaternion();
|
|
|
- quat.setFromAxisAngle( vy, parseFloat( data.shift().trim() ) * Math.PI / 180 );
|
|
|
+ if ( tokens[ 0 ].toUpperCase() === "END" && tokens[ 1 ].toUpperCase() === "SITE" ) {
|
|
|
|
|
|
- keyframe.rotation.multiply( quat );
|
|
|
- break;
|
|
|
- case "Zrotation":
|
|
|
- var quat = new THREE.Quaternion();
|
|
|
- quat.setFromAxisAngle( vz, parseFloat( data.shift().trim() ) * Math.PI / 180 );
|
|
|
+ node.type = "ENDSITE";
|
|
|
+ node.name = "ENDSITE"; // bvh end sites have no name
|
|
|
|
|
|
- keyframe.rotation.multiply( quat );
|
|
|
- break;
|
|
|
- default:
|
|
|
- throw "invalid channel type";
|
|
|
- break;
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
+ node.name = tokens[ 1 ];
|
|
|
+ node.type = tokens[ 0 ].toUpperCase();
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- // parse child nodes
|
|
|
- for ( var i = 0; i < bone.children.length; ++ i ) {
|
|
|
+ if ( nextLine( lines ) != "{" ) {
|
|
|
|
|
|
- this._readFrameData( data, frameTime, bone.children[ i ] );
|
|
|
+ throw "Expected opening { after type & name";
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
-}
|
|
|
+ // parse OFFSET
|
|
|
+ tokens = nextLine( lines ).split( /[\s]+/ );
|
|
|
|
|
|
-/*
|
|
|
- Recursively parses the HIERACHY section of the BVH file
|
|
|
+ if ( tokens[ 0 ] !== "OFFSET" ) {
|
|
|
|
|
|
- - lines: all lines of the file. lines are consumed as we go along.
|
|
|
- - firstline: line containing the node type and name e.g. "JOINT hip"
|
|
|
- - list: collects a flat list of nodes
|
|
|
+ throw "Expected OFFSET, but got: " + tokens[ 0 ];
|
|
|
|
|
|
- returns: a BVH node including children
|
|
|
-*/
|
|
|
-THREE.BVHLoader.prototype._readNode = function( lines, firstline, list ) {
|
|
|
+ }
|
|
|
|
|
|
- var node = { name: "", type: "", frames: [] };
|
|
|
- list.push( node );
|
|
|
+ if ( tokens.length != 4 ) {
|
|
|
|
|
|
- // parse node type and name.
|
|
|
- var tokens = firstline.split( /[\s]+/ )
|
|
|
+ throw "OFFSET: Invalid number of values";
|
|
|
|
|
|
- if ( tokens[ 0 ].toUpperCase() === "END" && tokens[ 1 ].toUpperCase() === "SITE" ) {
|
|
|
+ }
|
|
|
|
|
|
- node.type = "ENDSITE";
|
|
|
- node.name = "ENDSITE"; // bvh end sites have no name
|
|
|
+ var offset = {
|
|
|
+ x: parseFloat( tokens[ 1 ] ),
|
|
|
+ y: parseFloat( tokens[ 2 ] ),
|
|
|
+ z: parseFloat( tokens[ 3 ] )
|
|
|
+ };
|
|
|
|
|
|
- }
|
|
|
- else {
|
|
|
+ if ( isNaN( offset.x ) || isNaN( offset.y ) || isNaN( offset.z ) ) {
|
|
|
|
|
|
- node.name = tokens[ 1 ];
|
|
|
- node.type = tokens[ 0 ].toUpperCase();
|
|
|
+ throw "OFFSET: Invalid values";
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- if ( this._nextLine( lines ) != "{" ) {
|
|
|
+ node.offset = offset;
|
|
|
|
|
|
- throw "Expected opening { after type & name";
|
|
|
+ // parse CHANNELS definitions
|
|
|
+ if ( node.type != "ENDSITE" ) {
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- // parse OFFSET
|
|
|
- tokens = this._nextLine( lines ).split( /[\s]+/ );
|
|
|
+ tokens = nextLine( lines ).split( /[\s]+/ );
|
|
|
|
|
|
- if ( tokens[ 0 ] !== "OFFSET" ) {
|
|
|
+ if ( tokens[ 0 ] != "CHANNELS" ) {
|
|
|
|
|
|
- throw "Expected OFFSET, but got: " + tokens[ 0 ];
|
|
|
+ throw "Expected CHANNELS definition";
|
|
|
|
|
|
- }
|
|
|
- if ( tokens.length != 4 ) {
|
|
|
+ }
|
|
|
|
|
|
- throw "OFFSET: Invalid number of values";
|
|
|
-
|
|
|
- }
|
|
|
+ var numChannels = parseInt( tokens[ 1 ] );
|
|
|
+ node.channels = tokens.splice( 2, numChannels );
|
|
|
+ node.children = [];
|
|
|
|
|
|
- var offset = {
|
|
|
- x: parseFloat( tokens[ 1 ] ),
|
|
|
- y: parseFloat( tokens[ 2 ] ),
|
|
|
- z: parseFloat( tokens[ 3 ] )
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
- if ( isNaN( offset.x ) || isNaN( offset.y ) || isNaN( offset.z ) ) {
|
|
|
+ // read children
|
|
|
+ while ( true ) {
|
|
|
|
|
|
- throw "OFFSET: Invalid values";
|
|
|
+ var line = nextLine( lines );
|
|
|
|
|
|
- }
|
|
|
+ if ( line === "}" ) {
|
|
|
|
|
|
- node.offset = offset;
|
|
|
+ return node;
|
|
|
|
|
|
- // parse CHANNELS definitions
|
|
|
- if ( node.type != "ENDSITE" ) {
|
|
|
+ } else {
|
|
|
|
|
|
- tokens = this._nextLine( lines ).split( /[\s]+/ );
|
|
|
+ node.children.push( readNode( lines, line, list ) );
|
|
|
|
|
|
- if ( tokens[ 0 ] != "CHANNELS" ) {
|
|
|
+ }
|
|
|
|
|
|
- throw "Expected CHANNELS definition";
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
- var numChannels = parseInt( tokens[ 1 ] );
|
|
|
- node.channels = tokens.splice( 2, numChannels );
|
|
|
- node.children = [];
|
|
|
-
|
|
|
- }
|
|
|
+ /*
|
|
|
+ recursively converts the internal bvh node structure to a THREE.Bone hierarchy
|
|
|
|
|
|
- // read children
|
|
|
- while ( true ) {
|
|
|
+ source: the bvh root node
|
|
|
+ list: pass an empty array, collects a flat list of all converted THREE.Bones
|
|
|
|
|
|
- var line = this._nextLine( lines );
|
|
|
+ returns the root THREE.Bone
|
|
|
+ */
|
|
|
+ function toTHREEBone( source, list ) {
|
|
|
|
|
|
- if ( line === "}" ) {
|
|
|
+ var bone = new THREE.Bone();
|
|
|
+ list.push( bone );
|
|
|
|
|
|
- return node;
|
|
|
+ bone.position.add( source.offset );
|
|
|
+ bone.name = source.name;
|
|
|
|
|
|
- } else {
|
|
|
+ if ( source.type != "ENDSITE" ) {
|
|
|
|
|
|
- node.children.push( this._readNode( lines, line, list ) );
|
|
|
-
|
|
|
- }
|
|
|
+ for ( var i = 0; i < source.children.length; ++ i ) {
|
|
|
|
|
|
- }
|
|
|
+ bone.add( toTHREEBone( source.children[ i ], list ) );
|
|
|
|
|
|
-}
|
|
|
+ }
|
|
|
|
|
|
-/*
|
|
|
- recursively converts the internal bvh node structure to a THREE.Bone hierarchy
|
|
|
+ }
|
|
|
|
|
|
- source: the bvh root node
|
|
|
- list: pass an empty array, collects a flat list of all converted THREE.Bones
|
|
|
+ return bone;
|
|
|
|
|
|
- returns the root THREE.Bone
|
|
|
-*/
|
|
|
-THREE.BVHLoader.prototype._toTHREEBone = function( source, list ) {
|
|
|
+ }
|
|
|
|
|
|
- var bone = new THREE.Bone();
|
|
|
- list.push( bone );
|
|
|
+ /*
|
|
|
+ builds a THREE.AnimationClip from the keyframe data saved in each bone.
|
|
|
|
|
|
- bone.position.add( source.offset );
|
|
|
- bone.name = source.name;
|
|
|
+ bone: bvh root node
|
|
|
|
|
|
- if ( source.type != "ENDSITE" ) {
|
|
|
+ returns: a THREE.AnimationClip containing position and quaternion tracks
|
|
|
+ */
|
|
|
+ function toTHREEAnimation( bones ) {
|
|
|
|
|
|
- for ( var i = 0; i < source.children.length; ++ i ) {
|
|
|
+ var tracks = [];
|
|
|
|
|
|
- bone.add( this._toTHREEBone( source.children[ i ], list ) );
|
|
|
+ // create a position and quaternion animation track for each node
|
|
|
+ for ( var i = 0; i < bones.length; ++ i ) {
|
|
|
|
|
|
- }
|
|
|
+ var bone = bones[ i ];
|
|
|
|
|
|
- }
|
|
|
+ if ( bone.type == "ENDSITE" )
|
|
|
+ continue;
|
|
|
|
|
|
- return bone;
|
|
|
+ // track data
|
|
|
+ var times = [];
|
|
|
+ var positions = [];
|
|
|
+ var rotations = [];
|
|
|
|
|
|
-}
|
|
|
+ for ( var j = 0; j < bone.frames.length; ++ j ) {
|
|
|
|
|
|
-/*
|
|
|
- builds a THREE.AnimationClip from the keyframe data saved in each bone.
|
|
|
+ var frame = bone.frames[ j ];
|
|
|
|
|
|
- bone: bvh root node
|
|
|
+ times.push( frame.time );
|
|
|
|
|
|
- returns: a THREE.AnimationClip containing position and quaternion tracks
|
|
|
-*/
|
|
|
-THREE.BVHLoader.prototype._toTHREEAnimation = function( bones ) {
|
|
|
+ // the animation system animates the position property,
|
|
|
+ // so we have to add the joint offset to all values
|
|
|
+ positions.push( frame.position.x + bone.offset.x );
|
|
|
+ positions.push( frame.position.y + bone.offset.y );
|
|
|
+ positions.push( frame.position.z + bone.offset.z );
|
|
|
|
|
|
- var tracks = [];
|
|
|
+ rotations.push( frame.rotation.x );
|
|
|
+ rotations.push( frame.rotation.y );
|
|
|
+ rotations.push( frame.rotation.z );
|
|
|
+ rotations.push( frame.rotation.w );
|
|
|
|
|
|
- // create a position and quaternion animation track for each node
|
|
|
- for ( var i = 0; i < bones.length; ++ i ) {
|
|
|
+ }
|
|
|
|
|
|
- var bone = bones[ i ];
|
|
|
+ if ( scope.animateBonePositions ) {
|
|
|
|
|
|
- if ( bone.type == "ENDSITE" )
|
|
|
- continue;
|
|
|
+ tracks.push( new THREE.VectorKeyframeTrack(
|
|
|
+ ".bones[" + bone.name + "].position", times, positions ) );
|
|
|
|
|
|
- // track data
|
|
|
- var times = [];
|
|
|
- var positions = [];
|
|
|
- var rotations = [];
|
|
|
+ }
|
|
|
|
|
|
- for ( var j = 0; j < bone.frames.length; ++ j ) {
|
|
|
+ if ( scope.animateBoneRotations ) {
|
|
|
|
|
|
- var frame = bone.frames[ j ];
|
|
|
+ tracks.push( new THREE.QuaternionKeyframeTrack(
|
|
|
+ ".bones[" + bone.name + "].quaternion", times, rotations ) );
|
|
|
|
|
|
- times.push( frame.time );
|
|
|
+ }
|
|
|
|
|
|
- // the animation system animates the position property,
|
|
|
- // so we have to add the joint offset to all values
|
|
|
- positions.push( frame.position.x + bone.offset.x );
|
|
|
- positions.push( frame.position.y + bone.offset.y );
|
|
|
- positions.push( frame.position.z + bone.offset.z );
|
|
|
+ }
|
|
|
|
|
|
- rotations.push( frame.rotation.x );
|
|
|
- rotations.push( frame.rotation.y );
|
|
|
- rotations.push( frame.rotation.z );
|
|
|
- rotations.push( frame.rotation.w );
|
|
|
+ return new THREE.AnimationClip( "animation", - 1, tracks );
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( this.animateBonePositions ) {
|
|
|
+ /*
|
|
|
+ returns the next non-empty line in lines
|
|
|
+ */
|
|
|
+ function nextLine( lines ) {
|
|
|
|
|
|
- tracks.push( new THREE.VectorKeyframeTrack(
|
|
|
- ".bones[" + bone.name + "].position", times, positions ) );
|
|
|
+ var line;
|
|
|
+ // skip empty lines
|
|
|
+ while ( ( line = lines.shift().trim() ).length === 0 ) { }
|
|
|
+ return line;
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( this.animateBoneRotations ) {
|
|
|
+ var scope = this;
|
|
|
+
|
|
|
+ // convert buffer to ASCII string
|
|
|
+ var text = "";
|
|
|
+ var raw = new Uint8Array( buffer );
|
|
|
+ for ( var i = 0; i < raw.length; ++ i ) {
|
|
|
|
|
|
- tracks.push( new THREE.QuaternionKeyframeTrack(
|
|
|
- ".bones[" + bone.name + "].quaternion", times, rotations ) );
|
|
|
+ text += String.fromCharCode( raw[ i ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
- }
|
|
|
+ var lines = text.split( /[\r\n]+/g );
|
|
|
|
|
|
- var clip = new THREE.AnimationClip( "animation", - 1, tracks );
|
|
|
+ var bones = readBvh( lines );
|
|
|
|
|
|
- return clip;
|
|
|
+ var threeBones = [];
|
|
|
+ toTHREEBone( bones[ 0 ], threeBones );
|
|
|
|
|
|
-}
|
|
|
+ var threeClip = toTHREEAnimation( bones );
|
|
|
|
|
|
-/*
|
|
|
- returns the next non-empty line in lines
|
|
|
-*/
|
|
|
-THREE.BVHLoader.prototype._nextLine = function( lines ) {
|
|
|
+ return {
|
|
|
+ skeleton: new THREE.Skeleton( threeBones ),
|
|
|
+ clip: threeClip
|
|
|
+ };
|
|
|
|
|
|
- var line;
|
|
|
- // skip empty lines
|
|
|
- while ( ( line = lines.shift().trim() ).length === 0 ) { }
|
|
|
- return line;
|
|
|
+ }
|
|
|
|
|
|
-}
|
|
|
+};
|