Преглед на файлове

Merge pull request #14574 from sunag/dev-sea3d-bvh

BVH Animation to SEA3D Character and SkeletonUtils
Mr.doob преди 7 години
родител
ревизия
e3043eeea7

+ 2 - 0
examples/files.js

@@ -114,6 +114,8 @@ var files = {
 		"webgl_loader_ply",
 		"webgl_loader_prwm",
 		"webgl_loader_sea3d",
+		"webgl_loader_sea3d_bvh",
+		"webgl_loader_sea3d_bvh_retarget",
 		"webgl_loader_sea3d_hierarchy",
 		"webgl_loader_sea3d_keyframe",
 		"webgl_loader_sea3d_morph",

+ 534 - 0
examples/js/utils/SkeletonUtils.js

@@ -0,0 +1,534 @@
+/**
+ * @author sunag / http://www.sunag.com.br
+ */
+
+'use strict';
+
+THREE.SkeletonUtils = {
+
+	retarget: function () {
+
+		var pos = new THREE.Vector3(),
+			quat = new THREE.Quaternion(),
+			scale = new THREE.Vector3(),
+			hipPosition = new THREE.Vector3(),
+			bindBoneMatrix = new THREE.Matrix4(),
+			relativeMatrix = new THREE.Matrix4(),
+			globalMatrix = new THREE.Matrix4();
+
+		return function ( target, source, options ) {
+
+			options = options || {};
+			options.preserveMatrix = options.preserveMatrix !== undefined ? options.preserveMatrix : true;
+			options.preservePosition = options.preservePosition !== undefined ? options.preservePosition : true;
+			options.preserveHipPosition = options.preserveHipPosition !== undefined ? options.preserveHipPosition : false;
+			options.useTargetMatrix = options.useTargetMatrix !== undefined ? options.useTargetMatrix : false;
+			options.hip = options.hip !== undefined ? options.hip : "hip";
+			options.names = options.names || {};
+
+			var sourceBones = source.isObject3D ? source.skeleton.bones : this.getBones( source ),
+				bones = target.isObject3D ? target.skeleton.bones : this.getBones( target ),
+				bindBones,
+				bone, name, boneTo,
+				bonesPosition, i;
+
+			// reset bones
+
+			if ( target.isObject3D ) {
+
+				target.skeleton.pose();
+
+			} else {
+
+				options.useTargetMatrix = true;
+				options.preserveMatrix = false;
+
+			}
+
+			if ( options.preservePosition ) {
+
+				bonesPosition = [];
+
+				for ( i = 0; i < bones.length; i ++ ) {
+
+					bonesPosition.push( bones[ i ].position.clone() );
+
+				}
+
+			}
+
+			if ( options.preserveMatrix ) {
+
+				// reset matrix
+
+				target.updateMatrixWorld();
+
+				target.matrixWorld.identity();
+
+				// reset children matrix
+
+				for ( i = 0; i < target.children.length; ++ i ) {
+
+					target.children[ i ].updateMatrixWorld( true );
+
+				}
+
+			}
+
+			if ( options.offsets ) {
+
+				bindBones = [];
+
+				for ( i = 0; i < bones.length; ++ i ) {
+
+					bone = bones[ i ];
+					name = options.names[ bone.name ] || bone.name;
+
+					if ( options.offsets && options.offsets[ name ] ) {
+
+						bone.matrix.multiply( options.offsets[ name ] );
+
+						bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
+
+						bone.updateMatrixWorld();
+
+					}
+					
+					bindBones.push( bone.matrixWorld.clone() );
+
+				}
+
+			}
+
+			for ( i = 0; i < bones.length; ++ i ) {
+
+				bone = bones[ i ];
+				name = options.names[ bone.name ] || bone.name;
+
+				boneTo = this.getBoneByName( name, sourceBones );
+
+				globalMatrix.copy( bone.matrixWorld );
+
+				if ( boneTo ) {
+
+					boneTo.updateMatrixWorld();
+					
+					if ( options.useTargetMatrix ) {
+
+						relativeMatrix.copy( boneTo.matrixWorld );
+
+					} else {
+
+						relativeMatrix.getInverse( target.matrixWorld );
+						relativeMatrix.multiply( boneTo.matrixWorld );
+
+					}
+
+					// ignore scale to extract rotation
+
+					scale.setFromMatrixScale( relativeMatrix );
+					relativeMatrix.scale( scale.set( 1 / scale.x, 1 / scale.y, 1 / scale.z ) );
+
+					// apply to global matrix
+
+					globalMatrix.makeRotationFromQuaternion( quat.setFromRotationMatrix( relativeMatrix ) );
+
+					if ( target.isObject3D ) {
+
+						var boneIndex = bones.indexOf( bone ),
+							wBindMatrix = bindBones ? bindBones[ boneIndex ] : bindBoneMatrix.getInverse( target.skeleton.boneInverses[ boneIndex ] );
+
+						globalMatrix.multiply( wBindMatrix );
+
+					}
+
+					globalMatrix.copyPosition( relativeMatrix );
+
+				}
+
+				if ( bone.parent && bone.parent.isBone ) {
+
+					bone.matrix.getInverse( bone.parent.matrixWorld );
+					bone.matrix.multiply( globalMatrix );
+
+				} else {
+
+					bone.matrix.copy( globalMatrix );
+
+				}
+
+				if ( options.preserveHipPosition && name === options.hip ) {
+					
+					bone.matrix.setPosition( pos.set( 0, bone.position.y, 0 ) );
+					
+				}
+				
+				bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
+
+				bone.updateMatrixWorld();
+
+			}
+
+			if ( options.preservePosition ) {
+
+				for ( i = 0; i < bones.length; ++ i ) {
+
+					bone = bones[ i ];
+					name = options.names[ bone.name ] || bone.name;
+
+					if ( name !== options.hip ) {
+
+						bone.position.copy( bonesPosition[ i ] );
+
+					}
+
+				}
+
+			}
+
+			if ( options.preserveMatrix ) {
+
+				// restore matrix
+
+				target.updateMatrixWorld( true );
+
+			}
+
+		};
+
+	}(),
+
+	retargetClip: function ( target, source, clip, options ) {
+
+		options = options || {};
+		options.useFirstFramePosition = options.useFirstFramePosition !== undefined ? options.useFirstFramePosition : false;
+		options.fps = options.fps !== undefined ? options.fps : 30;
+		options.names = options.names || [];
+
+		if ( ! source.isObject3D ) {
+
+			source = this.getHelperFromSkeleton( source );
+
+		}
+
+		var numFrames = Math.round( clip.duration * ( options.fps / 1000 ) * 1000 ),
+			delta = 1 / options.fps,
+			convertedTracks = [],
+			mixer = new THREE.AnimationMixer( source ),
+			bones = this.getBones( target.skeleton ),
+			boneDatas = [],
+			positionOffset,
+			bone, boneTo, boneData, 
+			name, i, j;
+
+		mixer.clipAction( clip ).play();
+		mixer.update( 0 );
+
+		source.updateMatrixWorld();
+
+		for ( i = 0; i < numFrames; ++ i ) {
+
+			var time = i * delta;
+
+			this.retarget( target, source, options );
+
+			for ( j = 0; j < bones.length; ++ j ) {
+
+				name = options.names[ bones[ j ].name ] || bones[ j ].name;
+				
+				boneTo = this.getBoneByName( name, source.skeleton );
+
+				if ( boneTo ) {
+
+					bone = bones[ j ];
+					boneData = boneDatas[ j ] = boneDatas[ j ] || { bone: bone };
+
+					if ( options.hip === name ) {
+
+						if ( ! boneData.pos ) {
+
+							boneData.pos = {
+								times: new Float32Array( numFrames ),
+								values: new Float32Array( numFrames * 3 )
+							};
+
+						}
+
+						if ( options.useFirstFramePosition ) {
+
+							if ( i === 0 ) {
+
+								positionOffset = bone.position.clone();
+
+							}
+
+							bone.position.sub( positionOffset );
+
+						}
+
+						boneData.pos.times[ i ] = time;
+
+						bone.position.toArray( boneData.pos.values, i * 3 );
+
+					}
+
+					if ( ! boneData.quat ) {
+
+						boneData.quat = {
+							times: new Float32Array( numFrames ),
+							values: new Float32Array( numFrames * 4 )
+						};
+
+					}
+
+					boneData.quat.times[ i ] = time;
+
+					bone.quaternion.toArray( boneData.quat.values, i * 4 );
+
+				}
+
+			}
+
+			mixer.update( delta );
+
+			source.updateMatrixWorld();
+
+		}
+
+		for ( i = 0; i < boneDatas.length; ++ i ) {
+
+			boneData = boneDatas[ i ];
+
+			if ( boneData ) {
+
+				if ( boneData.pos ) {
+
+					convertedTracks.push( new THREE.VectorKeyframeTrack(
+						".bones[" + boneData.bone.name + "].position",
+						boneData.pos.times,
+						boneData.pos.values
+					) );
+
+				}
+
+				convertedTracks.push( new THREE.QuaternionKeyframeTrack(
+					".bones[" + boneData.bone.name + "].quaternion",
+					boneData.quat.times,
+					boneData.quat.values
+				) );
+
+			}
+
+		}
+
+		mixer.uncacheAction( clip );
+
+		return new THREE.AnimationClip( clip.name, - 1, convertedTracks );
+
+	},
+	
+	getHelperFromSkeleton: function( skeleton ) {
+		
+		var source = new THREE.SkeletonHelper( skeleton.bones[ 0 ] );
+		source.skeleton = skeleton;
+		
+		return source;
+		
+	},
+	
+	getSkeletonOffsets: function () {
+
+		var targetParentPos = new THREE.Vector3(),
+			targetPos = new THREE.Vector3(),
+			sourceParentPos = new THREE.Vector3(),
+			sourcePos = new THREE.Vector3(),
+			targetDir = new THREE.Vector2(),
+			sourceDir = new THREE.Vector2();
+
+		return function ( target, source, options ) {
+
+			options = options || {};
+			options.hip = options.hip !== undefined ? options.hip : "hip";
+			options.names = options.names || {};
+
+			if ( ! source.isObject3D ) {
+
+				source = this.getHelperFromSkeleton( source );
+
+			}
+			
+			var nameKeys = Object.keys( options.names ),
+				nameValues = Object.values( options.names ),
+				sourceBones = source.isObject3D ? source.skeleton.bones : this.getBones( source ),
+				bones = target.isObject3D ? target.skeleton.bones : this.getBones( target ),
+				offsets = [],
+				bone, boneTo, 
+				name, i;
+			
+			target.skeleton.pose();
+			
+			for ( i = 0; i < bones.length; ++ i ) {
+
+				bone = bones[ i ];
+				name = options.names[ bone.name ] || bone.name;
+
+				boneTo = this.getBoneByName( name, sourceBones );
+				
+				if ( boneTo && name !== options.hip ) {
+				
+					var boneParent = this.getNearestBone( bone.parent, nameKeys ),
+						boneToParent = this.getNearestBone( boneTo.parent, nameValues );
+				
+					boneParent.updateMatrixWorld();
+					boneToParent.updateMatrixWorld();
+				
+					targetParentPos.setFromMatrixPosition( boneParent.matrixWorld  );
+					targetPos.setFromMatrixPosition( bone.matrixWorld );
+					
+					sourceParentPos.setFromMatrixPosition( boneToParent.matrixWorld );
+					sourcePos.setFromMatrixPosition( boneTo.matrixWorld );
+					
+					targetDir.subVectors( 
+						new THREE.Vector2( targetPos.x, targetPos.y ),
+						new THREE.Vector2( targetParentPos.x, targetParentPos.y ), 
+					).normalize();
+					
+					sourceDir.subVectors( 
+						new THREE.Vector2( sourcePos.x, sourcePos.y ),
+						new THREE.Vector2( sourceParentPos.x, sourceParentPos.y ),
+					).normalize();
+					
+					var laterialAngle = targetDir.angle() - sourceDir.angle();
+					
+					var offset = new THREE.Matrix4().makeRotationFromEuler(
+						new THREE.Euler(
+							0,
+							0,
+							laterialAngle
+						)
+					);
+					
+					bone.matrix.multiply( offset );
+
+					bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
+
+					bone.updateMatrixWorld();
+					
+					offsets[ name ] = offset;
+					
+				}
+				
+			}
+			
+			return offsets;
+			
+		}
+
+	}(),
+
+	renameBones: function ( skeleton, names ) {
+
+		var bones = this.getBones( skeleton );
+
+		for ( var i = 0; i < bones.length; ++ i ) {
+
+			var bone = bones[ i ];
+
+			if ( names[ bone.name ] ) {
+
+				bone.name = names[ bone.name ];
+
+			}
+
+		}
+
+		return this;
+
+	},
+
+	getBones: function ( skeleton ) {
+
+		return Array.isArray( skeleton ) ? skeleton : skeleton.bones;
+
+	},
+
+	getBoneByName: function ( name, skeleton ) {
+
+		for ( var i = 0, bones = this.getBones( skeleton ); i < bones.length; i ++ ) {
+
+			if ( name === bones[ i ].name )
+				
+				return bones[ i ];
+
+		}
+
+	},
+	
+	getNearestBone: function ( bone, names ) {
+
+		while( bone.isBone ) {
+			
+			if ( names.indexOf( bone.name ) !== -1 ) {
+				
+				return bone;
+				
+			}
+			
+			bone = bone.parent;
+			
+		}
+
+	},
+
+	findBoneTrackData: function ( name, tracks ) {
+
+		var regexp = /\[(.*)\]\.(.*)/,
+			result = { name: name };
+
+		for ( var i = 0; i < tracks.length; ++ i ) {
+
+			// 1 is track name
+			// 2 is track type
+			var trackData = regexp.exec( tracks[ i ].name );
+
+			if ( trackData && name === trackData[ 1 ] ) {
+
+				result[ trackData[ 2 ] ] = i;
+
+			}
+
+		}
+
+		return result;
+
+	},
+
+	getEqualsBonesNames: function ( skeleton, targetSkeleton ) {
+
+		var sourceBones = this.getBones( skeleton ),
+			targetBones = this.getBones( targetSkeleton ),
+			bones = [];
+
+		search : for ( var i = 0; i < sourceBones.length; i ++ ) {
+
+			var boneName = sourceBones[ i ].name;
+
+			for ( var j = 0; j < targetBones.length; j ++ ) {
+
+				if ( boneName === targetBones[ j ].name ) {
+
+					bones.push( boneName );
+
+					continue search;
+
+				}
+
+			}
+
+		}
+
+		return bones;
+
+	}
+
+};

+ 310 - 0
examples/webgl_loader_sea3d_bvh.html

@@ -0,0 +1,310 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - sea3d / bvh</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				font-family: Monospace;
+				background-color: #000;
+				margin: 0px;
+				overflow: hidden;
+			}
+
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				z-index: 100;
+				display:block;
+
+			}
+
+			a { color: white }
+		</style>
+	</head>
+	<body>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank" rel="noopener">Three.JS</a> - Exported by <a href="https://github.com/sunag/sea3d" style="color:#FFFFFF" target="_blank" rel="noopener">SEA3D Exporter</a>. Asset by <a href="http://www.turbosquid.com/3d-models/soccer-player-max-free/307330" style="color:#FFFFFF" target="_blank" rel="noopener">Trivision</a>
+			<br/>
+			<br/>Runtime convertion of BVH Animation to SEA3D Skeleton Animation
+		</div>
+
+		<script src="../build/three.js"></script>
+
+		<script src="js/controls/OrbitControls.js"></script>
+
+		<script src="js/postprocessing/EffectComposer.js"></script>
+		<script src="js/postprocessing/RenderPass.js"></script>
+		<script src="js/postprocessing/ShaderPass.js"></script>
+		<script src="js/postprocessing/MaskPass.js"></script>
+		<script src="js/shaders/CopyShader.js"></script>
+		<script src="js/shaders/ColorCorrectionShader.js"></script>
+		<script src="js/shaders/VignetteShader.js"></script>
+
+		<script src="js/loaders/sea3d/SEA3D.js"></script>
+		<script src="js/loaders/sea3d/SEA3DLZMA.js"></script>
+		<script src="js/loaders/sea3d/SEA3DLoader.js"></script>
+
+		<script src="js/loaders/BVHLoader.js"></script>
+		<script src="js/utils/SkeletonUtils.js"></script>
+		
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			console.log( "Visit https://github.com/sunag/sea3d to all codes and builds under development." );
+
+			var container, stats;
+
+			var camera, scene, renderer, composer, player, hat;
+
+			var loader;
+
+			var bvhSkeletonHelper, bvhMixer;
+
+			// Initialize Three.JS
+
+			init();
+
+			//
+			// SEA3D Loader
+			//
+
+			loader = new THREE.SEA3D( {
+
+				autoPlay: true, // Auto play animations
+				container: scene, // Container to add models
+				multiplier: .6 // Light multiplier
+
+			} );
+
+			loader.onComplete = function ( ) {
+
+				// Get the first camera from SEA3D Studio
+				// use loader.get... to get others objects
+
+				var cam = loader.cameras[ 0 ];
+				camera.position.copy( cam.position );
+				camera.rotation.copy( cam.rotation );
+
+				new THREE.OrbitControls( camera );
+
+				// get meshes
+				player = loader.getMesh( "Player" );
+				hat = loader.getMesh( "Hat" );
+
+				hat.visible = false;
+
+				loadBVH();
+
+				animate();
+
+			};
+
+			loader.load( './models/sea3d/skin.tjs.sea' );
+
+			//
+
+			function bvhToSEA3D( result ) {
+
+				var options = {
+					useFirstFramePosition: true,
+					preserveHipPosition: false,
+					hip: "hip",
+					// left is SEA3D bone names and right BVH bone names
+					names: {
+						"Base HumanPelvis": "hip",
+						"Base HumanSpine3": "abdomen",
+						"Base HumanRibcage": "chest",
+						"Base HumanHead": "head",
+
+						"Base HumanRUpperarm": "rShldr",
+						"Base HumanRForearm1": "rForeArm",
+						"Base HumanRPalm": "rHand",
+
+						"Base HumanLUpperarm": "lShldr",
+						"Base HumanLForearm1": "lForeArm",
+						"Base HumanLPalm": "lHand",
+
+						"Base HumanRThigh": "rThigh",
+						"Base HumanRCalf1": "rShin",
+						"Base HumanRFoot": "rFoot",
+
+						"Base HumanLThigh": "lThigh",
+						"Base HumanLCalf1": "lShin",
+						"Base HumanLFoot": "lFoot"
+					},
+
+				};
+
+				// Automatic offset: get offsets when it is in T-Pose
+				options.offsets = THREE.SkeletonUtils.getSkeletonOffsets( player, bvhSkeletonHelper, options );
+
+				/*
+				// Manual offsets: compensates the difference in skeletons ( T-Pose )
+				options.offsets = {
+					"lShldr": new THREE.Matrix4().makeRotationFromEuler(
+						new THREE.Euler(
+							0,
+							THREE.Math.degToRad( - 45 ),
+							THREE.Math.degToRad( - 80 )
+						)
+					),
+					"rShldr": new THREE.Matrix4().makeRotationFromEuler(
+						new THREE.Euler(
+							0,
+							THREE.Math.degToRad( 45 ),
+							THREE.Math.degToRad( 80 )
+						)
+					),
+					"lFoot": new THREE.Matrix4().makeRotationFromEuler(
+						new THREE.Euler(
+							0,
+							THREE.Math.degToRad( 15 ),
+							0
+						)
+					),
+					"rFoot": new THREE.Matrix4().makeRotationFromEuler(
+						new THREE.Euler(
+							0,
+							THREE.Math.degToRad( 15 ),
+							0
+						)
+					)
+				};
+				*/
+
+				var clip = THREE.SkeletonUtils.retargetClip( player, result.skeleton, result.clip, options );
+
+				clip.name = "dance";
+
+				clip = THREE.SEA3D.AnimationClip.fromClip( clip );
+
+				player.addAnimation( new THREE.SEA3D.Animation( clip ) );
+
+				player.play( "dance" );
+
+
+			}
+
+			function loadBVH() {
+
+				var loader = new THREE.BVHLoader();
+				loader.load( "models/bvh/pirouette.bvh", function ( result ) {
+
+					bvhSkeletonHelper = new THREE.SkeletonHelper( result.skeleton.bones[ 0 ] );
+					bvhSkeletonHelper.skeleton = result.skeleton; // allow animation mixer to bind to SkeletonHelper directly
+
+					var boneContainer = new THREE.Group();
+					boneContainer.add( result.skeleton.bones[ 0 ] );
+					boneContainer.position.y = - 100;
+
+					scene.add( bvhSkeletonHelper );
+					scene.add( boneContainer );
+
+					// play animation
+					bvhMixer = new THREE.AnimationMixer( bvhSkeletonHelper );
+					bvhMixer.clipAction( result.clip ).setEffectiveWeight( 1.0 ).play();
+
+					bvhToSEA3D( result );
+
+				} );
+
+			}
+
+			function init() {
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0x333333 );
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 20000 );
+				camera.position.set( 1000, - 300, 1000 );
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				// post-processing
+
+				composer = new THREE.EffectComposer( renderer );
+
+				var renderPass = new THREE.RenderPass( scene, camera );
+				var copyPass = new THREE.ShaderPass( THREE.CopyShader );
+				composer.addPass( renderPass );
+
+				var vh = 1.4, vl = 1.2;
+
+				var colorCorrectionPass = new THREE.ShaderPass( THREE.ColorCorrectionShader );
+				colorCorrectionPass.uniforms[ "powRGB" ].value = new THREE.Vector3( vh, vh, vh );
+				colorCorrectionPass.uniforms[ "mulRGB" ].value = new THREE.Vector3( vl, vl, vl );
+				composer.addPass( colorCorrectionPass );
+
+				var vignettePass = new THREE.ShaderPass( THREE.VignetteShader );
+				vignettePass.uniforms[ "darkness" ].value = 1.0;
+				composer.addPass( vignettePass );
+
+				composer.addPass( copyPass );
+				copyPass.renderToScreen = true;
+
+				// events
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				composer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			//
+
+			var clock = new THREE.Clock();
+
+			function animate() {
+
+				var delta = clock.getDelta();
+
+				requestAnimationFrame( animate );
+
+				// Update SEA3D Animations
+				THREE.SEA3D.AnimationHandler.update( delta );
+
+				if ( bvhMixer ) bvhMixer.update( delta );
+
+				render( delta );
+
+				stats.update();
+
+			}
+
+			function render( dlt ) {
+
+				//renderer.render( scene, camera );
+				composer.render( dlt );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 265 - 0
examples/webgl_loader_sea3d_bvh_retarget.html

@@ -0,0 +1,265 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - sea3d / bvh / retarget</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				font-family: Monospace;
+				background-color: #000;
+				margin: 0px;
+				overflow: hidden;
+			}
+
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				z-index: 100;
+				display:block;
+
+			}
+
+			a { color: white }
+		</style>
+	</head>
+	<body>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank" rel="noopener">Three.JS</a> - Exported by <a href="https://github.com/sunag/sea3d" style="color:#FFFFFF" target="_blank" rel="noopener">SEA3D Exporter</a>. Asset by <a href="http://www.turbosquid.com/3d-models/soccer-player-max-free/307330" style="color:#FFFFFF" target="_blank" rel="noopener">Trivision</a>
+			<br/>
+			<br/>Runtime retarget of BVH Animation to SEA3D Skeleton
+		</div>
+
+		<script src="../build/three.js"></script>
+
+		<script src="js/controls/OrbitControls.js"></script>
+
+		<script src="js/postprocessing/EffectComposer.js"></script>
+		<script src="js/postprocessing/RenderPass.js"></script>
+		<script src="js/postprocessing/ShaderPass.js"></script>
+		<script src="js/postprocessing/MaskPass.js"></script>
+		<script src="js/shaders/CopyShader.js"></script>
+		<script src="js/shaders/ColorCorrectionShader.js"></script>
+		<script src="js/shaders/VignetteShader.js"></script>
+
+		<script src="js/loaders/sea3d/SEA3D.js"></script>
+		<script src="js/loaders/sea3d/SEA3DLZMA.js"></script>
+		<script src="js/loaders/sea3d/SEA3DLoader.js"></script>
+
+		<script src="js/loaders/BVHLoader.js"></script>
+		<script src="js/utils/SkeletonUtils.js"></script>
+		
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			console.log( "Visit https://github.com/sunag/sea3d to all codes and builds under development." );
+
+			var container, stats;
+
+			var camera, scene, renderer, composer, player, hat;
+
+			var loader, options;
+
+			var bvhSkeletonHelper, bvhMixer;
+
+			// Initialize Three.JS
+
+			init();
+
+			//
+			// SEA3D Loader
+			//
+
+			loader = new THREE.SEA3D( {
+
+				autoPlay: false, // Auto play animations
+				container: scene, // Container to add models
+				multiplier: .6 // Light multiplier
+
+			} );
+
+			loader.onComplete = function ( ) {
+
+				// Get the first camera from SEA3D Studio
+				// use loader.get... to get others objects
+
+				var cam = loader.cameras[ 0 ];
+				camera.position.copy( cam.position );
+				camera.rotation.copy( cam.rotation );
+
+				new THREE.OrbitControls( camera );
+
+				// get meshes
+				player = loader.getMesh( "Player" );
+				hat = loader.getMesh( "Hat" );
+
+				hat.visible = false;
+
+				loadBVH();
+
+				animate();
+
+			};
+
+			loader.load( './models/sea3d/skin.tjs.sea' );
+
+			//
+
+			options = {
+				hip: "hip",
+				// left is SEA3D bone names and right BVH bone names
+				names: {
+					"Base HumanPelvis": "hip",
+					"Base HumanSpine3": "abdomen",
+					"Base HumanRibcage": "chest",
+					"Base HumanHead": "head",
+
+					"Base HumanRUpperarm": "rShldr",
+					"Base HumanRForearm1": "rForeArm",
+					"Base HumanRPalm": "rHand",
+
+					"Base HumanLUpperarm": "lShldr",
+					"Base HumanLForearm1": "lForeArm",
+					"Base HumanLPalm": "lHand",
+
+					"Base HumanRThigh": "rThigh",
+					"Base HumanRCalf1": "rShin",
+					"Base HumanRFoot": "rFoot",
+
+					"Base HumanLThigh": "lThigh",
+					"Base HumanLCalf1": "lShin",
+					"Base HumanLFoot": "lFoot"
+				}
+			};
+
+			//
+
+			function loadBVH() {
+
+				var loader = new THREE.BVHLoader();
+				loader.load( "models/bvh/pirouette.bvh", function ( result ) {
+
+					bvhSkeletonHelper = new THREE.SkeletonHelper( result.skeleton.bones[ 0 ] );
+					bvhSkeletonHelper.skeleton = result.skeleton; // allow animation mixer to bind to SkeletonHelper directly
+
+					var boneContainer = new THREE.Group();
+					boneContainer.add( result.skeleton.bones[ 0 ] );
+					boneContainer.position.z = - 100;
+					boneContainer.position.y = - 100;
+
+					scene.add( bvhSkeletonHelper );
+					scene.add( boneContainer );
+
+					// get offsets when it is in T-Pose
+					options.offsets = THREE.SkeletonUtils.getSkeletonOffsets( player, bvhSkeletonHelper, options );
+
+					// play animation
+					bvhMixer = new THREE.AnimationMixer( bvhSkeletonHelper );
+					bvhMixer.clipAction( result.clip ).setEffectiveWeight( 1.0 ).play();
+
+				} );
+
+			}
+
+			function init() {
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0x333333 );
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 20000 );
+				camera.position.set( 1000, - 300, 1000 );
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				// post-processing
+
+				composer = new THREE.EffectComposer( renderer );
+
+				var renderPass = new THREE.RenderPass( scene, camera );
+				var copyPass = new THREE.ShaderPass( THREE.CopyShader );
+				composer.addPass( renderPass );
+
+				var vh = 1.4, vl = 1.2;
+
+				var colorCorrectionPass = new THREE.ShaderPass( THREE.ColorCorrectionShader );
+				colorCorrectionPass.uniforms[ "powRGB" ].value = new THREE.Vector3( vh, vh, vh );
+				colorCorrectionPass.uniforms[ "mulRGB" ].value = new THREE.Vector3( vl, vl, vl );
+				composer.addPass( colorCorrectionPass );
+
+				var vignettePass = new THREE.ShaderPass( THREE.VignetteShader );
+				vignettePass.uniforms[ "darkness" ].value = 1.0;
+				composer.addPass( vignettePass );
+
+				composer.addPass( copyPass );
+				copyPass.renderToScreen = true;
+
+				// events
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				composer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			//
+
+			var clock = new THREE.Clock();
+
+			function animate() {
+
+				var delta = clock.getDelta();
+
+				requestAnimationFrame( animate );
+
+				// Update SEA3D Animations
+				THREE.SEA3D.AnimationHandler.update( delta );
+
+				if ( bvhMixer ) {
+
+					bvhMixer.update( delta );
+
+					THREE.SkeletonUtils.retarget( player, bvhSkeletonHelper, options );
+
+				}
+
+				render( delta );
+
+				stats.update();
+
+			}
+
+			function render( dlt ) {
+
+				//renderer.render( scene, camera );
+				composer.render( dlt );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 10 - 10
examples/webgl_loader_sea3d_hierarchy.html

@@ -59,11 +59,11 @@
 
 			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
 
-			console.log("Visit https://github.com/sunag/sea3d to all codes and builds under development.");
+			console.log( "Visit https://github.com/sunag/sea3d to all codes and builds under development." );
 
 			var container, stats;
 
-			var camera, scene, renderer, composer, controls;
+			var camera, scene, renderer, composer;
 
 			var loader;
 
@@ -77,30 +77,30 @@
 
 			loader = new THREE.SEA3D( {
 
-				autoPlay : false, // Auto play animations
-				container : scene // Container to add models
+				autoPlay: false, // Auto play animations
+				container: scene // Container to add models
 
 			} );
 
-			loader.onComplete = function( e ) {
+			loader.onComplete = function ( e ) {
 
 				// play all animations
 
-				for(var i = 0; i < loader.meshes.length; i++) {
+				for ( var i = 0; i < loader.meshes.length; i ++ ) {
 
-					if (loader.meshes[i].animator)
-						loader.meshes[i].animator.play( "root" );
+					if ( loader.meshes[ i ].animator )
+						loader.meshes[ i ].animator.play( "root" );
 
 				}
 
 				// Get the first camera from SEA3D Studio
 				// use loader.get... to get others objects
 
-				var cam = loader.cameras[0];
+				var cam = loader.cameras[ 0 ];
 				camera.position.copy( cam.position );
 				camera.rotation.copy( cam.rotation );
 
-				controls = new THREE.OrbitControls( camera );
+				new THREE.OrbitControls( camera );
 
 				animate();
 

+ 38 - 36
examples/webgl_loader_sea3d_keyframe.html

@@ -56,11 +56,11 @@
 
 			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
 
-			console.log("Visit https://github.com/sunag/sea3d to all codes and builds under development.");
+			console.log( "Visit https://github.com/sunag/sea3d to all codes and builds under development." );
 
 			var container, stats;
 
-			var camera, scene, renderer, composer, controls, demoAt = -1;
+			var camera, scene, renderer, composer, demoAt = - 1;
 
 			var loader;
 
@@ -74,25 +74,25 @@
 
 			loader = new THREE.SEA3D( {
 
-				autoPlay : false, // Manual play animations
-				container : scene // Container to add models
+				autoPlay: false, // Manual play animations
+				container: scene // Container to add models
 
 			} );
 
-			loader.onComplete = function( e ) {
+			loader.onComplete = function ( e ) {
 
 				// Get the first camera from 3ds Max
 				// use loader.get... to get others objects
 
-				var cam = loader.cameras[0];
+				var cam = loader.cameras[ 0 ];
 				camera.position.copy( cam.position );
 				camera.rotation.copy( cam.rotation );
 
-				controls = new THREE.OrbitControls( camera );
+				new THREE.OrbitControls( camera );
 
 				// events
 
-				window.addEventListener('click', onMouseClick, false);
+				window.addEventListener( 'click', onMouseClick, false );
 
 				animate();
 
@@ -104,14 +104,14 @@
 			//	Animation Functions
 			//
 
-			function playAll(id, crossfade, offset) {
+			function playAll( id, crossfade, offset ) {
 
 				// play all animations
 
-				for(var i = 0; i < loader.meshes.length; i++) {
+				for ( var i = 0; i < loader.meshes.length; i ++ ) {
 
-					if (loader.meshes[i].animator)
-						loader.meshes[i].animator.play(id, crossfade, offset);
+					if ( loader.meshes[ i ].animator )
+						loader.meshes[ i ].animator.play( id, crossfade, offset );
 
 				}
 
@@ -119,10 +119,10 @@
 
 			function setTimeScale( timeScale ) {
 
-				for(var i = 0; i < loader.meshes.length; i++) {
+				for ( var i = 0; i < loader.meshes.length; i ++ ) {
 
-					if (loader.meshes[i].animator)
-						loader.meshes[i].animator.setTimeScale( timeScale );
+					if ( loader.meshes[ i ].animator )
+						loader.meshes[ i ].animator.setTimeScale( timeScale );
 
 				}
 
@@ -130,10 +130,10 @@
 
 			function stopAll() {
 
-				for(var i = 0; i < loader.meshes.length; i++) {
+				for ( var i = 0; i < loader.meshes.length; i ++ ) {
 
-					if (loader.meshes[i].animator)
-						loader.meshes[i].animator.stop();
+					if ( loader.meshes[ i ].animator )
+						loader.meshes[ i ].animator.stop();
 
 				}
 
@@ -201,9 +201,9 @@
 
 			//
 
-			function description(msg) {
+			function description( msg ) {
 
-				document.getElementById('description').innerHTML = (demoAt + 1) + " - " + msg;
+				document.getElementById( 'description' ).innerHTML = ( demoAt + 1 ) + " - " + msg;
 
 			}
 
@@ -211,62 +211,64 @@
 
 				// 0 at 3 demos
 
-				switch( demoAt = ++demoAt % 4 ) {
+				switch ( demoAt = ++ demoAt % 4 ) {
 
-					// play all animation to sequence "crash#1"
+						// play all animation to sequence "crash#1"
 
 					case 0:
 
-						playAll("crash#1", .5);
-						setTimeScale(1);
+						playAll( "crash#1", .5 );
+						setTimeScale( 1 );
 
-						description("crash#1 - crossfade 0.5 seconds");
+						description( "crash#1 - crossfade 0.5 seconds" );
 
 						break;
 
-					// play all animation to sequence "crash#2"
+						// play all animation to sequence "crash#2"
 
 					case 1:
 
-						playAll("crash#2", .5);
+						playAll( "crash#2", .5 );
 
-						description("crash#2 - crossfade 0.5 seconds");
+						description( "crash#2 - crossfade 0.5 seconds" );
 
 						break;
 
-					// play animation in single object
+						// play animation in single object
 
 					case 2:
 
 						stopAll();
 
-						var fracture99 = loader.getMesh("Object099");
+						var fracture99 = loader.getMesh( "Object099" );
 
 						//fracture99.animator.setTimeScale( 1 );
 						//fracture99.animator.playing
 						//fracture99.animator.currentAnimation
 						//fracture99.animator.previousAnimation
 
-						fracture99.animator.play("crash#2", .5, 0);
+						fracture99.animator.play( "crash#2", .5, 0 );
 						//fracture99.animator.stop();
 						//fracture99.animator.pause();
 						//fracture99.animator.resume();
 
-						description("crash#2 in single object - crossfade 0.5 seconds");
+						description( "crash#2 in single object - crossfade 0.5 seconds" );
 
 						break;
 
-					// set time scale
+						// set time scale
 
 					case 3:
 
-						playAll("crash#2", .5);
-						setTimeScale(.1);
+						playAll( "crash#2", .5 );
+						setTimeScale( .1 );
 
-						description("time scale / 10 - crossfade 0.5 seconds");
+						description( "time scale / 10 - crossfade 0.5 seconds" );
 
 						break;
+
 				}
+
 			}
 
 			//

+ 11 - 10
examples/webgl_loader_sea3d_morph.html

@@ -56,11 +56,11 @@
 
 			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
 
-			console.log("Visit https://github.com/sunag/sea3d to all codes and builds under development.");
+			console.log( "Visit https://github.com/sunag/sea3d to all codes and builds under development." );
 
 			var container, stats;
 
-			var camera, scene, renderer, composer, controls, teapot;
+			var camera, scene, renderer, composer, teapot;
 
 			var loader;
 
@@ -74,25 +74,26 @@
 
 			loader = new THREE.SEA3D( {
 
-				autoPlay : true, // Auto play animations
-				container : scene // Container to add models
+				autoPlay: true, // Auto play animations
+				scripts: false, // Disable embed scripts
+				container: scene // Container to add models
 
 			} );
 
-			loader.onComplete = function( e ) {
+			loader.onComplete = function ( e ) {
 
 				// Get the first camera from SEA3D Studio
 				// use loader.get... to get others objects
 
-				var cam = loader.cameras[0];
+				var cam = loader.cameras[ 0 ];
 				camera.position.copy( cam.position );
 				camera.rotation.copy( cam.rotation );
 
-				controls = new THREE.OrbitControls( camera );
+				new THREE.OrbitControls( camera );
 
 				// get mesh
 
-				teapot = loader.getMesh("Teapot01");
+				teapot = loader.getMesh( "Teapot01" );
 
 				// events
 
@@ -160,8 +161,8 @@
 
 				// change morph weight: 0 at 1
 				// names from modifier Morph of 3ds Max
-				teapot.setWeight("OldTeapot", mouseX / window.innerWidth);
-				teapot.setWeight("Sphere", mouseY / window.innerHeight);
+				teapot.setWeight( "OldTeapot", mouseX / window.innerWidth );
+				teapot.setWeight( "Sphere", mouseY / window.innerHeight );
 
 			}
 

+ 22 - 18
examples/webgl_loader_sea3d_physics.html

@@ -61,21 +61,21 @@
 
 			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
 
-			console.log("Visit https://github.com/sunag/sea3d to all codes and builds under development.");
+			console.log( "Visit https://github.com/sunag/sea3d to all codes and builds under development." );
 
 			var container, stats;
 
-			var camera, scene, renderer, composer, controls, demoAt = -1;
+			var camera, scene, renderer, composer;
 
 			var loader;
 
 			// Initialize Three.JS
 
 			init();
-			
+
 			// Initialize Physics Engine
-			
-			SEA3D.AMMO.init(); 
+
+			SEA3D.AMMO.init();
 
 			//
 			// SEA3D Loader
@@ -83,37 +83,41 @@
 
 			loader = new THREE.SEA3D( {
 
-				container : scene // Container to add models
+				container: scene // Container to add models
 
 			} );
 
-			loader.onComplete = function( e ) {
+			loader.onComplete = function ( e ) {
 
-				controls = new THREE.OrbitControls( camera );
+				new THREE.OrbitControls( camera );
 
 				// events
 
-				window.addEventListener( 'contextmenu', function(e) { 
+				window.addEventListener( 'contextmenu', function ( e ) {
 
-					e.preventDefault(); 
+					e.preventDefault();
 
-					cloneAsset(); 
+					cloneAsset();
 
 				} );
 
+				// prevent material compilation in render loop
+
+				renderer.compile( scene, camera );
+
 				animate();
 
 			};
 
 			loader.load( './models/sea3d/car.tjs.sea' );
 
-			var cloneAsset = function() {
+			var cloneAsset = function () {
 
 				var offset = 0;
 
-				return function() {
+				return function () {
 
-					var domain = this.loader.clone( { lights : false, runScripts : false, autoPlay: false, enabledPhysics : false } );
+					var domain = this.loader.clone( { lights: false, runScripts: false, autoPlay: false, enabledPhysics: false } );
 
 					offset -= 180;
 
@@ -124,7 +128,7 @@
 
 					this.scene.add( domain.container );
 
-				}
+				};
 
 			}();
 
@@ -139,7 +143,7 @@
 				document.body.appendChild( container );
 
 				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 15000 );
-				camera.position.set( 300, 200, -300 );
+				camera.position.set( 300, 200, - 300 );
 
 				renderer = new THREE.WebGLRenderer();
 				renderer.setPixelRatio( window.devicePixelRatio );
@@ -197,9 +201,9 @@
 
 				requestAnimationFrame( animate );
 
-				// Update Physics Engine
+				// Update Physics Engine ( fix delta-time in 60fps for more stability )
 
-				SEA3D.AMMO.update( delta );
+				SEA3D.AMMO.update( 1 / 60 );
 
 				// Update SEA3D Animations
 

+ 30 - 30
examples/webgl_loader_sea3d_skinning.html

@@ -58,11 +58,11 @@
 
 			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
 
-			console.log("Visit https://github.com/sunag/sea3d to all codes and builds under development.");
+			console.log( "Visit https://github.com/sunag/sea3d to all codes and builds under development." );
 
 			var container, stats;
 
-			var camera, scene, renderer, composer, controls, player, hat;
+			var camera, scene, renderer, composer, player, hat;
 
 			var loader;
 
@@ -76,26 +76,26 @@
 
 			loader = new THREE.SEA3D( {
 
-				autoPlay : true, // Auto play animations
-				container : scene, // Container to add models
-				multiplier : .6 // Light multiplier
+				autoPlay: true, // Auto play animations
+				container: scene, // Container to add models
+				multiplier: .6 // Light multiplier
 
 			} );
 
-			loader.onComplete = function( e ) {
+			loader.onComplete = function ( e ) {
 
 				// Get the first camera from SEA3D Studio
 				// use loader.get... to get others objects
 
-				var cam = loader.cameras[0];
+				var cam = loader.cameras[ 0 ];
 				camera.position.copy( cam.position );
 				camera.rotation.copy( cam.rotation );
 
 				controls = new THREE.OrbitControls( camera );
 
 				// get meshes
-				player = loader.getMesh("Player");
-				hat = loader.getMesh("Hat");
+				player = loader.getMesh( "Player" );
+				hat = loader.getMesh( "Hat" );
 
 				// on animation complete example
 
@@ -103,13 +103,13 @@
 
 				// or
 
-				player.animation['pass#1'].addEventListener(THREE.SEA3D.Animation.COMPLETE, function(e) {
+				player.animation[ 'pass#1' ].addEventListener( THREE.SEA3D.Animation.COMPLETE, function ( e ) {
 
-					console.log("Animation completed!", e);
+					console.log( "Animation completed!", e );
 
 				} );
 
-				window.addEventListener( 'click', onMouseClick );
+				window.addEventListener( 'mousedown', onMouseClick );
 				window.addEventListener( 'contextmenu', onRightClick );
 
 				animate();
@@ -124,34 +124,34 @@
 
 			function updatePlayerCountDescription( count ) {
 
-				document.getElementById("playercount").innerText = "Numbers of players " + count + " | Middle click to clone";
+				document.getElementById( "playercount" ).innerText = "Numbers of players " + count + " | Middle click to clone";
 
 			}
 
-			var cloneAsset = function() {
+			var cloneAsset = function () {
 
 				var count = 2, size = 25;
 
-				return function() {
+				return function () {
 
 					updatePlayerCountDescription( count );
 
-					var PHI = Math.PI * 2 * (Math.sqrt(5) + 1) / 2;  // golden ratio
-					var angle = PHI * count++;
+					var PHI = Math.PI * 2 * ( Math.sqrt( 5 ) + 1 ) / 2; // golden ratio
+					var angle = PHI * count ++;
 
 					var container = new THREE.Object3D();
 					container.position.z = ( size * count ) * Math.cos( angle );
 					container.position.x = ( size * count ) * Math.sin( angle );
 
 					scene.add( container );
-					
-					var domain = loader.clone( { 
-						autoPlay : true, 
-						container : container, 
-						lights : false 
-					});
 
-				}
+					var domain = loader.clone( {
+						autoPlay: true,
+						container: container,
+						lights: false
+					} );
+
+				};
 
 			}();
 
@@ -205,10 +205,10 @@
 			function onRightClick( e ) {
 
 				// play character animation
-				if (player.currentAnimation.name == "idle")
-					player.play("run", .5);// play "pass#1" to caller .onComplete animation example
+				if ( player.currentAnimation.name == "idle" )
+					player.play( "run", .5 );// play "pass#1" to caller .onComplete animation example
 				else
-					player.play("idle", .5);
+					player.play( "idle", .5 );
 
 				e.preventDefault();
 
@@ -216,11 +216,11 @@
 
 			function onMouseClick( e ) {
 
-				if (e.button === 0) {
+				if ( e.button === 0 ) {
 
-					hat.visible = !hat.visible;
+					hat.visible = ! hat.visible;
 
-				} else if (e.button === 1) {
+				} else if ( e.button === 1 ) {
 
 					cloneAsset();
 

+ 13 - 13
examples/webgl_loader_sea3d_sound.html

@@ -103,7 +103,7 @@
 
 			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
 
-			console.log("Visit https://github.com/sunag/sea3d to all codes and builds under development.");
+			console.log( "Visit https://github.com/sunag/sea3d to all codes and builds under development." );
 
 			var container, raycaster, stats;
 
@@ -126,12 +126,12 @@
 
 			loader = new THREE.SEA3D( {
 
-				autoPlay : true, // Auto play animations
-				container : scene // Container to add models
+				autoPlay: true, // Auto play animations
+				container: scene // Container to add models
 
 			} );
 
-			loader.onComplete = function( e ) {
+			loader.onComplete = function ( e ) {
 
 				audioListener = loader.audioListener;
 
@@ -144,19 +144,19 @@
 
 				// sound asset 1
 
-				lightOutside = loader.getLight("Light1");
+				lightOutside = loader.getLight( "Light1" );
 
-				var soundOutside = loader.getSound3D("Point001");
+				var soundOutside = loader.getSound3D( "Point001" );
 				soundOutsideAnalyser = new THREE.AudioAnalyser( soundOutside, 32 );
 
 				// sound asset 2 + area
 
-				lightArea = loader.getLight("Light2");
+				lightArea = loader.getLight( "Light2" );
 
-				soundArea = loader.getSound3D("Point002");
+				soundArea = loader.getSound3D( "Point002" );
 				soundAreaAnalyser = new THREE.AudioAnalyser( soundArea, 512 );
 
-				collisionArea = loader.getMesh("Torus003");
+				collisionArea = loader.getMesh( "Torus003" );
 
 				animate();
 
@@ -394,18 +394,18 @@
 			function updateSoundFilter() {
 
 				// difference position between sound and listener
-				var difPos = new THREE.Vector3().setFromMatrixPosition( soundArea.matrixWorld ).sub(audioPos);
+				var difPos = new THREE.Vector3().setFromMatrixPosition( soundArea.matrixWorld ).sub( audioPos );
 				var length = difPos.length();
 
 				// pick a vector from camera to sound
 				raycaster.set( audioPos, difPos.normalize() );
 
 				// intersecting sound1
-				if ( length > 50 && raycaster.intersectObjects( [collisionArea] ).length ) {
+				if ( length > 50 && raycaster.intersectObjects( [ collisionArea ] ).length ) {
 
-					if ( soundArea.getFilters()[0] !== soundFilter ) soundArea.setFilters( [ soundFilter ] );
+					if ( soundArea.getFilters()[ 0 ] !== soundFilter ) soundArea.setFilters( [ soundFilter ] );
 
-				} else if ( soundArea.getFilters()[0] === soundFilter ) {
+				} else if ( soundArea.getFilters()[ 0 ] === soundFilter ) {
 
 					soundArea.setFilters( [] );