diff --git a/examples/js/loaders/BVHLoader.js b/examples/js/loaders/BVHLoader.js index 1f780f54136fbba09a1f9d2165ce328b27e99bac..3957159e9ca5b9c45aa99ef36fe037d13355da11 100644 --- a/examples/js/loaders/BVHLoader.js +++ b/examples/js/loaders/BVHLoader.js @@ -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; + } -} +}; diff --git a/examples/webgl_loader_bvh.html b/examples/webgl_loader_bvh.html index 091f240a9897575f81a846cc9bcbfdd4221e47da..772d8af306517d1dfc1dda6e8cbbab7c77d8a6e3 100644 --- a/examples/webgl_loader_bvh.html +++ b/examples/webgl_loader_bvh.html @@ -27,25 +27,24 @@
- three.js - BVH Loader - - animation from http://mocap.cs.cmu.edu/ + three.js - BVH Loader - + animation from http://mocap.cs.cmu.edu/
- +