438 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			438 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								import {
							 | 
						||
| 
								 | 
							
									AnimationClip,
							 | 
						||
| 
								 | 
							
									Bone,
							 | 
						||
| 
								 | 
							
									FileLoader,
							 | 
						||
| 
								 | 
							
									Loader,
							 | 
						||
| 
								 | 
							
									Quaternion,
							 | 
						||
| 
								 | 
							
									QuaternionKeyframeTrack,
							 | 
						||
| 
								 | 
							
									Skeleton,
							 | 
						||
| 
								 | 
							
									Vector3,
							 | 
						||
| 
								 | 
							
									VectorKeyframeTrack
							 | 
						||
| 
								 | 
							
								} from 'three';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Description: reads BVH files and outputs a single Skeleton and an AnimationClip
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Currently only supports bvh files containing a single root.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class BVHLoader extends Loader {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									constructor( manager ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										super( manager );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.animateBonePositions = true;
							 | 
						||
| 
								 | 
							
										this.animateBoneRotations = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									load( url, onLoad, onProgress, onError ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const scope = this;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const loader = new FileLoader( scope.manager );
							 | 
						||
| 
								 | 
							
										loader.setPath( scope.path );
							 | 
						||
| 
								 | 
							
										loader.setRequestHeader( scope.requestHeader );
							 | 
						||
| 
								 | 
							
										loader.setWithCredentials( scope.withCredentials );
							 | 
						||
| 
								 | 
							
										loader.load( url, function ( text ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											try {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												onLoad( scope.parse( text ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											} catch ( e ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( onError ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													onError( e );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													console.error( e );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												scope.manager.itemError( url );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}, onProgress, onError );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									parse( text ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
											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 ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// read model structure
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( nextLine( lines ) !== 'HIERARCHY' ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												console.error( 'THREE.BVHLoader: HIERARCHY expected.' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const list = []; // collects flat array of all bones
							 | 
						||
| 
								 | 
							
											const root = readNode( lines, nextLine( lines ), list );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// read motion data
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( nextLine( lines ) !== 'MOTION' ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												console.error( 'THREE.BVHLoader: MOTION expected.' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// number of frames
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											let tokens = nextLine( lines ).split( /[\s]+/ );
							 | 
						||
| 
								 | 
							
											const numFrames = parseInt( tokens[ 1 ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( isNaN( numFrames ) ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												console.error( 'THREE.BVHLoader: Failed to read number of frames.' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// frame time
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											tokens = nextLine( lines ).split( /[\s]+/ );
							 | 
						||
| 
								 | 
							
											const frameTime = parseFloat( tokens[ 2 ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( isNaN( frameTime ) ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												console.error( 'THREE.BVHLoader: Failed to read frame time.' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// read frame data line by line
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for ( let i = 0; i < numFrames; i ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												tokens = nextLine( lines ).split( /[\s]+/ );
							 | 
						||
| 
								 | 
							
												readFrameData( tokens, i * frameTime, root );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											return list;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
											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.
							 | 
						||
| 
								 | 
							
										*/
							 | 
						||
| 
								 | 
							
										function readFrameData( data, frameTime, bone ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// end sites have no motion data
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( bone.type === 'ENDSITE' ) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// add keyframe
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const keyframe = {
							 | 
						||
| 
								 | 
							
												time: frameTime,
							 | 
						||
| 
								 | 
							
												position: new Vector3(),
							 | 
						||
| 
								 | 
							
												rotation: new Quaternion()
							 | 
						||
| 
								 | 
							
											};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											bone.frames.push( keyframe );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const quat = new Quaternion();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const vx = new Vector3( 1, 0, 0 );
							 | 
						||
| 
								 | 
							
											const vy = new Vector3( 0, 1, 0 );
							 | 
						||
| 
								 | 
							
											const vz = new Vector3( 0, 0, 1 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// parse values for each channel in node
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for ( let 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:
							 | 
						||
| 
								 | 
							
														console.warn( 'THREE.BVHLoader: Invalid channel type.' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// parse child nodes
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for ( let i = 0; i < bone.children.length; i ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												readFrameData( data, frameTime, bone.children[ i ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
										 Recursively parses the HIERACHY section of the BVH file
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 - 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
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 returns: a BVH node including children
							 | 
						||
| 
								 | 
							
										*/
							 | 
						||
| 
								 | 
							
										function readNode( lines, firstline, list ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const node = { name: '', type: '', frames: [] };
							 | 
						||
| 
								 | 
							
											list.push( node );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// parse node type and name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											let tokens = firstline.split( /[\s]+/ );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( tokens[ 0 ].toUpperCase() === 'END' && tokens[ 1 ].toUpperCase() === 'SITE' ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												node.type = 'ENDSITE';
							 | 
						||
| 
								 | 
							
												node.name = 'ENDSITE'; // bvh end sites have no name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												node.name = tokens[ 1 ];
							 | 
						||
| 
								 | 
							
												node.type = tokens[ 0 ].toUpperCase();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( nextLine( lines ) !== '{' ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												console.error( 'THREE.BVHLoader: Expected opening { after type & name' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// parse OFFSET
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											tokens = nextLine( lines ).split( /[\s]+/ );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( tokens[ 0 ] !== 'OFFSET' ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												console.error( 'THREE.BVHLoader: Expected OFFSET but got: ' + tokens[ 0 ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( tokens.length !== 4 ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												console.error( 'THREE.BVHLoader: Invalid number of values for OFFSET.' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const offset = new Vector3(
							 | 
						||
| 
								 | 
							
												parseFloat( tokens[ 1 ] ),
							 | 
						||
| 
								 | 
							
												parseFloat( tokens[ 2 ] ),
							 | 
						||
| 
								 | 
							
												parseFloat( tokens[ 3 ] )
							 | 
						||
| 
								 | 
							
											);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( isNaN( offset.x ) || isNaN( offset.y ) || isNaN( offset.z ) ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												console.error( 'THREE.BVHLoader: Invalid values of OFFSET.' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											node.offset = offset;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// parse CHANNELS definitions
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( node.type !== 'ENDSITE' ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												tokens = nextLine( lines ).split( /[\s]+/ );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( tokens[ 0 ] !== 'CHANNELS' ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													console.error( 'THREE.BVHLoader: Expected CHANNELS definition.' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												const numChannels = parseInt( tokens[ 1 ] );
							 | 
						||
| 
								 | 
							
												node.channels = tokens.splice( 2, numChannels );
							 | 
						||
| 
								 | 
							
												node.children = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// read children
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											while ( true ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												const line = nextLine( lines );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( line === '}' ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													return node;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													node.children.push( readNode( lines, line, list ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
											recursively converts the internal bvh node structure to a Bone hierarchy
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											source: the bvh root node
							 | 
						||
| 
								 | 
							
											list: pass an empty array, collects a flat list of all converted THREE.Bones
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											returns the root Bone
							 | 
						||
| 
								 | 
							
										*/
							 | 
						||
| 
								 | 
							
										function toTHREEBone( source, list ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const bone = new Bone();
							 | 
						||
| 
								 | 
							
											list.push( bone );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											bone.position.add( source.offset );
							 | 
						||
| 
								 | 
							
											bone.name = source.name;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( source.type !== 'ENDSITE' ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												for ( let i = 0; i < source.children.length; i ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													bone.add( toTHREEBone( source.children[ i ], list ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											return bone;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
											builds a AnimationClip from the keyframe data saved in each bone.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											bone: bvh root node
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											returns: a AnimationClip containing position and quaternion tracks
							 | 
						||
| 
								 | 
							
										*/
							 | 
						||
| 
								 | 
							
										function toTHREEAnimation( bones ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const tracks = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// create a position and quaternion animation track for each node
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for ( let i = 0; i < bones.length; i ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												const bone = bones[ i ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( bone.type === 'ENDSITE' )
							 | 
						||
| 
								 | 
							
													continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												// track data
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												const times = [];
							 | 
						||
| 
								 | 
							
												const positions = [];
							 | 
						||
| 
								 | 
							
												const rotations = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												for ( let j = 0; j < bone.frames.length; j ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													const frame = bone.frames[ j ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													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 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( scope.animateBonePositions ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													tracks.push( new VectorKeyframeTrack( bone.name + '.position', times, positions ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( scope.animateBoneRotations ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													tracks.push( new QuaternionKeyframeTrack( bone.name + '.quaternion', times, rotations ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											return new AnimationClip( 'animation', - 1, tracks );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
											returns the next non-empty line in lines
							 | 
						||
| 
								 | 
							
										*/
							 | 
						||
| 
								 | 
							
										function nextLine( lines ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											let line;
							 | 
						||
| 
								 | 
							
											// skip empty lines
							 | 
						||
| 
								 | 
							
											while ( ( line = lines.shift().trim() ).length === 0 ) { }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											return line;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const scope = this;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const lines = text.split( /[\r\n]+/g );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const bones = readBvh( lines );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const threeBones = [];
							 | 
						||
| 
								 | 
							
										toTHREEBone( bones[ 0 ], threeBones );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const threeClip = toTHREEAnimation( bones );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return {
							 | 
						||
| 
								 | 
							
											skeleton: new Skeleton( threeBones ),
							 | 
						||
| 
								 | 
							
											clip: threeClip
							 | 
						||
| 
								 | 
							
										};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export { BVHLoader };
							 |