提交 d5e3bdd2 编写于 作者: M Mr.doob

BVHLoader: Clean up.

上级 c87ca5d0
......@@ -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;
}
}
};
......@@ -27,25 +27,24 @@
</head>
<body>
<div id="info">
<a href="http://threejs.org" target="_blank">three.js</a> - BVH Loader -
animation from <a href="http://mocap.cs.cmu.edu/">http://mocap.cs.cmu.edu/</a>
<a href="http://threejs.org" target="_blank">three.js</a> - BVH Loader -
animation from <a href="http://mocap.cs.cmu.edu/">http://mocap.cs.cmu.edu/</a>
</div>
<script src="../build/three.js"></script>
<script src="js/controls/TrackballControls.js"></script>
<script src="js/controls/OrbitControls.js"></script>
<script src="js/loaders/BVHLoader.js"></script>
<script>
var clock = new THREE.Clock();
var mixer, skeletonHelper, camera, controls, scene, renderer;
var camera, controls, scene, renderer;
var mixer, skeletonHelper;
var mesh;
init();
animate();
var loader = new THREE.BVHLoader();
loader.load( "models/bvh/pirouette.bvh", function( result ) {
loader.load( "models/bvh/pirouette.bvh", function ( result ) {
skeletonHelper = new THREE.SkeletonHelper( result.skeleton.bones[ 0 ] );
skeletonHelper.skeleton = result.skeleton; // allow animation mixer to bind to SkeletonHelper directly
......@@ -55,29 +54,23 @@
scene.add( skeletonHelper );
scene.add( boneContainer );
// play animation
mixer = new THREE.AnimationMixer( skeletonHelper );
mixer.clipAction( result.clip ).setEffectiveWeight( 1.0 ).play()
mixer.clipAction( result.clip ).setEffectiveWeight( 1.0 ).play();
} );
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 0, 200, 400 );
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 4.5;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls = new THREE.OrbitControls( camera );
scene = new THREE.Scene();
camera.position.z = 600;
camera.position.y = 200;
scene.add( new THREE.GridHelper( 200, 100 ) );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
......@@ -88,6 +81,7 @@
document.body.appendChild( renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
......@@ -97,7 +91,6 @@
renderer.setSize( window.innerWidth, window.innerHeight );
controls.handleResize();
}
function animate() {
......@@ -107,15 +100,12 @@
var delta = clock.getDelta();
if ( mixer )
mixer.update( delta );
if ( skeletonHelper )
skeletonHelper.update();
if ( mixer ) mixer.update( delta );
if ( skeletonHelper ) skeletonHelper.update();
renderer.render( scene, camera );
}
}
</script>
</body>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册