瀏覽代碼

git should handle the line endings

Jürgen Ebert 7 年之前
父節點
當前提交
5446f535d1
共有 33 個文件被更改,包括 15973 次插入16392 次删除
  1. 3 0
      .gitattributes
  2. 1 1
      editor/js/Sidebar.Geometry.LatheGeometry.js
  3. 254 254
      examples/js/MD2Character.js
  4. 560 560
      examples/js/MD2CharacterComplex.js
  5. 1044 1044
      examples/js/MarchingCubes.js
  6. 316 316
      examples/js/MorphBlendMesh.js
  7. 1 1
      examples/js/Ocean.js
  8. 2358 2358
      examples/js/loaders/AssimpLoader.js
  9. 260 260
      examples/js/loaders/LoaderSupport.js
  10. 33 33
      examples/js/loaders/SVGLoader.js
  11. 1142 1142
      examples/js/loaders/TDSLoader.js
  12. 3309 3309
      examples/js/loaders/sea3d/SEA3D.js
  13. 774 774
      examples/js/loaders/sea3d/SEA3DLegacy.js
  14. 3843 3843
      examples/js/loaders/sea3d/SEA3DLoader.js
  15. 262 262
      examples/js/objects/Reflector.js
  16. 0 161
      examples/js/postprocessing/SMAAPass.js
  17. 64 64
      examples/js/shaders/LuminosityHighPassShader.js
  18. 1 1
      examples/js/shaders/OceanShaders.js
  19. 462 462
      examples/js/shaders/SMAAShader.js
  20. 0 19
      examples/models/animated/horse.js
  21. 0 36
      examples/models/skinned/marine/m4.js
  22. 125 125
      examples/obj/camaro/CamaroNoUv_bin.js
  23. 296 296
      examples/obj/f50/F50NoUv_bin.js
  24. 95 95
      examples/obj/female02/Female02_bin.js
  25. 0 95
      examples/obj/female02/Female02_slim.js
  26. 94 94
      examples/obj/gallardo/GallardoNoUv_bin.js
  27. 82 82
      examples/obj/male02/Male02_bin.js
  28. 0 82
      examples/obj/male02/Male02_slim.js
  29. 108 108
      examples/obj/veyron/VeyronNoUv_bin.js
  30. 29 29
      examples/obj/walt/WaltHead_bin.js
  31. 0 29
      examples/obj/walt/WaltHead_slim.js
  32. 1 1
      test/unit/editor/Sidebar.Geometry.LatheGeometry.tests.js
  33. 456 456
      test/unit/src/animation/AnimationAction.tests.js

+ 3 - 0
.gitattributes

@@ -0,0 +1,3 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto
+

+ 1 - 1
editor/js/Sidebar.Geometry.LatheGeometry.js

@@ -1,4 +1,4 @@
-/**
+/**
  * @author rfm1201
  */
 

+ 254 - 254
examples/js/MD2Character.js

@@ -1,254 +1,254 @@
-/**
- * @author alteredq / http://alteredqualia.com/
- */
-
-THREE.MD2Character = function () {
-
-	var scope = this;
-
-	this.scale = 1;
-	this.animationFPS = 6;
-
-	this.root = new THREE.Object3D();
-
-	this.meshBody = null;
-	this.meshWeapon = null;
-
-	this.skinsBody = [];
-	this.skinsWeapon = [];
-
-	this.weapons = [];
-
-	this.activeAnimation = null;
-
-	this.mixer = null;
-
-	this.onLoadComplete = function () {};
-
-	this.loadCounter = 0;
-
-	this.loadParts = function ( config ) {
-
-		this.loadCounter = config.weapons.length * 2 + config.skins.length + 1;
-
-		var weaponsTextures = [];
-		for ( var i = 0; i < config.weapons.length; i ++ ) weaponsTextures[ i ] = config.weapons[ i ][ 1 ];
-		// SKINS
-
-		this.skinsBody = loadTextures( config.baseUrl + "skins/", config.skins );
-		this.skinsWeapon = loadTextures( config.baseUrl + "skins/", weaponsTextures );
-
-		// BODY
-
-		var loader = new THREE.MD2Loader();
-
-		loader.load( config.baseUrl + config.body, function( geo ) {
-
-			geo.computeBoundingBox();
-			scope.root.position.y = - scope.scale * geo.boundingBox.min.y;
-
-			var mesh = createPart( geo, scope.skinsBody[ 0 ] );
-			mesh.scale.set( scope.scale, scope.scale, scope.scale );
-
-			scope.root.add( mesh );
-
-			scope.meshBody = mesh;
-
-			scope.meshBody.clipOffset = 0;
-			scope.activeAnimationClipName = mesh.geometry.animations[0].name;
-
-			scope.mixer = new THREE.AnimationMixer( mesh );
-
-			checkLoadingComplete();
-
-		} );
-
-		// WEAPONS
-
-		var generateCallback = function ( index, name ) {
-
-			return function( geo ) {
-
-				var mesh = createPart( geo, scope.skinsWeapon[ index ] );
-				mesh.scale.set( scope.scale, scope.scale, scope.scale );
-				mesh.visible = false;
-
-				mesh.name = name;
-
-				scope.root.add( mesh );
-
-				scope.weapons[ index ] = mesh;
-				scope.meshWeapon = mesh;
-
-				checkLoadingComplete();
-
-			}
-
-		};
-
-		for ( var i = 0; i < config.weapons.length; i ++ ) {
-
-			loader.load( config.baseUrl + config.weapons[ i ][ 0 ], generateCallback( i, config.weapons[ i ][ 0 ] ) );
-
-		}
-
-	};
-
-	this.setPlaybackRate = function ( rate ) {
-
-		if( rate !== 0 ) {
-			this.mixer.timeScale = 1 / rate;
-		}
-		else {
-			this.mixer.timeScale = 0;
-		}
-
-	};
-
-	this.setWireframe = function ( wireframeEnabled ) {
-
-		if ( wireframeEnabled ) {
-
-			if ( this.meshBody ) this.meshBody.material = this.meshBody.materialWireframe;
-			if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialWireframe;
-
-		} else {
-
-			if ( this.meshBody ) this.meshBody.material = this.meshBody.materialTexture;
-			if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialTexture;
-
-		}
-
-	};
-
-	this.setSkin = function( index ) {
-
-		if ( this.meshBody && this.meshBody.material.wireframe === false ) {
-
-			this.meshBody.material.map = this.skinsBody[ index ];
-
-		}
-
-	};
-
-	this.setWeapon = function ( index ) {
-
-		for ( var i = 0; i < this.weapons.length; i ++ ) this.weapons[ i ].visible = false;
-
-		var activeWeapon = this.weapons[ index ];
-
-		if ( activeWeapon ) {
-
-			activeWeapon.visible = true;
-			this.meshWeapon = activeWeapon;
-
-			scope.syncWeaponAnimation();
-
-		}
-
-	};
-
-	this.setAnimation = function ( clipName ) {
-
-		if ( this.meshBody ) {
-
-			if( this.meshBody.activeAction ) {
-				this.meshBody.activeAction.stop();
-				this.meshBody.activeAction = null;
-			}
-
-			var action = this.mixer.clipAction( clipName, this.meshBody );
-			if( action ) {
-
-				this.meshBody.activeAction = action.play();
-
-			}
-
-		}
-
-		scope.activeClipName = clipName;
-
-		scope.syncWeaponAnimation();
-
-	};
-
-	this.syncWeaponAnimation = function() {
-
-		var clipName = scope.activeClipName;
-
-		if ( scope.meshWeapon ) {
-
-			if( this.meshWeapon.activeAction ) {
-				this.meshWeapon.activeAction.stop();
-				this.meshWeapon.activeAction = null;
-			}
-
-			var geometry = this.meshWeapon.geometry,
-				animations = geometry.animations;
-
-			var action = this.mixer.clipAction( clipName, this.meshWeapon );
-			if( action ) {
-
-				this.meshWeapon.activeAction =
-						action.syncWith( this.meshBody.activeAction ).play();
-
-			}
-
-		}
-
-	}
-
-	this.update = function ( delta ) {
-
-		if( this.mixer ) this.mixer.update( delta );
-
-	};
-
-	function loadTextures( baseUrl, textureUrls ) {
-
-		var textureLoader = new THREE.TextureLoader();
-		var textures = [];
-
-		for ( var i = 0; i < textureUrls.length; i ++ ) {
-
-			textures[ i ] = textureLoader.load( baseUrl + textureUrls[ i ], checkLoadingComplete );
-			textures[ i ].mapping = THREE.UVMapping;
-			textures[ i ].name = textureUrls[ i ];
-
-		}
-
-		return textures;
-
-	}
-
-	function createPart( geometry, skinMap ) {
-
-		var materialWireframe = new THREE.MeshLambertMaterial( { color: 0xffaa00, wireframe: true, morphTargets: true, morphNormals: true } );
-		var materialTexture = new THREE.MeshLambertMaterial( { color: 0xffffff, wireframe: false, map: skinMap, morphTargets: true, morphNormals: true } );
-
-		//
-
-		var mesh = new THREE.Mesh( geometry, materialTexture );
-		mesh.rotation.y = - Math.PI / 2;
-
-		mesh.castShadow = true;
-		mesh.receiveShadow = true;
-
-		//
-
-		mesh.materialTexture = materialTexture;
-		mesh.materialWireframe = materialWireframe;
-
-		return mesh;
-
-	}
-
-	function checkLoadingComplete() {
-
-		scope.loadCounter -= 1;
-
-		if ( scope.loadCounter === 0 ) scope.onLoadComplete();
-
-	}
-
-};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.MD2Character = function () {
+
+	var scope = this;
+
+	this.scale = 1;
+	this.animationFPS = 6;
+
+	this.root = new THREE.Object3D();
+
+	this.meshBody = null;
+	this.meshWeapon = null;
+
+	this.skinsBody = [];
+	this.skinsWeapon = [];
+
+	this.weapons = [];
+
+	this.activeAnimation = null;
+
+	this.mixer = null;
+
+	this.onLoadComplete = function () {};
+
+	this.loadCounter = 0;
+
+	this.loadParts = function ( config ) {
+
+		this.loadCounter = config.weapons.length * 2 + config.skins.length + 1;
+
+		var weaponsTextures = [];
+		for ( var i = 0; i < config.weapons.length; i ++ ) weaponsTextures[ i ] = config.weapons[ i ][ 1 ];
+		// SKINS
+
+		this.skinsBody = loadTextures( config.baseUrl + "skins/", config.skins );
+		this.skinsWeapon = loadTextures( config.baseUrl + "skins/", weaponsTextures );
+
+		// BODY
+
+		var loader = new THREE.MD2Loader();
+
+		loader.load( config.baseUrl + config.body, function( geo ) {
+
+			geo.computeBoundingBox();
+			scope.root.position.y = - scope.scale * geo.boundingBox.min.y;
+
+			var mesh = createPart( geo, scope.skinsBody[ 0 ] );
+			mesh.scale.set( scope.scale, scope.scale, scope.scale );
+
+			scope.root.add( mesh );
+
+			scope.meshBody = mesh;
+
+			scope.meshBody.clipOffset = 0;
+			scope.activeAnimationClipName = mesh.geometry.animations[0].name;
+
+			scope.mixer = new THREE.AnimationMixer( mesh );
+
+			checkLoadingComplete();
+
+		} );
+
+		// WEAPONS
+
+		var generateCallback = function ( index, name ) {
+
+			return function( geo ) {
+
+				var mesh = createPart( geo, scope.skinsWeapon[ index ] );
+				mesh.scale.set( scope.scale, scope.scale, scope.scale );
+				mesh.visible = false;
+
+				mesh.name = name;
+
+				scope.root.add( mesh );
+
+				scope.weapons[ index ] = mesh;
+				scope.meshWeapon = mesh;
+
+				checkLoadingComplete();
+
+			}
+
+		};
+
+		for ( var i = 0; i < config.weapons.length; i ++ ) {
+
+			loader.load( config.baseUrl + config.weapons[ i ][ 0 ], generateCallback( i, config.weapons[ i ][ 0 ] ) );
+
+		}
+
+	};
+
+	this.setPlaybackRate = function ( rate ) {
+
+		if( rate !== 0 ) {
+			this.mixer.timeScale = 1 / rate;
+		}
+		else {
+			this.mixer.timeScale = 0;
+		}
+
+	};
+
+	this.setWireframe = function ( wireframeEnabled ) {
+
+		if ( wireframeEnabled ) {
+
+			if ( this.meshBody ) this.meshBody.material = this.meshBody.materialWireframe;
+			if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialWireframe;
+
+		} else {
+
+			if ( this.meshBody ) this.meshBody.material = this.meshBody.materialTexture;
+			if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialTexture;
+
+		}
+
+	};
+
+	this.setSkin = function( index ) {
+
+		if ( this.meshBody && this.meshBody.material.wireframe === false ) {
+
+			this.meshBody.material.map = this.skinsBody[ index ];
+
+		}
+
+	};
+
+	this.setWeapon = function ( index ) {
+
+		for ( var i = 0; i < this.weapons.length; i ++ ) this.weapons[ i ].visible = false;
+
+		var activeWeapon = this.weapons[ index ];
+
+		if ( activeWeapon ) {
+
+			activeWeapon.visible = true;
+			this.meshWeapon = activeWeapon;
+
+			scope.syncWeaponAnimation();
+
+		}
+
+	};
+
+	this.setAnimation = function ( clipName ) {
+
+		if ( this.meshBody ) {
+
+			if( this.meshBody.activeAction ) {
+				this.meshBody.activeAction.stop();
+				this.meshBody.activeAction = null;
+			}
+
+			var action = this.mixer.clipAction( clipName, this.meshBody );
+			if( action ) {
+
+				this.meshBody.activeAction = action.play();
+
+			}
+
+		}
+
+		scope.activeClipName = clipName;
+
+		scope.syncWeaponAnimation();
+
+	};
+
+	this.syncWeaponAnimation = function() {
+
+		var clipName = scope.activeClipName;
+
+		if ( scope.meshWeapon ) {
+
+			if( this.meshWeapon.activeAction ) {
+				this.meshWeapon.activeAction.stop();
+				this.meshWeapon.activeAction = null;
+			}
+
+			var geometry = this.meshWeapon.geometry,
+				animations = geometry.animations;
+
+			var action = this.mixer.clipAction( clipName, this.meshWeapon );
+			if( action ) {
+
+				this.meshWeapon.activeAction =
+						action.syncWith( this.meshBody.activeAction ).play();
+
+			}
+
+		}
+
+	}
+
+	this.update = function ( delta ) {
+
+		if( this.mixer ) this.mixer.update( delta );
+
+	};
+
+	function loadTextures( baseUrl, textureUrls ) {
+
+		var textureLoader = new THREE.TextureLoader();
+		var textures = [];
+
+		for ( var i = 0; i < textureUrls.length; i ++ ) {
+
+			textures[ i ] = textureLoader.load( baseUrl + textureUrls[ i ], checkLoadingComplete );
+			textures[ i ].mapping = THREE.UVMapping;
+			textures[ i ].name = textureUrls[ i ];
+
+		}
+
+		return textures;
+
+	}
+
+	function createPart( geometry, skinMap ) {
+
+		var materialWireframe = new THREE.MeshLambertMaterial( { color: 0xffaa00, wireframe: true, morphTargets: true, morphNormals: true } );
+		var materialTexture = new THREE.MeshLambertMaterial( { color: 0xffffff, wireframe: false, map: skinMap, morphTargets: true, morphNormals: true } );
+
+		//
+
+		var mesh = new THREE.Mesh( geometry, materialTexture );
+		mesh.rotation.y = - Math.PI / 2;
+
+		mesh.castShadow = true;
+		mesh.receiveShadow = true;
+
+		//
+
+		mesh.materialTexture = materialTexture;
+		mesh.materialWireframe = materialWireframe;
+
+		return mesh;
+
+	}
+
+	function checkLoadingComplete() {
+
+		scope.loadCounter -= 1;
+
+		if ( scope.loadCounter === 0 ) scope.onLoadComplete();
+
+	}
+
+};

+ 560 - 560
examples/js/MD2CharacterComplex.js

@@ -1,560 +1,560 @@
-/**
- * @author alteredq / http://alteredqualia.com/
- */
-
-THREE.MD2CharacterComplex = function () {
-
-	var scope = this;
-
-	this.scale = 1;
-
-	// animation parameters
-
-	this.animationFPS = 6;
-	this.transitionFrames = 15;
-
-	// movement model parameters
-
-	this.maxSpeed = 275;
-	this.maxReverseSpeed = - 275;
-
-	this.frontAcceleration = 600;
-	this.backAcceleration = 600;
-
-	this.frontDecceleration = 600;
-
-	this.angularSpeed = 2.5;
-
-	// rig
-
-	this.root = new THREE.Object3D();
-
-	this.meshBody = null;
-	this.meshWeapon = null;
-
-	this.controls = null;
-
-	// skins
-
-	this.skinsBody = [];
-	this.skinsWeapon = [];
-
-	this.weapons = [];
-
-	this.currentSkin = undefined;
-
-	//
-
-	this.onLoadComplete = function () {};
-
-	// internals
-
-	this.meshes = [];
-	this.animations = {};
-
-	this.loadCounter = 0;
-
-	// internal movement control variables
-
-	this.speed = 0;
-	this.bodyOrientation = 0;
-
-	this.walkSpeed = this.maxSpeed;
-	this.crouchSpeed = this.maxSpeed * 0.5;
-
-	// internal animation parameters
-
-	this.activeAnimation = null;
-	this.oldAnimation = null;
-
-	// API
-
-	this.enableShadows = function ( enable ) {
-
-		for ( var i = 0; i < this.meshes.length; i ++ ) {
-
-			this.meshes[ i ].castShadow = enable;
-			this.meshes[ i ].receiveShadow = enable;
-
-		}
-
-	};
-
-	this.setVisible = function ( enable ) {
-
-		for ( var i = 0; i < this.meshes.length; i ++ ) {
-
-			this.meshes[ i ].visible = enable;
-			this.meshes[ i ].visible = enable;
-
-		}
-
-	};
-
-
-	this.shareParts = function ( original ) {
-
-		this.animations = original.animations;
-		this.walkSpeed = original.walkSpeed;
-		this.crouchSpeed = original.crouchSpeed;
-
-		this.skinsBody = original.skinsBody;
-		this.skinsWeapon = original.skinsWeapon;
-
-		// BODY
-
-		var mesh = createPart( original.meshBody.geometry, this.skinsBody[ 0 ] );
-		mesh.scale.set( this.scale, this.scale, this.scale );
-
-		this.root.position.y = original.root.position.y;
-		this.root.add( mesh );
-
-		this.meshBody = mesh;
-
-		this.meshes.push( mesh );
-
-		// WEAPONS
-
-		for ( var i = 0; i < original.weapons.length; i ++ ) {
-
-			var meshWeapon = createPart( original.weapons[ i ].geometry, this.skinsWeapon[ i ] );
-			meshWeapon.scale.set( this.scale, this.scale, this.scale );
-			meshWeapon.visible = false;
-
-			meshWeapon.name = original.weapons[ i ].name;
-
-			this.root.add( meshWeapon );
-
-			this.weapons[ i ] = meshWeapon;
-			this.meshWeapon = meshWeapon;
-
-			this.meshes.push( meshWeapon );
-
-		}
-
-	};
-
-	this.loadParts = function ( config ) {
-
-		this.animations = config.animations;
-		this.walkSpeed = config.walkSpeed;
-		this.crouchSpeed = config.crouchSpeed;
-
-		this.loadCounter = config.weapons.length * 2 + config.skins.length + 1;
-
-		var weaponsTextures = [];
-		for ( var i = 0; i < config.weapons.length; i ++ ) weaponsTextures[ i ] = config.weapons[ i ][ 1 ];
-
-		// SKINS
-
-		this.skinsBody = loadTextures( config.baseUrl + "skins/", config.skins );
-		this.skinsWeapon = loadTextures( config.baseUrl + "skins/", weaponsTextures );
-
-		// BODY
-
-		var loader = new THREE.MD2Loader();
-
-		loader.load( config.baseUrl + config.body, function( geo ) {
-
-			geo.computeBoundingBox();
-			scope.root.position.y = - scope.scale * geo.boundingBox.min.y;
-
-			var mesh = createPart( geo, scope.skinsBody[ 0 ] );
-			mesh.scale.set( scope.scale, scope.scale, scope.scale );
-
-			scope.root.add( mesh );
-
-			scope.meshBody = mesh;
-			scope.meshes.push( mesh );
-
-			checkLoadingComplete();
-
-		} );
-
-		// WEAPONS
-
-		var generateCallback = function ( index, name ) {
-
-			return function( geo ) {
-
-				var mesh = createPart( geo, scope.skinsWeapon[ index ] );
-				mesh.scale.set( scope.scale, scope.scale, scope.scale );
-				mesh.visible = false;
-
-				mesh.name = name;
-
-				scope.root.add( mesh );
-
-				scope.weapons[ index ] = mesh;
-				scope.meshWeapon = mesh;
-				scope.meshes.push( mesh );
-
-				checkLoadingComplete();
-
-			}
-
-		};
-
-		for ( var i = 0; i < config.weapons.length; i ++ ) {
-
-			loader.load( config.baseUrl + config.weapons[ i ][ 0 ], generateCallback( i, config.weapons[ i ][ 0 ] ) );
-
-		}
-
-	};
-
-	this.setPlaybackRate = function ( rate ) {
-
-		if ( this.meshBody ) this.meshBody.duration = this.meshBody.baseDuration / rate;
-		if ( this.meshWeapon ) this.meshWeapon.duration = this.meshWeapon.baseDuration / rate;
-
-	};
-
-	this.setWireframe = function ( wireframeEnabled ) {
-
-		if ( wireframeEnabled ) {
-
-			if ( this.meshBody ) this.meshBody.material = this.meshBody.materialWireframe;
-			if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialWireframe;
-
-		} else {
-
-			if ( this.meshBody ) this.meshBody.material = this.meshBody.materialTexture;
-			if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialTexture;
-
-		}
-
-	};
-
-	this.setSkin = function( index ) {
-
-		if ( this.meshBody && this.meshBody.material.wireframe === false ) {
-
-			this.meshBody.material.map = this.skinsBody[ index ];
-			this.currentSkin = index;
-
-		}
-
-	};
-
-	this.setWeapon = function ( index ) {
-
-		for ( var i = 0; i < this.weapons.length; i ++ ) this.weapons[ i ].visible = false;
-
-		var activeWeapon = this.weapons[ index ];
-
-		if ( activeWeapon ) {
-
-			activeWeapon.visible = true;
-			this.meshWeapon = activeWeapon;
-
-			if ( this.activeAnimation ) {
-
-				activeWeapon.playAnimation( this.activeAnimation );
-				this.meshWeapon.setAnimationTime( this.activeAnimation, this.meshBody.getAnimationTime( this.activeAnimation ) );
-
-			}
-
-		}
-
-	};
-
-	this.setAnimation = function ( animationName ) {
-
-		if ( animationName === this.activeAnimation || ! animationName ) return;
-
-		if ( this.meshBody ) {
-
-			this.meshBody.setAnimationWeight( animationName, 0 );
-			this.meshBody.playAnimation( animationName );
-
-			this.oldAnimation = this.activeAnimation;
-			this.activeAnimation = animationName;
-
-			this.blendCounter = this.transitionFrames;
-
-		}
-
-		if ( this.meshWeapon ) {
-
-			this.meshWeapon.setAnimationWeight( animationName, 0 );
-			this.meshWeapon.playAnimation( animationName );
-
-		}
-
-
-	};
-
-	this.update = function ( delta ) {
-
-		if ( this.controls ) this.updateMovementModel( delta );
-
-		if ( this.animations ) {
-
-			this.updateBehaviors( delta );
-			this.updateAnimations( delta );
-
-		}
-
-	};
-
-	this.updateAnimations = function ( delta ) {
-
-		var mix = 1;
-
-		if ( this.blendCounter > 0 ) {
-
-			mix = ( this.transitionFrames - this.blendCounter ) / this.transitionFrames;
-			this.blendCounter -= 1;
-
-		}
-
-		if ( this.meshBody ) {
-
-			this.meshBody.update( delta );
-
-			this.meshBody.setAnimationWeight( this.activeAnimation, mix );
-			this.meshBody.setAnimationWeight( this.oldAnimation,  1 - mix );
-
-		}
-
-		if ( this.meshWeapon ) {
-
-			this.meshWeapon.update( delta );
-
-			this.meshWeapon.setAnimationWeight( this.activeAnimation, mix );
-			this.meshWeapon.setAnimationWeight( this.oldAnimation,  1 - mix );
-
-		}
-
-	};
-
-	this.updateBehaviors = function ( delta ) {
-
-		var controls = this.controls;
-		var animations = this.animations;
-
-		var moveAnimation, idleAnimation;
-
-		// crouch vs stand
-
-		if ( controls.crouch ) {
-
-			moveAnimation = animations[ "crouchMove" ];
-			idleAnimation = animations[ "crouchIdle" ];
-
-		} else {
-
-			moveAnimation = animations[ "move" ];
-			idleAnimation = animations[ "idle" ];
-
-		}
-
-		// actions
-
-		if ( controls.jump ) {
-
-			moveAnimation = animations[ "jump" ];
-			idleAnimation = animations[ "jump" ];
-
-		}
-
-		if ( controls.attack ) {
-
-			if ( controls.crouch ) {
-
-				moveAnimation = animations[ "crouchAttack" ];
-				idleAnimation = animations[ "crouchAttack" ];
-
-			} else {
-
-				moveAnimation = animations[ "attack" ];
-				idleAnimation = animations[ "attack" ];
-
-			}
-
-		}
-
-		// set animations
-
-		if ( controls.moveForward || controls.moveBackward || controls.moveLeft || controls.moveRight ) {
-
-			if ( this.activeAnimation !== moveAnimation ) {
-
-				this.setAnimation( moveAnimation );
-
-			}
-
-		}
-
-
-		if ( Math.abs( this.speed ) < 0.2 * this.maxSpeed && ! ( controls.moveLeft || controls.moveRight || controls.moveForward || controls.moveBackward ) ) {
-
-			if ( this.activeAnimation !== idleAnimation ) {
-
-				this.setAnimation( idleAnimation );
-
-			}
-
-		}
-
-		// set animation direction
-
-		if ( controls.moveForward ) {
-
-			if ( this.meshBody ) {
-
-				this.meshBody.setAnimationDirectionForward( this.activeAnimation );
-				this.meshBody.setAnimationDirectionForward( this.oldAnimation );
-
-			}
-
-			if ( this.meshWeapon ) {
-
-				this.meshWeapon.setAnimationDirectionForward( this.activeAnimation );
-				this.meshWeapon.setAnimationDirectionForward( this.oldAnimation );
-
-			}
-
-		}
-
-		if ( controls.moveBackward ) {
-
-			if ( this.meshBody ) {
-
-				this.meshBody.setAnimationDirectionBackward( this.activeAnimation );
-				this.meshBody.setAnimationDirectionBackward( this.oldAnimation );
-
-			}
-
-			if ( this.meshWeapon ) {
-
-				this.meshWeapon.setAnimationDirectionBackward( this.activeAnimation );
-				this.meshWeapon.setAnimationDirectionBackward( this.oldAnimation );
-
-			}
-
-		}
-
-	};
-
-	this.updateMovementModel = function ( delta ) {
-
-		var controls = this.controls;
-
-		// speed based on controls
-
-		if ( controls.crouch ) 	this.maxSpeed = this.crouchSpeed;
-		else this.maxSpeed = this.walkSpeed;
-
-		this.maxReverseSpeed = - this.maxSpeed;
-
-		if ( controls.moveForward )  this.speed = THREE.Math.clamp( this.speed + delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );
-		if ( controls.moveBackward ) this.speed = THREE.Math.clamp( this.speed - delta * this.backAcceleration, this.maxReverseSpeed, this.maxSpeed );
-
-		// orientation based on controls
-		// (don't just stand while turning)
-
-		var dir = 1;
-
-		if ( controls.moveLeft ) {
-
-			this.bodyOrientation += delta * this.angularSpeed;
-			this.speed = THREE.Math.clamp( this.speed + dir * delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );
-
-		}
-
-		if ( controls.moveRight ) {
-
-			this.bodyOrientation -= delta * this.angularSpeed;
-			this.speed = THREE.Math.clamp( this.speed + dir * delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );
-
-		}
-
-		// speed decay
-
-		if ( ! ( controls.moveForward || controls.moveBackward ) ) {
-
-			if ( this.speed > 0 ) {
-
-				var k = exponentialEaseOut( this.speed / this.maxSpeed );
-				this.speed = THREE.Math.clamp( this.speed - k * delta * this.frontDecceleration, 0, this.maxSpeed );
-
-			} else {
-
-				var k = exponentialEaseOut( this.speed / this.maxReverseSpeed );
-				this.speed = THREE.Math.clamp( this.speed + k * delta * this.backAcceleration, this.maxReverseSpeed, 0 );
-
-			}
-
-		}
-
-		// displacement
-
-		var forwardDelta = this.speed * delta;
-
-		this.root.position.x += Math.sin( this.bodyOrientation ) * forwardDelta;
-		this.root.position.z += Math.cos( this.bodyOrientation ) * forwardDelta;
-
-		// steering
-
-		this.root.rotation.y = this.bodyOrientation;
-
-	};
-
-	// internal helpers
-
-	function loadTextures( baseUrl, textureUrls ) {
-
-		var textureLoader = new THREE.TextureLoader();
-		var textures = [];
-
-		for ( var i = 0; i < textureUrls.length; i ++ ) {
-
-			textures[ i ] = textureLoader.load( baseUrl + textureUrls[ i ], checkLoadingComplete );
-			textures[ i ].mapping = THREE.UVMapping;
-			textures[ i ].name = textureUrls[ i ];
-
-		}
-
-		return textures;
-
-	}
-
-	function createPart( geometry, skinMap ) {
-
-		var materialWireframe = new THREE.MeshLambertMaterial( { color: 0xffaa00, wireframe: true, morphTargets: true, morphNormals: true } );
-		var materialTexture = new THREE.MeshLambertMaterial( { color: 0xffffff, wireframe: false, map: skinMap, morphTargets: true, morphNormals: true } );
-
-		//
-
-		var mesh = new THREE.MorphBlendMesh( geometry, materialTexture );
-		mesh.rotation.y = - Math.PI / 2;
-
-		//
-
-		mesh.materialTexture = materialTexture;
-		mesh.materialWireframe = materialWireframe;
-
-		//
-
-		mesh.autoCreateAnimations( scope.animationFPS );
-
-		return mesh;
-
-	}
-
-	function checkLoadingComplete() {
-
-		scope.loadCounter -= 1;
-		if ( scope.loadCounter === 0 ) 	scope.onLoadComplete();
-
-	}
-
-	function exponentialEaseOut( k ) {
-
-		return k === 1 ? 1 : - Math.pow( 2, - 10 * k ) + 1;
-
-	}
-
-};
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.MD2CharacterComplex = function () {
+
+	var scope = this;
+
+	this.scale = 1;
+
+	// animation parameters
+
+	this.animationFPS = 6;
+	this.transitionFrames = 15;
+
+	// movement model parameters
+
+	this.maxSpeed = 275;
+	this.maxReverseSpeed = - 275;
+
+	this.frontAcceleration = 600;
+	this.backAcceleration = 600;
+
+	this.frontDecceleration = 600;
+
+	this.angularSpeed = 2.5;
+
+	// rig
+
+	this.root = new THREE.Object3D();
+
+	this.meshBody = null;
+	this.meshWeapon = null;
+
+	this.controls = null;
+
+	// skins
+
+	this.skinsBody = [];
+	this.skinsWeapon = [];
+
+	this.weapons = [];
+
+	this.currentSkin = undefined;
+
+	//
+
+	this.onLoadComplete = function () {};
+
+	// internals
+
+	this.meshes = [];
+	this.animations = {};
+
+	this.loadCounter = 0;
+
+	// internal movement control variables
+
+	this.speed = 0;
+	this.bodyOrientation = 0;
+
+	this.walkSpeed = this.maxSpeed;
+	this.crouchSpeed = this.maxSpeed * 0.5;
+
+	// internal animation parameters
+
+	this.activeAnimation = null;
+	this.oldAnimation = null;
+
+	// API
+
+	this.enableShadows = function ( enable ) {
+
+		for ( var i = 0; i < this.meshes.length; i ++ ) {
+
+			this.meshes[ i ].castShadow = enable;
+			this.meshes[ i ].receiveShadow = enable;
+
+		}
+
+	};
+
+	this.setVisible = function ( enable ) {
+
+		for ( var i = 0; i < this.meshes.length; i ++ ) {
+
+			this.meshes[ i ].visible = enable;
+			this.meshes[ i ].visible = enable;
+
+		}
+
+	};
+
+
+	this.shareParts = function ( original ) {
+
+		this.animations = original.animations;
+		this.walkSpeed = original.walkSpeed;
+		this.crouchSpeed = original.crouchSpeed;
+
+		this.skinsBody = original.skinsBody;
+		this.skinsWeapon = original.skinsWeapon;
+
+		// BODY
+
+		var mesh = createPart( original.meshBody.geometry, this.skinsBody[ 0 ] );
+		mesh.scale.set( this.scale, this.scale, this.scale );
+
+		this.root.position.y = original.root.position.y;
+		this.root.add( mesh );
+
+		this.meshBody = mesh;
+
+		this.meshes.push( mesh );
+
+		// WEAPONS
+
+		for ( var i = 0; i < original.weapons.length; i ++ ) {
+
+			var meshWeapon = createPart( original.weapons[ i ].geometry, this.skinsWeapon[ i ] );
+			meshWeapon.scale.set( this.scale, this.scale, this.scale );
+			meshWeapon.visible = false;
+
+			meshWeapon.name = original.weapons[ i ].name;
+
+			this.root.add( meshWeapon );
+
+			this.weapons[ i ] = meshWeapon;
+			this.meshWeapon = meshWeapon;
+
+			this.meshes.push( meshWeapon );
+
+		}
+
+	};
+
+	this.loadParts = function ( config ) {
+
+		this.animations = config.animations;
+		this.walkSpeed = config.walkSpeed;
+		this.crouchSpeed = config.crouchSpeed;
+
+		this.loadCounter = config.weapons.length * 2 + config.skins.length + 1;
+
+		var weaponsTextures = [];
+		for ( var i = 0; i < config.weapons.length; i ++ ) weaponsTextures[ i ] = config.weapons[ i ][ 1 ];
+
+		// SKINS
+
+		this.skinsBody = loadTextures( config.baseUrl + "skins/", config.skins );
+		this.skinsWeapon = loadTextures( config.baseUrl + "skins/", weaponsTextures );
+
+		// BODY
+
+		var loader = new THREE.MD2Loader();
+
+		loader.load( config.baseUrl + config.body, function( geo ) {
+
+			geo.computeBoundingBox();
+			scope.root.position.y = - scope.scale * geo.boundingBox.min.y;
+
+			var mesh = createPart( geo, scope.skinsBody[ 0 ] );
+			mesh.scale.set( scope.scale, scope.scale, scope.scale );
+
+			scope.root.add( mesh );
+
+			scope.meshBody = mesh;
+			scope.meshes.push( mesh );
+
+			checkLoadingComplete();
+
+		} );
+
+		// WEAPONS
+
+		var generateCallback = function ( index, name ) {
+
+			return function( geo ) {
+
+				var mesh = createPart( geo, scope.skinsWeapon[ index ] );
+				mesh.scale.set( scope.scale, scope.scale, scope.scale );
+				mesh.visible = false;
+
+				mesh.name = name;
+
+				scope.root.add( mesh );
+
+				scope.weapons[ index ] = mesh;
+				scope.meshWeapon = mesh;
+				scope.meshes.push( mesh );
+
+				checkLoadingComplete();
+
+			}
+
+		};
+
+		for ( var i = 0; i < config.weapons.length; i ++ ) {
+
+			loader.load( config.baseUrl + config.weapons[ i ][ 0 ], generateCallback( i, config.weapons[ i ][ 0 ] ) );
+
+		}
+
+	};
+
+	this.setPlaybackRate = function ( rate ) {
+
+		if ( this.meshBody ) this.meshBody.duration = this.meshBody.baseDuration / rate;
+		if ( this.meshWeapon ) this.meshWeapon.duration = this.meshWeapon.baseDuration / rate;
+
+	};
+
+	this.setWireframe = function ( wireframeEnabled ) {
+
+		if ( wireframeEnabled ) {
+
+			if ( this.meshBody ) this.meshBody.material = this.meshBody.materialWireframe;
+			if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialWireframe;
+
+		} else {
+
+			if ( this.meshBody ) this.meshBody.material = this.meshBody.materialTexture;
+			if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialTexture;
+
+		}
+
+	};
+
+	this.setSkin = function( index ) {
+
+		if ( this.meshBody && this.meshBody.material.wireframe === false ) {
+
+			this.meshBody.material.map = this.skinsBody[ index ];
+			this.currentSkin = index;
+
+		}
+
+	};
+
+	this.setWeapon = function ( index ) {
+
+		for ( var i = 0; i < this.weapons.length; i ++ ) this.weapons[ i ].visible = false;
+
+		var activeWeapon = this.weapons[ index ];
+
+		if ( activeWeapon ) {
+
+			activeWeapon.visible = true;
+			this.meshWeapon = activeWeapon;
+
+			if ( this.activeAnimation ) {
+
+				activeWeapon.playAnimation( this.activeAnimation );
+				this.meshWeapon.setAnimationTime( this.activeAnimation, this.meshBody.getAnimationTime( this.activeAnimation ) );
+
+			}
+
+		}
+
+	};
+
+	this.setAnimation = function ( animationName ) {
+
+		if ( animationName === this.activeAnimation || ! animationName ) return;
+
+		if ( this.meshBody ) {
+
+			this.meshBody.setAnimationWeight( animationName, 0 );
+			this.meshBody.playAnimation( animationName );
+
+			this.oldAnimation = this.activeAnimation;
+			this.activeAnimation = animationName;
+
+			this.blendCounter = this.transitionFrames;
+
+		}
+
+		if ( this.meshWeapon ) {
+
+			this.meshWeapon.setAnimationWeight( animationName, 0 );
+			this.meshWeapon.playAnimation( animationName );
+
+		}
+
+
+	};
+
+	this.update = function ( delta ) {
+
+		if ( this.controls ) this.updateMovementModel( delta );
+
+		if ( this.animations ) {
+
+			this.updateBehaviors( delta );
+			this.updateAnimations( delta );
+
+		}
+
+	};
+
+	this.updateAnimations = function ( delta ) {
+
+		var mix = 1;
+
+		if ( this.blendCounter > 0 ) {
+
+			mix = ( this.transitionFrames - this.blendCounter ) / this.transitionFrames;
+			this.blendCounter -= 1;
+
+		}
+
+		if ( this.meshBody ) {
+
+			this.meshBody.update( delta );
+
+			this.meshBody.setAnimationWeight( this.activeAnimation, mix );
+			this.meshBody.setAnimationWeight( this.oldAnimation,  1 - mix );
+
+		}
+
+		if ( this.meshWeapon ) {
+
+			this.meshWeapon.update( delta );
+
+			this.meshWeapon.setAnimationWeight( this.activeAnimation, mix );
+			this.meshWeapon.setAnimationWeight( this.oldAnimation,  1 - mix );
+
+		}
+
+	};
+
+	this.updateBehaviors = function ( delta ) {
+
+		var controls = this.controls;
+		var animations = this.animations;
+
+		var moveAnimation, idleAnimation;
+
+		// crouch vs stand
+
+		if ( controls.crouch ) {
+
+			moveAnimation = animations[ "crouchMove" ];
+			idleAnimation = animations[ "crouchIdle" ];
+
+		} else {
+
+			moveAnimation = animations[ "move" ];
+			idleAnimation = animations[ "idle" ];
+
+		}
+
+		// actions
+
+		if ( controls.jump ) {
+
+			moveAnimation = animations[ "jump" ];
+			idleAnimation = animations[ "jump" ];
+
+		}
+
+		if ( controls.attack ) {
+
+			if ( controls.crouch ) {
+
+				moveAnimation = animations[ "crouchAttack" ];
+				idleAnimation = animations[ "crouchAttack" ];
+
+			} else {
+
+				moveAnimation = animations[ "attack" ];
+				idleAnimation = animations[ "attack" ];
+
+			}
+
+		}
+
+		// set animations
+
+		if ( controls.moveForward || controls.moveBackward || controls.moveLeft || controls.moveRight ) {
+
+			if ( this.activeAnimation !== moveAnimation ) {
+
+				this.setAnimation( moveAnimation );
+
+			}
+
+		}
+
+
+		if ( Math.abs( this.speed ) < 0.2 * this.maxSpeed && ! ( controls.moveLeft || controls.moveRight || controls.moveForward || controls.moveBackward ) ) {
+
+			if ( this.activeAnimation !== idleAnimation ) {
+
+				this.setAnimation( idleAnimation );
+
+			}
+
+		}
+
+		// set animation direction
+
+		if ( controls.moveForward ) {
+
+			if ( this.meshBody ) {
+
+				this.meshBody.setAnimationDirectionForward( this.activeAnimation );
+				this.meshBody.setAnimationDirectionForward( this.oldAnimation );
+
+			}
+
+			if ( this.meshWeapon ) {
+
+				this.meshWeapon.setAnimationDirectionForward( this.activeAnimation );
+				this.meshWeapon.setAnimationDirectionForward( this.oldAnimation );
+
+			}
+
+		}
+
+		if ( controls.moveBackward ) {
+
+			if ( this.meshBody ) {
+
+				this.meshBody.setAnimationDirectionBackward( this.activeAnimation );
+				this.meshBody.setAnimationDirectionBackward( this.oldAnimation );
+
+			}
+
+			if ( this.meshWeapon ) {
+
+				this.meshWeapon.setAnimationDirectionBackward( this.activeAnimation );
+				this.meshWeapon.setAnimationDirectionBackward( this.oldAnimation );
+
+			}
+
+		}
+
+	};
+
+	this.updateMovementModel = function ( delta ) {
+
+		var controls = this.controls;
+
+		// speed based on controls
+
+		if ( controls.crouch ) 	this.maxSpeed = this.crouchSpeed;
+		else this.maxSpeed = this.walkSpeed;
+
+		this.maxReverseSpeed = - this.maxSpeed;
+
+		if ( controls.moveForward )  this.speed = THREE.Math.clamp( this.speed + delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );
+		if ( controls.moveBackward ) this.speed = THREE.Math.clamp( this.speed - delta * this.backAcceleration, this.maxReverseSpeed, this.maxSpeed );
+
+		// orientation based on controls
+		// (don't just stand while turning)
+
+		var dir = 1;
+
+		if ( controls.moveLeft ) {
+
+			this.bodyOrientation += delta * this.angularSpeed;
+			this.speed = THREE.Math.clamp( this.speed + dir * delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );
+
+		}
+
+		if ( controls.moveRight ) {
+
+			this.bodyOrientation -= delta * this.angularSpeed;
+			this.speed = THREE.Math.clamp( this.speed + dir * delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );
+
+		}
+
+		// speed decay
+
+		if ( ! ( controls.moveForward || controls.moveBackward ) ) {
+
+			if ( this.speed > 0 ) {
+
+				var k = exponentialEaseOut( this.speed / this.maxSpeed );
+				this.speed = THREE.Math.clamp( this.speed - k * delta * this.frontDecceleration, 0, this.maxSpeed );
+
+			} else {
+
+				var k = exponentialEaseOut( this.speed / this.maxReverseSpeed );
+				this.speed = THREE.Math.clamp( this.speed + k * delta * this.backAcceleration, this.maxReverseSpeed, 0 );
+
+			}
+
+		}
+
+		// displacement
+
+		var forwardDelta = this.speed * delta;
+
+		this.root.position.x += Math.sin( this.bodyOrientation ) * forwardDelta;
+		this.root.position.z += Math.cos( this.bodyOrientation ) * forwardDelta;
+
+		// steering
+
+		this.root.rotation.y = this.bodyOrientation;
+
+	};
+
+	// internal helpers
+
+	function loadTextures( baseUrl, textureUrls ) {
+
+		var textureLoader = new THREE.TextureLoader();
+		var textures = [];
+
+		for ( var i = 0; i < textureUrls.length; i ++ ) {
+
+			textures[ i ] = textureLoader.load( baseUrl + textureUrls[ i ], checkLoadingComplete );
+			textures[ i ].mapping = THREE.UVMapping;
+			textures[ i ].name = textureUrls[ i ];
+
+		}
+
+		return textures;
+
+	}
+
+	function createPart( geometry, skinMap ) {
+
+		var materialWireframe = new THREE.MeshLambertMaterial( { color: 0xffaa00, wireframe: true, morphTargets: true, morphNormals: true } );
+		var materialTexture = new THREE.MeshLambertMaterial( { color: 0xffffff, wireframe: false, map: skinMap, morphTargets: true, morphNormals: true } );
+
+		//
+
+		var mesh = new THREE.MorphBlendMesh( geometry, materialTexture );
+		mesh.rotation.y = - Math.PI / 2;
+
+		//
+
+		mesh.materialTexture = materialTexture;
+		mesh.materialWireframe = materialWireframe;
+
+		//
+
+		mesh.autoCreateAnimations( scope.animationFPS );
+
+		return mesh;
+
+	}
+
+	function checkLoadingComplete() {
+
+		scope.loadCounter -= 1;
+		if ( scope.loadCounter === 0 ) 	scope.onLoadComplete();
+
+	}
+
+	function exponentialEaseOut( k ) {
+
+		return k === 1 ? 1 : - Math.pow( 2, - 10 * k ) + 1;
+
+	}
+
+};

+ 1044 - 1044
examples/js/MarchingCubes.js

@@ -1,1044 +1,1044 @@
-/**
- * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com
- * Port of http://webglsamples.org/blob/blob.html
- */
-
-THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors ) {
-
-	THREE.ImmediateRenderObject.call( this, material );
-
-	var scope = this;
-
-	// temp buffers used in polygonize
-
-	var vlist = new Float32Array( 12 * 3 );
-	var nlist = new Float32Array( 12 * 3 );
-
-	this.enableUvs = enableUvs !== undefined ? enableUvs : false;
-	this.enableColors = enableColors !== undefined ? enableColors : false;
-
-	// functions have to be object properties
-	// prototype functions kill performance
-	// (tested and it was 4x slower !!!)
-
-	this.init = function ( resolution ) {
-
-		this.resolution = resolution;
-
-		// parameters
-
-		this.isolation = 80.0;
-
-		// size of field, 32 is pushing it in Javascript :)
-
-		this.size = resolution;
-		this.size2 = this.size * this.size;
-		this.size3 = this.size2 * this.size;
-		this.halfsize = this.size / 2.0;
-
-		// deltas
-
-		this.delta = 2.0 / this.size;
-		this.yd = this.size;
-		this.zd = this.size2;
-
-		this.field = new Float32Array( this.size3 );
-		this.normal_cache = new Float32Array( this.size3 * 3 );
-
-		// immediate render mode simulator
-
-		this.maxCount = 4096; // TODO: find the fastest size for this buffer
-		this.count = 0;
-
-		this.hasPositions = false;
-		this.hasNormals = false;
-		this.hasColors = false;
-		this.hasUvs = false;
-
-		this.positionArray = new Float32Array( this.maxCount * 3 );
-		this.normalArray   = new Float32Array( this.maxCount * 3 );
-
-		if ( this.enableUvs ) {
-
-			this.uvArray = new Float32Array( this.maxCount * 2 );
-
-		}
-
-		if ( this.enableColors ) {
-
-			this.colorArray   = new Float32Array( this.maxCount * 3 );
-
-		}
-
-	};
-
-	///////////////////////
-	// Polygonization
-	///////////////////////
-
-	function lerp( a, b, t ) {
-
-		return a + ( b - a ) * t;
-
-	}
-
-	function VIntX( q, offset, isol, x, y, z, valp1, valp2 ) {
-
-		var mu = ( isol - valp1 ) / ( valp2 - valp1 ),
-		nc = scope.normal_cache;
-
-		vlist[ offset + 0 ] = x + mu * scope.delta;
-		vlist[ offset + 1 ] = y;
-		vlist[ offset + 2 ] = z;
-
-		nlist[ offset + 0 ] = lerp( nc[ q + 0 ], nc[ q + 3 ], mu );
-		nlist[ offset + 1 ] = lerp( nc[ q + 1 ], nc[ q + 4 ], mu );
-		nlist[ offset + 2 ] = lerp( nc[ q + 2 ], nc[ q + 5 ], mu );
-
-	}
-
-	function VIntY( q, offset, isol, x, y, z, valp1, valp2 ) {
-
-		var mu = ( isol - valp1 ) / ( valp2 - valp1 ),
-		nc = scope.normal_cache;
-
-		vlist[ offset + 0 ] = x;
-		vlist[ offset + 1 ] = y + mu * scope.delta;
-		vlist[ offset + 2 ] = z;
-
-		var q2 = q + scope.yd * 3;
-
-		nlist[ offset + 0 ] = lerp( nc[ q + 0 ], nc[ q2 + 0 ], mu );
-		nlist[ offset + 1 ] = lerp( nc[ q + 1 ], nc[ q2 + 1 ], mu );
-		nlist[ offset + 2 ] = lerp( nc[ q + 2 ], nc[ q2 + 2 ], mu );
-
-	}
-
-	function VIntZ( q, offset, isol, x, y, z, valp1, valp2 ) {
-
-		var mu = ( isol - valp1 ) / ( valp2 - valp1 ),
-		nc = scope.normal_cache;
-
-		vlist[ offset + 0 ] = x;
-		vlist[ offset + 1 ] = y;
-		vlist[ offset + 2 ] = z + mu * scope.delta;
-
-		var q2 = q + scope.zd * 3;
-
-		nlist[ offset + 0 ] = lerp( nc[ q + 0 ], nc[ q2 + 0 ], mu );
-		nlist[ offset + 1 ] = lerp( nc[ q + 1 ], nc[ q2 + 1 ], mu );
-		nlist[ offset + 2 ] = lerp( nc[ q + 2 ], nc[ q2 + 2 ], mu );
-
-	}
-
-	function compNorm( q ) {
-
-		var q3 = q * 3;
-
-		if ( scope.normal_cache[ q3 ] === 0.0 ) {
-
-			scope.normal_cache[ q3 + 0 ] = scope.field[ q - 1 ] 	    - scope.field[ q + 1 ];
-			scope.normal_cache[ q3 + 1 ] = scope.field[ q - scope.yd ] - scope.field[ q + scope.yd ];
-			scope.normal_cache[ q3 + 2 ] = scope.field[ q - scope.zd ] - scope.field[ q + scope.zd ];
-
-		}
-
-	}
-
-	// Returns total number of triangles. Fills triangles.
-	// (this is where most of time is spent - it's inner work of O(n3) loop )
-
-	function polygonize( fx, fy, fz, q, isol, renderCallback ) {
-
-		// cache indices
-		var q1 = q + 1,
-			qy = q + scope.yd,
-			qz = q + scope.zd,
-			q1y = q1 + scope.yd,
-			q1z = q1 + scope.zd,
-			qyz = q + scope.yd + scope.zd,
-			q1yz = q1 + scope.yd + scope.zd;
-
-		var cubeindex = 0,
-			field0 = scope.field[ q ],
-			field1 = scope.field[ q1 ],
-			field2 = scope.field[ qy ],
-			field3 = scope.field[ q1y ],
-			field4 = scope.field[ qz ],
-			field5 = scope.field[ q1z ],
-			field6 = scope.field[ qyz ],
-			field7 = scope.field[ q1yz ];
-
-		if ( field0 < isol ) cubeindex |= 1;
-		if ( field1 < isol ) cubeindex |= 2;
-		if ( field2 < isol ) cubeindex |= 8;
-		if ( field3 < isol ) cubeindex |= 4;
-		if ( field4 < isol ) cubeindex |= 16;
-		if ( field5 < isol ) cubeindex |= 32;
-		if ( field6 < isol ) cubeindex |= 128;
-		if ( field7 < isol ) cubeindex |= 64;
-
-		// if cube is entirely in/out of the surface - bail, nothing to draw
-
-		var bits = THREE.edgeTable[ cubeindex ];
-		if ( bits === 0 ) return 0;
-
-		var d = scope.delta,
-			fx2 = fx + d,
-			fy2 = fy + d,
-			fz2 = fz + d;
-
-		// top of the cube
-
-		if ( bits & 1 ) {
-
-			compNorm( q );
-			compNorm( q1 );
-			VIntX( q * 3, 0, isol, fx, fy, fz, field0, field1 );
-
-		}
-
-		if ( bits & 2 ) {
-
-			compNorm( q1 );
-			compNorm( q1y );
-			VIntY( q1 * 3, 3, isol, fx2, fy, fz, field1, field3 );
-
-		}
-
-		if ( bits & 4 ) {
-
-			compNorm( qy );
-			compNorm( q1y );
-			VIntX( qy * 3, 6, isol, fx, fy2, fz, field2, field3 );
-
-		}
-
-		if ( bits & 8 ) {
-
-			compNorm( q );
-			compNorm( qy );
-			VIntY( q * 3, 9, isol, fx, fy, fz, field0, field2 );
-
-		}
-
-		// bottom of the cube
-
-		if ( bits & 16 ) {
-
-			compNorm( qz );
-			compNorm( q1z );
-			VIntX( qz * 3, 12, isol, fx, fy, fz2, field4, field5 );
-
-		}
-
-		if ( bits & 32 ) {
-
-			compNorm( q1z );
-			compNorm( q1yz );
-			VIntY( q1z * 3, 15, isol, fx2, fy, fz2, field5, field7 );
-
-		}
-
-		if ( bits & 64 ) {
-
-			compNorm( qyz );
-			compNorm( q1yz );
-			VIntX( qyz * 3, 18, isol, fx, fy2, fz2, field6, field7 );
-
-		}
-
-		if ( bits & 128 ) {
-
-			compNorm( qz );
-			compNorm( qyz );
-			VIntY( qz * 3, 21, isol, fx, fy, fz2, field4, field6 );
-
-		}
-
-		// vertical lines of the cube
-
-		if ( bits & 256 ) {
-
-			compNorm( q );
-			compNorm( qz );
-			VIntZ( q * 3, 24, isol, fx, fy, fz, field0, field4 );
-
-		}
-
-		if ( bits & 512 ) {
-
-			compNorm( q1 );
-			compNorm( q1z );
-			VIntZ( q1 * 3, 27, isol, fx2, fy,  fz, field1, field5 );
-
-		}
-
-		if ( bits & 1024 ) {
-
-			compNorm( q1y );
-			compNorm( q1yz );
-			VIntZ( q1y * 3, 30, isol, fx2, fy2, fz, field3, field7 );
-
-		}
-
-		if ( bits & 2048 ) {
-
-			compNorm( qy );
-			compNorm( qyz );
-			VIntZ( qy * 3, 33, isol, fx,  fy2, fz, field2, field6 );
-
-		}
-
-		cubeindex <<= 4;  // re-purpose cubeindex into an offset into triTable
-
-		var o1, o2, o3, numtris = 0, i = 0;
-
-		// here is where triangles are created
-
-		while ( THREE.triTable[ cubeindex + i ] != - 1 ) {
-
-			o1 = cubeindex + i;
-			o2 = o1 + 1;
-			o3 = o1 + 2;
-
-			posnormtriv( vlist, nlist,
-				3 * THREE.triTable[ o1 ],
-				3 * THREE.triTable[ o2 ],
-				3 * THREE.triTable[ o3 ],
-				renderCallback );
-
-			i += 3;
-			numtris ++;
-
-		}
-
-		return numtris;
-
-	}
-
-	/////////////////////////////////////
-	// Immediate render mode simulator
-	/////////////////////////////////////
-
-	function posnormtriv( pos, norm, o1, o2, o3, renderCallback ) {
-
-		var c = scope.count * 3;
-
-		// positions
-
-		scope.positionArray[ c + 0 ] = pos[ o1 ];
-		scope.positionArray[ c + 1 ] = pos[ o1 + 1 ];
-		scope.positionArray[ c + 2 ] = pos[ o1 + 2 ];
-
-		scope.positionArray[ c + 3 ] = pos[ o2 ];
-		scope.positionArray[ c + 4 ] = pos[ o2 + 1 ];
-		scope.positionArray[ c + 5 ] = pos[ o2 + 2 ];
-
-		scope.positionArray[ c + 6 ] = pos[ o3 ];
-		scope.positionArray[ c + 7 ] = pos[ o3 + 1 ];
-		scope.positionArray[ c + 8 ] = pos[ o3 + 2 ];
-
-		// normals
-
-		scope.normalArray[ c + 0 ] = norm[ o1 ];
-		scope.normalArray[ c + 1 ] = norm[ o1 + 1 ];
-		scope.normalArray[ c + 2 ] = norm[ o1 + 2 ];
-
-		scope.normalArray[ c + 3 ] = norm[ o2 ];
-		scope.normalArray[ c + 4 ] = norm[ o2 + 1 ];
-		scope.normalArray[ c + 5 ] = norm[ o2 + 2 ];
-
-		scope.normalArray[ c + 6 ] = norm[ o3 ];
-		scope.normalArray[ c + 7 ] = norm[ o3 + 1 ];
-		scope.normalArray[ c + 8 ] = norm[ o3 + 2 ];
-
-		// uvs
-
-		if ( scope.enableUvs ) {
-
-			var d = scope.count * 2;
-
-			scope.uvArray[ d + 0 ] = pos[ o1 ];
-			scope.uvArray[ d + 1 ] = pos[ o1 + 2 ];
-
-			scope.uvArray[ d + 2 ] = pos[ o2 ];
-			scope.uvArray[ d + 3 ] = pos[ o2 + 2 ];
-
-			scope.uvArray[ d + 4 ] = pos[ o3 ];
-			scope.uvArray[ d + 5 ] = pos[ o3 + 2 ];
-
-		}
-
-		// colors
-
-		if ( scope.enableColors ) {
-
-			scope.colorArray[ c + 0 ] = pos[ o1 ];
-			scope.colorArray[ c + 1 ] = pos[ o1 + 1 ];
-			scope.colorArray[ c + 2 ] = pos[ o1 + 2 ];
-
-			scope.colorArray[ c + 3 ] = pos[ o2 ];
-			scope.colorArray[ c + 4 ] = pos[ o2 + 1 ];
-			scope.colorArray[ c + 5 ] = pos[ o2 + 2 ];
-
-			scope.colorArray[ c + 6 ] = pos[ o3 ];
-			scope.colorArray[ c + 7 ] = pos[ o3 + 1 ];
-			scope.colorArray[ c + 8 ] = pos[ o3 + 2 ];
-
-		}
-
-		scope.count += 3;
-
-		if ( scope.count >= scope.maxCount - 3 ) {
-
-			scope.hasPositions = true;
-			scope.hasNormals = true;
-
-			if ( scope.enableUvs ) {
-
-				scope.hasUvs = true;
-
-			}
-
-			if ( scope.enableColors ) {
-
-				scope.hasColors = true;
-
-			}
-
-			renderCallback( scope );
-
-		}
-
-	}
-
-	this.begin = function () {
-
-		this.count = 0;
-
-		this.hasPositions = false;
-		this.hasNormals = false;
-		this.hasUvs = false;
-		this.hasColors = false;
-
-	};
-
-	this.end = function ( renderCallback ) {
-
-		if ( this.count === 0 ) return;
-
-		for ( var i = this.count * 3; i < this.positionArray.length; i ++ ) {
-
-			this.positionArray[ i ] = 0.0;
-
-		}
-
-		this.hasPositions = true;
-		this.hasNormals = true;
-
-		if ( this.enableUvs ) {
-
-			this.hasUvs = true;
-
-		}
-
-		if ( this.enableColors ) {
-
-			this.hasColors = true;
-
-		}
-
-		renderCallback( this );
-
-	};
-
-	/////////////////////////////////////
-	// Metaballs
-	/////////////////////////////////////
-
-	// Adds a reciprocal ball (nice and blobby) that, to be fast, fades to zero after
-	// a fixed distance, determined by strength and subtract.
-
-	this.addBall = function ( ballx, bally, ballz, strength, subtract ) {
-
-		var sign = Math.sign( strength );
-		strength = Math.abs( strength );
-
-		// Let's solve the equation to find the radius:
-		// 1.0 / (0.000001 + radius^2) * strength - subtract = 0
-		// strength / (radius^2) = subtract
-		// strength = subtract * radius^2
-		// radius^2 = strength / subtract
-		// radius = sqrt(strength / subtract)
-
-		var radius = this.size * Math.sqrt( strength / subtract ),
-			zs = ballz * this.size,
-			ys = bally * this.size,
-			xs = ballx * this.size;
-
-		var min_z = Math.floor( zs - radius ); if ( min_z < 1 ) min_z = 1;
-		var max_z = Math.floor( zs + radius ); if ( max_z > this.size - 1 ) max_z = this.size - 1;
-		var min_y = Math.floor( ys - radius ); if ( min_y < 1 ) min_y = 1;
-		var max_y = Math.floor( ys + radius ); if ( max_y > this.size - 1 ) max_y = this.size - 1;
-		var min_x = Math.floor( xs - radius ); if ( min_x < 1  ) min_x = 1;
-		var max_x = Math.floor( xs + radius ); if ( max_x > this.size - 1 ) max_x = this.size - 1;
-
-
-		// Don't polygonize in the outer layer because normals aren't
-		// well-defined there.
-
-		var x, y, z, y_offset, z_offset, fx, fy, fz, fz2, fy2, val;
-
-		for ( z = min_z; z < max_z; z ++ ) {
-
-			z_offset = this.size2 * z;
-			fz = z / this.size - ballz;
-			fz2 = fz * fz;
-
-			for ( y = min_y; y < max_y; y ++ ) {
-
-				y_offset = z_offset + this.size * y;
-				fy = y / this.size - bally;
-				fy2 = fy * fy;
-
-				for ( x = min_x; x < max_x; x ++ ) {
-
-					fx = x / this.size - ballx;
-					val = strength / ( 0.000001 + fx * fx + fy2 + fz2 ) - subtract;
-					if ( val > 0.0 ) this.field[ y_offset + x ] += val * sign;
-
-				}
-
-			}
-
-		}
-
-	};
-
-	this.addPlaneX = function( strength, subtract ) {
-
-		var x, y, z, xx, val, xdiv, cxy,
-
-			// cache attribute lookups
-			size = this.size,
-			yd = this.yd,
-			zd = this.zd,
-			field = this.field,
-
-			dist = size * Math.sqrt( strength / subtract );
-
-		if ( dist > size ) dist = size;
-
-		for ( x = 0; x < dist; x ++ ) {
-
-			xdiv = x / size;
-			xx = xdiv * xdiv;
-			val = strength / ( 0.0001 + xx ) - subtract;
-
-			if ( val > 0.0 ) {
-
-				for ( y = 0; y < size; y ++ ) {
-
-					cxy = x + y * yd;
-
-					for ( z = 0; z < size; z ++ ) {
-
-						field[ zd * z + cxy ] += val;
-
-					}
-
-				}
-
-			}
-
-		}
-
-	};
-
-	this.addPlaneY = function( strength, subtract ) {
-
-		var x, y, z, yy, val, ydiv, cy, cxy,
-
-			// cache attribute lookups
-			size = this.size,
-			yd = this.yd,
-			zd = this.zd,
-			field = this.field,
-
-			dist = size * Math.sqrt( strength / subtract );
-
-		if ( dist > size ) dist = size;
-
-		for ( y = 0; y < dist; y ++ ) {
-
-			ydiv = y / size;
-			yy = ydiv * ydiv;
-			val = strength / ( 0.0001 + yy ) - subtract;
-
-			if ( val > 0.0 ) {
-
-				cy = y * yd;
-
-				for ( x = 0; x < size; x ++ ) {
-
-					cxy = cy + x;
-
-					for ( z = 0; z < size; z ++ )
-						field[ zd * z + cxy ] += val;
-
-				}
-
-			}
-
-		}
-
-	};
-
-	this.addPlaneZ = function( strength, subtract ) {
-
-		var x, y, z, zz, val, zdiv, cz, cyz,
-
-			// cache attribute lookups
-			size = this.size,
-			yd = this.yd,
-			zd = this.zd,
-			field = this.field,
-
-			dist = size * Math.sqrt( strength / subtract );
-
-		if ( dist > size ) dist = size;
-
-		for ( z = 0; z < dist; z ++ ) {
-
-			zdiv = z / size;
-			zz = zdiv * zdiv;
-			val = strength / ( 0.0001 + zz ) - subtract;
-			if ( val > 0.0 ) {
-
-				cz = zd * z;
-
-				for ( y = 0; y < size; y ++ ) {
-
-					cyz = cz + y * yd;
-
-					for ( x = 0; x < size; x ++ )
-						field[ cyz + x ] += val;
-
-				}
-
-			}
-
-		}
-
-	};
-
-	/////////////////////////////////////
-	// Updates
-	/////////////////////////////////////
-
-	this.reset = function () {
-
-		var i;
-
-		// wipe the normal cache
-
-		for ( i = 0; i < this.size3; i ++ ) {
-
-			this.normal_cache[ i * 3 ] = 0.0;
-			this.field[ i ] = 0.0;
-
-		}
-
-	};
-
-	this.render = function ( renderCallback ) {
-
-		this.begin();
-
-		// Triangulate. Yeah, this is slow.
-
-		var smin2 = this.size - 2;
-
-		for ( var z = 1; z < smin2; z ++ ) {
-
-			var z_offset = this.size2 * z;
-			var fz = ( z - this.halfsize ) / this.halfsize; //+ 1
-
-			for ( var y = 1; y < smin2; y ++ ) {
-
-				var y_offset = z_offset + this.size * y;
-				var fy = ( y - this.halfsize ) / this.halfsize; //+ 1
-
-				for ( var x = 1; x < smin2; x ++ ) {
-
-					var fx = ( x - this.halfsize ) / this.halfsize; //+ 1
-					var q = y_offset + x;
-
-					polygonize( fx, fy, fz, q, this.isolation, renderCallback );
-
-				}
-
-			}
-
-		}
-
-		this.end( renderCallback );
-
-	};
-
-	this.generateGeometry = function() {
-
-		var start = 0, geo = new THREE.Geometry();
-		var normals = [];
-
-		var geo_callback = function( object ) {
-
-			for ( var i = 0; i < object.count; i ++ ) {
-
-				var vertex = new THREE.Vector3().fromArray( object.positionArray, i * 3 );
-				var normal = new THREE.Vector3().fromArray( object.normalArray, i * 3 );
-
-				geo.vertices.push( vertex );
-				normals.push( normal );
-
-			}
-
-			var nfaces = object.count / 3;
-
-			for ( i = 0; i < nfaces; i ++ ) {
-
-				var a = ( start + i ) * 3;
-				var b = a + 1;
-				var c = a + 2;
-
-				var na = normals[ a ];
-				var nb = normals[ b ];
-				var nc = normals[ c ];
-
-				var face = new THREE.Face3( a, b, c, [ na, nb, nc ] );
-				geo.faces.push( face );
-
-			}
-
-			start += nfaces;
-			object.count = 0;
-
-		};
-
-		this.render( geo_callback );
-
-		// console.log( "generated " + geo.faces.length + " triangles" );
-
-		return geo;
-
-	};
-
-	this.init( resolution );
-
-};
-
-THREE.MarchingCubes.prototype = Object.create( THREE.ImmediateRenderObject.prototype );
-THREE.MarchingCubes.prototype.constructor = THREE.MarchingCubes;
-
-
-/////////////////////////////////////
-// Marching cubes lookup tables
-/////////////////////////////////////
-
-// These tables are straight from Paul Bourke's page:
-// http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/
-// who in turn got them from Cory Gene Bloyd.
-
-THREE.edgeTable = new Int32Array( [
-0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
-0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
-0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
-0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
-0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c,
-0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
-0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac,
-0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
-0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c,
-0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
-0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc,
-0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
-0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c,
-0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
-0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc,
-0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
-0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
-0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
-0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
-0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
-0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
-0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
-0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
-0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460,
-0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
-0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0,
-0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
-0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230,
-0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
-0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190,
-0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
-0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 ] );
-
-THREE.triTable = new Int32Array( [
-- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 8, 3, 9, 8, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 3, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 2, 10, 0, 2, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 8, 3, 2, 10, 8, 10, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 11, 2, 8, 11, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 9, 0, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 11, 2, 1, 9, 11, 9, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 10, 1, 11, 10, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 10, 1, 0, 8, 10, 8, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 9, 0, 3, 11, 9, 11, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 3, 0, 7, 3, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 1, 9, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 1, 9, 4, 7, 1, 7, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 10, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 4, 7, 3, 0, 4, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 2, 10, 9, 0, 2, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1,
-8, 4, 7, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-11, 4, 7, 11, 2, 4, 2, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 0, 1, 8, 4, 7, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, - 1, - 1, - 1, - 1,
-3, 10, 1, 3, 11, 10, 7, 8, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, - 1, - 1, - 1, - 1,
-4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1,
-4, 7, 11, 4, 11, 9, 9, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 5, 4, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 5, 4, 1, 5, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 5, 4, 8, 3, 5, 3, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 10, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 0, 8, 1, 2, 10, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 2, 10, 5, 4, 2, 4, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, - 1, - 1, - 1, - 1,
-9, 5, 4, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 11, 2, 0, 8, 11, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 5, 4, 0, 1, 5, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, - 1, - 1, - 1, - 1,
-10, 3, 11, 10, 1, 3, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, - 1, - 1, - 1, - 1,
-5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1,
-5, 4, 8, 5, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 7, 8, 5, 7, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 3, 0, 9, 5, 3, 5, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 7, 8, 0, 1, 7, 1, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 7, 8, 9, 5, 7, 10, 1, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, - 1, - 1, - 1, - 1,
-8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, - 1, - 1, - 1, - 1,
-2, 10, 5, 2, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-7, 9, 5, 7, 8, 9, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1,
-2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, - 1, - 1, - 1, - 1,
-11, 2, 1, 11, 1, 7, 7, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, - 1, - 1, - 1, - 1,
-5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, - 1,
-11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, - 1,
-11, 10, 5, 7, 11, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 3, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 0, 1, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 8, 3, 1, 9, 8, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 6, 5, 2, 6, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 6, 5, 1, 2, 6, 3, 0, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 6, 5, 9, 0, 6, 0, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, - 1, - 1, - 1, - 1,
-2, 3, 11, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-11, 0, 8, 11, 2, 0, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 1, 9, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, - 1, - 1, - 1, - 1,
-6, 3, 11, 6, 5, 3, 5, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1,
-3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, - 1, - 1, - 1, - 1,
-6, 5, 9, 6, 9, 11, 11, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 10, 6, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 3, 0, 4, 7, 3, 6, 5, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 9, 0, 5, 10, 6, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1,
-6, 1, 2, 6, 5, 1, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, - 1, - 1, - 1, - 1,
-8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, - 1, - 1, - 1, - 1,
-7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, - 1,
-3, 11, 2, 7, 8, 4, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1,
-0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1,
-9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, - 1,
-8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1,
-5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, - 1,
-0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, - 1,
-6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, - 1, - 1, - 1, - 1,
-10, 4, 9, 6, 4, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 10, 6, 4, 9, 10, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 0, 1, 10, 6, 0, 6, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1,
-1, 4, 9, 1, 2, 4, 2, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, - 1, - 1, - 1, - 1,
-0, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 3, 2, 8, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 4, 9, 10, 6, 4, 11, 2, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, - 1, - 1, - 1, - 1,
-3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1,
-6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, - 1,
-9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, - 1, - 1, - 1, - 1,
-8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, - 1,
-3, 11, 6, 3, 6, 0, 0, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-6, 4, 8, 11, 6, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-7, 10, 6, 7, 8, 10, 8, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, - 1, - 1, - 1, - 1,
-10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, - 1, - 1, - 1, - 1,
-10, 6, 7, 10, 7, 1, 1, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1,
-2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, - 1,
-7, 8, 0, 7, 0, 6, 6, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-7, 3, 2, 6, 7, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1,
-2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, - 1,
-1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, - 1,
-11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, - 1, - 1, - 1, - 1,
-8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, - 1,
-0, 9, 1, 11, 6, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, - 1, - 1, - 1, - 1,
-7, 11, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 0, 8, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 1, 9, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 1, 9, 8, 3, 1, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 1, 2, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 10, 3, 0, 8, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 9, 0, 2, 10, 9, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, - 1, - 1, - 1, - 1,
-7, 2, 3, 6, 2, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-7, 0, 8, 7, 6, 0, 6, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 7, 6, 2, 3, 7, 0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, - 1, - 1, - 1, - 1,
-10, 7, 6, 10, 1, 7, 1, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, - 1, - 1, - 1, - 1,
-0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, - 1, - 1, - 1, - 1,
-7, 6, 10, 7, 10, 8, 8, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-6, 8, 4, 11, 8, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 6, 11, 3, 0, 6, 0, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 6, 11, 8, 4, 6, 9, 0, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, - 1, - 1, - 1, - 1,
-6, 8, 4, 6, 11, 8, 2, 10, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, - 1, - 1, - 1, - 1,
-4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, - 1, - 1, - 1, - 1,
-10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, - 1,
-8, 2, 3, 8, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, - 1, - 1, - 1, - 1,
-1, 9, 4, 1, 4, 2, 2, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, - 1, - 1, - 1, - 1,
-10, 1, 0, 10, 0, 6, 6, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, - 1,
-10, 9, 4, 6, 10, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 9, 5, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 3, 4, 9, 5, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 0, 1, 5, 4, 0, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, - 1, - 1, - 1, - 1,
-9, 5, 4, 10, 1, 2, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, - 1, - 1, - 1, - 1,
-7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, - 1, - 1, - 1, - 1,
-3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, - 1,
-7, 2, 3, 7, 6, 2, 5, 4, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, - 1, - 1, - 1, - 1,
-3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, - 1, - 1, - 1, - 1,
-6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, - 1,
-9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, - 1, - 1, - 1, - 1,
-1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, - 1,
-4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, - 1,
-7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, - 1, - 1, - 1, - 1,
-6, 9, 5, 6, 11, 9, 11, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, - 1, - 1, - 1, - 1,
-0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, - 1, - 1, - 1, - 1,
-6, 11, 3, 6, 3, 5, 5, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, - 1, - 1, - 1, - 1,
-0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, - 1,
-11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, - 1,
-6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, - 1, - 1, - 1, - 1,
-5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, - 1, - 1, - 1, - 1,
-9, 5, 6, 9, 6, 0, 0, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, - 1,
-1, 5, 6, 2, 1, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, - 1,
-10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, - 1, - 1, - 1, - 1,
-0, 3, 8, 5, 6, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 5, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-11, 5, 10, 7, 5, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-11, 5, 10, 11, 7, 5, 8, 3, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 11, 7, 5, 10, 11, 1, 9, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, - 1, - 1, - 1, - 1,
-11, 1, 2, 11, 7, 1, 7, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, - 1, - 1, - 1, - 1,
-9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, - 1, - 1, - 1, - 1,
-7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, - 1,
-2, 5, 10, 2, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, - 1, - 1, - 1, - 1,
-9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, - 1, - 1, - 1, - 1,
-9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, - 1,
-1, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 7, 0, 7, 1, 1, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 0, 3, 9, 3, 5, 5, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 8, 7, 5, 9, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 8, 4, 5, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, - 1, - 1, - 1, - 1,
-0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, - 1, - 1, - 1, - 1,
-10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, - 1,
-2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, - 1, - 1, - 1, - 1,
-0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, - 1,
-0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, - 1,
-9, 4, 5, 2, 11, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, - 1, - 1, - 1, - 1,
-5, 10, 2, 5, 2, 4, 4, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, - 1,
-5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, - 1, - 1, - 1, - 1,
-8, 4, 5, 8, 5, 3, 3, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 4, 5, 1, 0, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, - 1, - 1, - 1, - 1,
-9, 4, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 11, 7, 4, 9, 11, 9, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, - 1, - 1, - 1, - 1,
-1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, - 1, - 1, - 1, - 1,
-3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, - 1,
-4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, - 1, - 1, - 1, - 1,
-9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, - 1,
-11, 7, 4, 11, 4, 2, 2, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, - 1, - 1, - 1, - 1,
-2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, - 1, - 1, - 1, - 1,
-9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, - 1,
-3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, - 1,
-1, 10, 2, 8, 7, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 9, 1, 4, 1, 7, 7, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, - 1, - 1, - 1, - 1,
-4, 0, 3, 7, 4, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 8, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 0, 9, 3, 9, 11, 11, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 1, 10, 0, 10, 8, 8, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 1, 10, 11, 3, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 11, 1, 11, 9, 9, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, - 1, - 1, - 1, - 1,
-0, 2, 11, 8, 0, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 2, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 3, 8, 2, 8, 10, 10, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 10, 2, 0, 9, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, - 1, - 1, - 1, - 1,
-1, 10, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 3, 8, 9, 1, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 9, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 3, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 ] );
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com
+ * Port of http://webglsamples.org/blob/blob.html
+ */
+
+THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors ) {
+
+	THREE.ImmediateRenderObject.call( this, material );
+
+	var scope = this;
+
+	// temp buffers used in polygonize
+
+	var vlist = new Float32Array( 12 * 3 );
+	var nlist = new Float32Array( 12 * 3 );
+
+	this.enableUvs = enableUvs !== undefined ? enableUvs : false;
+	this.enableColors = enableColors !== undefined ? enableColors : false;
+
+	// functions have to be object properties
+	// prototype functions kill performance
+	// (tested and it was 4x slower !!!)
+
+	this.init = function ( resolution ) {
+
+		this.resolution = resolution;
+
+		// parameters
+
+		this.isolation = 80.0;
+
+		// size of field, 32 is pushing it in Javascript :)
+
+		this.size = resolution;
+		this.size2 = this.size * this.size;
+		this.size3 = this.size2 * this.size;
+		this.halfsize = this.size / 2.0;
+
+		// deltas
+
+		this.delta = 2.0 / this.size;
+		this.yd = this.size;
+		this.zd = this.size2;
+
+		this.field = new Float32Array( this.size3 );
+		this.normal_cache = new Float32Array( this.size3 * 3 );
+
+		// immediate render mode simulator
+
+		this.maxCount = 4096; // TODO: find the fastest size for this buffer
+		this.count = 0;
+
+		this.hasPositions = false;
+		this.hasNormals = false;
+		this.hasColors = false;
+		this.hasUvs = false;
+
+		this.positionArray = new Float32Array( this.maxCount * 3 );
+		this.normalArray   = new Float32Array( this.maxCount * 3 );
+
+		if ( this.enableUvs ) {
+
+			this.uvArray = new Float32Array( this.maxCount * 2 );
+
+		}
+
+		if ( this.enableColors ) {
+
+			this.colorArray   = new Float32Array( this.maxCount * 3 );
+
+		}
+
+	};
+
+	///////////////////////
+	// Polygonization
+	///////////////////////
+
+	function lerp( a, b, t ) {
+
+		return a + ( b - a ) * t;
+
+	}
+
+	function VIntX( q, offset, isol, x, y, z, valp1, valp2 ) {
+
+		var mu = ( isol - valp1 ) / ( valp2 - valp1 ),
+		nc = scope.normal_cache;
+
+		vlist[ offset + 0 ] = x + mu * scope.delta;
+		vlist[ offset + 1 ] = y;
+		vlist[ offset + 2 ] = z;
+
+		nlist[ offset + 0 ] = lerp( nc[ q + 0 ], nc[ q + 3 ], mu );
+		nlist[ offset + 1 ] = lerp( nc[ q + 1 ], nc[ q + 4 ], mu );
+		nlist[ offset + 2 ] = lerp( nc[ q + 2 ], nc[ q + 5 ], mu );
+
+	}
+
+	function VIntY( q, offset, isol, x, y, z, valp1, valp2 ) {
+
+		var mu = ( isol - valp1 ) / ( valp2 - valp1 ),
+		nc = scope.normal_cache;
+
+		vlist[ offset + 0 ] = x;
+		vlist[ offset + 1 ] = y + mu * scope.delta;
+		vlist[ offset + 2 ] = z;
+
+		var q2 = q + scope.yd * 3;
+
+		nlist[ offset + 0 ] = lerp( nc[ q + 0 ], nc[ q2 + 0 ], mu );
+		nlist[ offset + 1 ] = lerp( nc[ q + 1 ], nc[ q2 + 1 ], mu );
+		nlist[ offset + 2 ] = lerp( nc[ q + 2 ], nc[ q2 + 2 ], mu );
+
+	}
+
+	function VIntZ( q, offset, isol, x, y, z, valp1, valp2 ) {
+
+		var mu = ( isol - valp1 ) / ( valp2 - valp1 ),
+		nc = scope.normal_cache;
+
+		vlist[ offset + 0 ] = x;
+		vlist[ offset + 1 ] = y;
+		vlist[ offset + 2 ] = z + mu * scope.delta;
+
+		var q2 = q + scope.zd * 3;
+
+		nlist[ offset + 0 ] = lerp( nc[ q + 0 ], nc[ q2 + 0 ], mu );
+		nlist[ offset + 1 ] = lerp( nc[ q + 1 ], nc[ q2 + 1 ], mu );
+		nlist[ offset + 2 ] = lerp( nc[ q + 2 ], nc[ q2 + 2 ], mu );
+
+	}
+
+	function compNorm( q ) {
+
+		var q3 = q * 3;
+
+		if ( scope.normal_cache[ q3 ] === 0.0 ) {
+
+			scope.normal_cache[ q3 + 0 ] = scope.field[ q - 1 ] 	    - scope.field[ q + 1 ];
+			scope.normal_cache[ q3 + 1 ] = scope.field[ q - scope.yd ] - scope.field[ q + scope.yd ];
+			scope.normal_cache[ q3 + 2 ] = scope.field[ q - scope.zd ] - scope.field[ q + scope.zd ];
+
+		}
+
+	}
+
+	// Returns total number of triangles. Fills triangles.
+	// (this is where most of time is spent - it's inner work of O(n3) loop )
+
+	function polygonize( fx, fy, fz, q, isol, renderCallback ) {
+
+		// cache indices
+		var q1 = q + 1,
+			qy = q + scope.yd,
+			qz = q + scope.zd,
+			q1y = q1 + scope.yd,
+			q1z = q1 + scope.zd,
+			qyz = q + scope.yd + scope.zd,
+			q1yz = q1 + scope.yd + scope.zd;
+
+		var cubeindex = 0,
+			field0 = scope.field[ q ],
+			field1 = scope.field[ q1 ],
+			field2 = scope.field[ qy ],
+			field3 = scope.field[ q1y ],
+			field4 = scope.field[ qz ],
+			field5 = scope.field[ q1z ],
+			field6 = scope.field[ qyz ],
+			field7 = scope.field[ q1yz ];
+
+		if ( field0 < isol ) cubeindex |= 1;
+		if ( field1 < isol ) cubeindex |= 2;
+		if ( field2 < isol ) cubeindex |= 8;
+		if ( field3 < isol ) cubeindex |= 4;
+		if ( field4 < isol ) cubeindex |= 16;
+		if ( field5 < isol ) cubeindex |= 32;
+		if ( field6 < isol ) cubeindex |= 128;
+		if ( field7 < isol ) cubeindex |= 64;
+
+		// if cube is entirely in/out of the surface - bail, nothing to draw
+
+		var bits = THREE.edgeTable[ cubeindex ];
+		if ( bits === 0 ) return 0;
+
+		var d = scope.delta,
+			fx2 = fx + d,
+			fy2 = fy + d,
+			fz2 = fz + d;
+
+		// top of the cube
+
+		if ( bits & 1 ) {
+
+			compNorm( q );
+			compNorm( q1 );
+			VIntX( q * 3, 0, isol, fx, fy, fz, field0, field1 );
+
+		}
+
+		if ( bits & 2 ) {
+
+			compNorm( q1 );
+			compNorm( q1y );
+			VIntY( q1 * 3, 3, isol, fx2, fy, fz, field1, field3 );
+
+		}
+
+		if ( bits & 4 ) {
+
+			compNorm( qy );
+			compNorm( q1y );
+			VIntX( qy * 3, 6, isol, fx, fy2, fz, field2, field3 );
+
+		}
+
+		if ( bits & 8 ) {
+
+			compNorm( q );
+			compNorm( qy );
+			VIntY( q * 3, 9, isol, fx, fy, fz, field0, field2 );
+
+		}
+
+		// bottom of the cube
+
+		if ( bits & 16 ) {
+
+			compNorm( qz );
+			compNorm( q1z );
+			VIntX( qz * 3, 12, isol, fx, fy, fz2, field4, field5 );
+
+		}
+
+		if ( bits & 32 ) {
+
+			compNorm( q1z );
+			compNorm( q1yz );
+			VIntY( q1z * 3, 15, isol, fx2, fy, fz2, field5, field7 );
+
+		}
+
+		if ( bits & 64 ) {
+
+			compNorm( qyz );
+			compNorm( q1yz );
+			VIntX( qyz * 3, 18, isol, fx, fy2, fz2, field6, field7 );
+
+		}
+
+		if ( bits & 128 ) {
+
+			compNorm( qz );
+			compNorm( qyz );
+			VIntY( qz * 3, 21, isol, fx, fy, fz2, field4, field6 );
+
+		}
+
+		// vertical lines of the cube
+
+		if ( bits & 256 ) {
+
+			compNorm( q );
+			compNorm( qz );
+			VIntZ( q * 3, 24, isol, fx, fy, fz, field0, field4 );
+
+		}
+
+		if ( bits & 512 ) {
+
+			compNorm( q1 );
+			compNorm( q1z );
+			VIntZ( q1 * 3, 27, isol, fx2, fy,  fz, field1, field5 );
+
+		}
+
+		if ( bits & 1024 ) {
+
+			compNorm( q1y );
+			compNorm( q1yz );
+			VIntZ( q1y * 3, 30, isol, fx2, fy2, fz, field3, field7 );
+
+		}
+
+		if ( bits & 2048 ) {
+
+			compNorm( qy );
+			compNorm( qyz );
+			VIntZ( qy * 3, 33, isol, fx,  fy2, fz, field2, field6 );
+
+		}
+
+		cubeindex <<= 4;  // re-purpose cubeindex into an offset into triTable
+
+		var o1, o2, o3, numtris = 0, i = 0;
+
+		// here is where triangles are created
+
+		while ( THREE.triTable[ cubeindex + i ] != - 1 ) {
+
+			o1 = cubeindex + i;
+			o2 = o1 + 1;
+			o3 = o1 + 2;
+
+			posnormtriv( vlist, nlist,
+				3 * THREE.triTable[ o1 ],
+				3 * THREE.triTable[ o2 ],
+				3 * THREE.triTable[ o3 ],
+				renderCallback );
+
+			i += 3;
+			numtris ++;
+
+		}
+
+		return numtris;
+
+	}
+
+	/////////////////////////////////////
+	// Immediate render mode simulator
+	/////////////////////////////////////
+
+	function posnormtriv( pos, norm, o1, o2, o3, renderCallback ) {
+
+		var c = scope.count * 3;
+
+		// positions
+
+		scope.positionArray[ c + 0 ] = pos[ o1 ];
+		scope.positionArray[ c + 1 ] = pos[ o1 + 1 ];
+		scope.positionArray[ c + 2 ] = pos[ o1 + 2 ];
+
+		scope.positionArray[ c + 3 ] = pos[ o2 ];
+		scope.positionArray[ c + 4 ] = pos[ o2 + 1 ];
+		scope.positionArray[ c + 5 ] = pos[ o2 + 2 ];
+
+		scope.positionArray[ c + 6 ] = pos[ o3 ];
+		scope.positionArray[ c + 7 ] = pos[ o3 + 1 ];
+		scope.positionArray[ c + 8 ] = pos[ o3 + 2 ];
+
+		// normals
+
+		scope.normalArray[ c + 0 ] = norm[ o1 ];
+		scope.normalArray[ c + 1 ] = norm[ o1 + 1 ];
+		scope.normalArray[ c + 2 ] = norm[ o1 + 2 ];
+
+		scope.normalArray[ c + 3 ] = norm[ o2 ];
+		scope.normalArray[ c + 4 ] = norm[ o2 + 1 ];
+		scope.normalArray[ c + 5 ] = norm[ o2 + 2 ];
+
+		scope.normalArray[ c + 6 ] = norm[ o3 ];
+		scope.normalArray[ c + 7 ] = norm[ o3 + 1 ];
+		scope.normalArray[ c + 8 ] = norm[ o3 + 2 ];
+
+		// uvs
+
+		if ( scope.enableUvs ) {
+
+			var d = scope.count * 2;
+
+			scope.uvArray[ d + 0 ] = pos[ o1 ];
+			scope.uvArray[ d + 1 ] = pos[ o1 + 2 ];
+
+			scope.uvArray[ d + 2 ] = pos[ o2 ];
+			scope.uvArray[ d + 3 ] = pos[ o2 + 2 ];
+
+			scope.uvArray[ d + 4 ] = pos[ o3 ];
+			scope.uvArray[ d + 5 ] = pos[ o3 + 2 ];
+
+		}
+
+		// colors
+
+		if ( scope.enableColors ) {
+
+			scope.colorArray[ c + 0 ] = pos[ o1 ];
+			scope.colorArray[ c + 1 ] = pos[ o1 + 1 ];
+			scope.colorArray[ c + 2 ] = pos[ o1 + 2 ];
+
+			scope.colorArray[ c + 3 ] = pos[ o2 ];
+			scope.colorArray[ c + 4 ] = pos[ o2 + 1 ];
+			scope.colorArray[ c + 5 ] = pos[ o2 + 2 ];
+
+			scope.colorArray[ c + 6 ] = pos[ o3 ];
+			scope.colorArray[ c + 7 ] = pos[ o3 + 1 ];
+			scope.colorArray[ c + 8 ] = pos[ o3 + 2 ];
+
+		}
+
+		scope.count += 3;
+
+		if ( scope.count >= scope.maxCount - 3 ) {
+
+			scope.hasPositions = true;
+			scope.hasNormals = true;
+
+			if ( scope.enableUvs ) {
+
+				scope.hasUvs = true;
+
+			}
+
+			if ( scope.enableColors ) {
+
+				scope.hasColors = true;
+
+			}
+
+			renderCallback( scope );
+
+		}
+
+	}
+
+	this.begin = function () {
+
+		this.count = 0;
+
+		this.hasPositions = false;
+		this.hasNormals = false;
+		this.hasUvs = false;
+		this.hasColors = false;
+
+	};
+
+	this.end = function ( renderCallback ) {
+
+		if ( this.count === 0 ) return;
+
+		for ( var i = this.count * 3; i < this.positionArray.length; i ++ ) {
+
+			this.positionArray[ i ] = 0.0;
+
+		}
+
+		this.hasPositions = true;
+		this.hasNormals = true;
+
+		if ( this.enableUvs ) {
+
+			this.hasUvs = true;
+
+		}
+
+		if ( this.enableColors ) {
+
+			this.hasColors = true;
+
+		}
+
+		renderCallback( this );
+
+	};
+
+	/////////////////////////////////////
+	// Metaballs
+	/////////////////////////////////////
+
+	// Adds a reciprocal ball (nice and blobby) that, to be fast, fades to zero after
+	// a fixed distance, determined by strength and subtract.
+
+	this.addBall = function ( ballx, bally, ballz, strength, subtract ) {
+
+		var sign = Math.sign( strength );
+		strength = Math.abs( strength );
+
+		// Let's solve the equation to find the radius:
+		// 1.0 / (0.000001 + radius^2) * strength - subtract = 0
+		// strength / (radius^2) = subtract
+		// strength = subtract * radius^2
+		// radius^2 = strength / subtract
+		// radius = sqrt(strength / subtract)
+
+		var radius = this.size * Math.sqrt( strength / subtract ),
+			zs = ballz * this.size,
+			ys = bally * this.size,
+			xs = ballx * this.size;
+
+		var min_z = Math.floor( zs - radius ); if ( min_z < 1 ) min_z = 1;
+		var max_z = Math.floor( zs + radius ); if ( max_z > this.size - 1 ) max_z = this.size - 1;
+		var min_y = Math.floor( ys - radius ); if ( min_y < 1 ) min_y = 1;
+		var max_y = Math.floor( ys + radius ); if ( max_y > this.size - 1 ) max_y = this.size - 1;
+		var min_x = Math.floor( xs - radius ); if ( min_x < 1  ) min_x = 1;
+		var max_x = Math.floor( xs + radius ); if ( max_x > this.size - 1 ) max_x = this.size - 1;
+
+
+		// Don't polygonize in the outer layer because normals aren't
+		// well-defined there.
+
+		var x, y, z, y_offset, z_offset, fx, fy, fz, fz2, fy2, val;
+
+		for ( z = min_z; z < max_z; z ++ ) {
+
+			z_offset = this.size2 * z;
+			fz = z / this.size - ballz;
+			fz2 = fz * fz;
+
+			for ( y = min_y; y < max_y; y ++ ) {
+
+				y_offset = z_offset + this.size * y;
+				fy = y / this.size - bally;
+				fy2 = fy * fy;
+
+				for ( x = min_x; x < max_x; x ++ ) {
+
+					fx = x / this.size - ballx;
+					val = strength / ( 0.000001 + fx * fx + fy2 + fz2 ) - subtract;
+					if ( val > 0.0 ) this.field[ y_offset + x ] += val * sign;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	this.addPlaneX = function( strength, subtract ) {
+
+		var x, y, z, xx, val, xdiv, cxy,
+
+			// cache attribute lookups
+			size = this.size,
+			yd = this.yd,
+			zd = this.zd,
+			field = this.field,
+
+			dist = size * Math.sqrt( strength / subtract );
+
+		if ( dist > size ) dist = size;
+
+		for ( x = 0; x < dist; x ++ ) {
+
+			xdiv = x / size;
+			xx = xdiv * xdiv;
+			val = strength / ( 0.0001 + xx ) - subtract;
+
+			if ( val > 0.0 ) {
+
+				for ( y = 0; y < size; y ++ ) {
+
+					cxy = x + y * yd;
+
+					for ( z = 0; z < size; z ++ ) {
+
+						field[ zd * z + cxy ] += val;
+
+					}
+
+				}
+
+			}
+
+		}
+
+	};
+
+	this.addPlaneY = function( strength, subtract ) {
+
+		var x, y, z, yy, val, ydiv, cy, cxy,
+
+			// cache attribute lookups
+			size = this.size,
+			yd = this.yd,
+			zd = this.zd,
+			field = this.field,
+
+			dist = size * Math.sqrt( strength / subtract );
+
+		if ( dist > size ) dist = size;
+
+		for ( y = 0; y < dist; y ++ ) {
+
+			ydiv = y / size;
+			yy = ydiv * ydiv;
+			val = strength / ( 0.0001 + yy ) - subtract;
+
+			if ( val > 0.0 ) {
+
+				cy = y * yd;
+
+				for ( x = 0; x < size; x ++ ) {
+
+					cxy = cy + x;
+
+					for ( z = 0; z < size; z ++ )
+						field[ zd * z + cxy ] += val;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	this.addPlaneZ = function( strength, subtract ) {
+
+		var x, y, z, zz, val, zdiv, cz, cyz,
+
+			// cache attribute lookups
+			size = this.size,
+			yd = this.yd,
+			zd = this.zd,
+			field = this.field,
+
+			dist = size * Math.sqrt( strength / subtract );
+
+		if ( dist > size ) dist = size;
+
+		for ( z = 0; z < dist; z ++ ) {
+
+			zdiv = z / size;
+			zz = zdiv * zdiv;
+			val = strength / ( 0.0001 + zz ) - subtract;
+			if ( val > 0.0 ) {
+
+				cz = zd * z;
+
+				for ( y = 0; y < size; y ++ ) {
+
+					cyz = cz + y * yd;
+
+					for ( x = 0; x < size; x ++ )
+						field[ cyz + x ] += val;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	/////////////////////////////////////
+	// Updates
+	/////////////////////////////////////
+
+	this.reset = function () {
+
+		var i;
+
+		// wipe the normal cache
+
+		for ( i = 0; i < this.size3; i ++ ) {
+
+			this.normal_cache[ i * 3 ] = 0.0;
+			this.field[ i ] = 0.0;
+
+		}
+
+	};
+
+	this.render = function ( renderCallback ) {
+
+		this.begin();
+
+		// Triangulate. Yeah, this is slow.
+
+		var smin2 = this.size - 2;
+
+		for ( var z = 1; z < smin2; z ++ ) {
+
+			var z_offset = this.size2 * z;
+			var fz = ( z - this.halfsize ) / this.halfsize; //+ 1
+
+			for ( var y = 1; y < smin2; y ++ ) {
+
+				var y_offset = z_offset + this.size * y;
+				var fy = ( y - this.halfsize ) / this.halfsize; //+ 1
+
+				for ( var x = 1; x < smin2; x ++ ) {
+
+					var fx = ( x - this.halfsize ) / this.halfsize; //+ 1
+					var q = y_offset + x;
+
+					polygonize( fx, fy, fz, q, this.isolation, renderCallback );
+
+				}
+
+			}
+
+		}
+
+		this.end( renderCallback );
+
+	};
+
+	this.generateGeometry = function() {
+
+		var start = 0, geo = new THREE.Geometry();
+		var normals = [];
+
+		var geo_callback = function( object ) {
+
+			for ( var i = 0; i < object.count; i ++ ) {
+
+				var vertex = new THREE.Vector3().fromArray( object.positionArray, i * 3 );
+				var normal = new THREE.Vector3().fromArray( object.normalArray, i * 3 );
+
+				geo.vertices.push( vertex );
+				normals.push( normal );
+
+			}
+
+			var nfaces = object.count / 3;
+
+			for ( i = 0; i < nfaces; i ++ ) {
+
+				var a = ( start + i ) * 3;
+				var b = a + 1;
+				var c = a + 2;
+
+				var na = normals[ a ];
+				var nb = normals[ b ];
+				var nc = normals[ c ];
+
+				var face = new THREE.Face3( a, b, c, [ na, nb, nc ] );
+				geo.faces.push( face );
+
+			}
+
+			start += nfaces;
+			object.count = 0;
+
+		};
+
+		this.render( geo_callback );
+
+		// console.log( "generated " + geo.faces.length + " triangles" );
+
+		return geo;
+
+	};
+
+	this.init( resolution );
+
+};
+
+THREE.MarchingCubes.prototype = Object.create( THREE.ImmediateRenderObject.prototype );
+THREE.MarchingCubes.prototype.constructor = THREE.MarchingCubes;
+
+
+/////////////////////////////////////
+// Marching cubes lookup tables
+/////////////////////////////////////
+
+// These tables are straight from Paul Bourke's page:
+// http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/
+// who in turn got them from Cory Gene Bloyd.
+
+THREE.edgeTable = new Int32Array( [
+0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
+0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
+0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
+0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
+0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c,
+0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
+0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac,
+0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
+0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c,
+0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
+0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc,
+0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
+0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c,
+0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
+0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc,
+0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
+0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
+0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
+0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
+0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
+0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
+0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
+0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
+0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460,
+0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
+0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0,
+0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
+0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230,
+0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
+0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190,
+0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
+0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 ] );
+
+THREE.triTable = new Int32Array( [
+- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 8, 3, 9, 8, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 8, 3, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 2, 10, 0, 2, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+2, 8, 3, 2, 10, 8, 10, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 11, 2, 8, 11, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 9, 0, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 11, 2, 1, 9, 11, 9, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 10, 1, 11, 10, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 10, 1, 0, 8, 10, 8, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 9, 0, 3, 11, 9, 11, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+4, 3, 0, 7, 3, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 1, 9, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+4, 1, 9, 4, 7, 1, 7, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 2, 10, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 4, 7, 3, 0, 4, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 2, 10, 9, 0, 2, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1,
+8, 4, 7, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+11, 4, 7, 11, 2, 4, 2, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 0, 1, 8, 4, 7, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, - 1, - 1, - 1, - 1,
+3, 10, 1, 3, 11, 10, 7, 8, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, - 1, - 1, - 1, - 1,
+4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1,
+4, 7, 11, 4, 11, 9, 9, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 5, 4, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 5, 4, 1, 5, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+8, 5, 4, 8, 3, 5, 3, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 2, 10, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 0, 8, 1, 2, 10, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+5, 2, 10, 5, 4, 2, 4, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, - 1, - 1, - 1, - 1,
+9, 5, 4, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 11, 2, 0, 8, 11, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 5, 4, 0, 1, 5, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, - 1, - 1, - 1, - 1,
+10, 3, 11, 10, 1, 3, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, - 1, - 1, - 1, - 1,
+5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1,
+5, 4, 8, 5, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 7, 8, 5, 7, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 3, 0, 9, 5, 3, 5, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 7, 8, 0, 1, 7, 1, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 7, 8, 9, 5, 7, 10, 1, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, - 1, - 1, - 1, - 1,
+8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, - 1, - 1, - 1, - 1,
+2, 10, 5, 2, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+7, 9, 5, 7, 8, 9, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1,
+2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, - 1, - 1, - 1, - 1,
+11, 2, 1, 11, 1, 7, 7, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, - 1, - 1, - 1, - 1,
+5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, - 1,
+11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, - 1,
+11, 10, 5, 7, 11, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 8, 3, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 0, 1, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 8, 3, 1, 9, 8, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 6, 5, 2, 6, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 6, 5, 1, 2, 6, 3, 0, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 6, 5, 9, 0, 6, 0, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, - 1, - 1, - 1, - 1,
+2, 3, 11, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+11, 0, 8, 11, 2, 0, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 1, 9, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, - 1, - 1, - 1, - 1,
+6, 3, 11, 6, 5, 3, 5, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1,
+3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, - 1, - 1, - 1, - 1,
+6, 5, 9, 6, 9, 11, 11, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+5, 10, 6, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+4, 3, 0, 4, 7, 3, 6, 5, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 9, 0, 5, 10, 6, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1,
+6, 1, 2, 6, 5, 1, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, - 1, - 1, - 1, - 1,
+8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, - 1, - 1, - 1, - 1,
+7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, - 1,
+3, 11, 2, 7, 8, 4, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1,
+0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1,
+9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, - 1,
+8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1,
+5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, - 1,
+0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, - 1,
+6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, - 1, - 1, - 1, - 1,
+10, 4, 9, 6, 4, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+4, 10, 6, 4, 9, 10, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+10, 0, 1, 10, 6, 0, 6, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1,
+1, 4, 9, 1, 2, 4, 2, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, - 1, - 1, - 1, - 1,
+0, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+8, 3, 2, 8, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+10, 4, 9, 10, 6, 4, 11, 2, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, - 1, - 1, - 1, - 1,
+3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1,
+6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, - 1,
+9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, - 1, - 1, - 1, - 1,
+8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, - 1,
+3, 11, 6, 3, 6, 0, 0, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+6, 4, 8, 11, 6, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+7, 10, 6, 7, 8, 10, 8, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, - 1, - 1, - 1, - 1,
+10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, - 1, - 1, - 1, - 1,
+10, 6, 7, 10, 7, 1, 1, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1,
+2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, - 1,
+7, 8, 0, 7, 0, 6, 6, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+7, 3, 2, 6, 7, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1,
+2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, - 1,
+1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, - 1,
+11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, - 1, - 1, - 1, - 1,
+8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, - 1,
+0, 9, 1, 11, 6, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, - 1, - 1, - 1, - 1,
+7, 11, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 0, 8, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 1, 9, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+8, 1, 9, 8, 3, 1, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+10, 1, 2, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 2, 10, 3, 0, 8, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+2, 9, 0, 2, 10, 9, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, - 1, - 1, - 1, - 1,
+7, 2, 3, 6, 2, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+7, 0, 8, 7, 6, 0, 6, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+2, 7, 6, 2, 3, 7, 0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, - 1, - 1, - 1, - 1,
+10, 7, 6, 10, 1, 7, 1, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, - 1, - 1, - 1, - 1,
+0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, - 1, - 1, - 1, - 1,
+7, 6, 10, 7, 10, 8, 8, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+6, 8, 4, 11, 8, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 6, 11, 3, 0, 6, 0, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+8, 6, 11, 8, 4, 6, 9, 0, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, - 1, - 1, - 1, - 1,
+6, 8, 4, 6, 11, 8, 2, 10, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, - 1, - 1, - 1, - 1,
+4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, - 1, - 1, - 1, - 1,
+10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, - 1,
+8, 2, 3, 8, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, - 1, - 1, - 1, - 1,
+1, 9, 4, 1, 4, 2, 2, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, - 1, - 1, - 1, - 1,
+10, 1, 0, 10, 0, 6, 6, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, - 1,
+10, 9, 4, 6, 10, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+4, 9, 5, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 8, 3, 4, 9, 5, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+5, 0, 1, 5, 4, 0, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, - 1, - 1, - 1, - 1,
+9, 5, 4, 10, 1, 2, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, - 1, - 1, - 1, - 1,
+7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, - 1, - 1, - 1, - 1,
+3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, - 1,
+7, 2, 3, 7, 6, 2, 5, 4, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, - 1, - 1, - 1, - 1,
+3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, - 1, - 1, - 1, - 1,
+6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, - 1,
+9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, - 1, - 1, - 1, - 1,
+1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, - 1,
+4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, - 1,
+7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, - 1, - 1, - 1, - 1,
+6, 9, 5, 6, 11, 9, 11, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, - 1, - 1, - 1, - 1,
+0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, - 1, - 1, - 1, - 1,
+6, 11, 3, 6, 3, 5, 5, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, - 1, - 1, - 1, - 1,
+0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, - 1,
+11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, - 1,
+6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, - 1, - 1, - 1, - 1,
+5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, - 1, - 1, - 1, - 1,
+9, 5, 6, 9, 6, 0, 0, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, - 1,
+1, 5, 6, 2, 1, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, - 1,
+10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, - 1, - 1, - 1, - 1,
+0, 3, 8, 5, 6, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+10, 5, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+11, 5, 10, 7, 5, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+11, 5, 10, 11, 7, 5, 8, 3, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+5, 11, 7, 5, 10, 11, 1, 9, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, - 1, - 1, - 1, - 1,
+11, 1, 2, 11, 7, 1, 7, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, - 1, - 1, - 1, - 1,
+9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, - 1, - 1, - 1, - 1,
+7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, - 1,
+2, 5, 10, 2, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, - 1, - 1, - 1, - 1,
+9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, - 1, - 1, - 1, - 1,
+9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, - 1,
+1, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 8, 7, 0, 7, 1, 1, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 0, 3, 9, 3, 5, 5, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 8, 7, 5, 9, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+5, 8, 4, 5, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, - 1, - 1, - 1, - 1,
+0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, - 1, - 1, - 1, - 1,
+10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, - 1,
+2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, - 1, - 1, - 1, - 1,
+0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, - 1,
+0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, - 1,
+9, 4, 5, 2, 11, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, - 1, - 1, - 1, - 1,
+5, 10, 2, 5, 2, 4, 4, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, - 1,
+5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, - 1, - 1, - 1, - 1,
+8, 4, 5, 8, 5, 3, 3, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 4, 5, 1, 0, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, - 1, - 1, - 1, - 1,
+9, 4, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+4, 11, 7, 4, 9, 11, 9, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, - 1, - 1, - 1, - 1,
+1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, - 1, - 1, - 1, - 1,
+3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, - 1,
+4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, - 1, - 1, - 1, - 1,
+9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, - 1,
+11, 7, 4, 11, 4, 2, 2, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, - 1, - 1, - 1, - 1,
+2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, - 1, - 1, - 1, - 1,
+9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, - 1,
+3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, - 1,
+1, 10, 2, 8, 7, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+4, 9, 1, 4, 1, 7, 7, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, - 1, - 1, - 1, - 1,
+4, 0, 3, 7, 4, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+4, 8, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 0, 9, 3, 9, 11, 11, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 1, 10, 0, 10, 8, 8, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 1, 10, 11, 3, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 2, 11, 1, 11, 9, 9, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, - 1, - 1, - 1, - 1,
+0, 2, 11, 8, 0, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+3, 2, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+2, 3, 8, 2, 8, 10, 10, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+9, 10, 2, 0, 9, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, - 1, - 1, - 1, - 1,
+1, 10, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+1, 3, 8, 9, 1, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 9, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+0, 3, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 ] );

+ 316 - 316
examples/js/MorphBlendMesh.js

@@ -1,318 +1,318 @@
 /**
- * @author alteredq / http://alteredqualia.com/
- */
-
+ * @author alteredq / http://alteredqualia.com/
+ */
+
 THREE.MorphBlendMesh = function ( geometry, material ) {
-
-	THREE.Mesh.call( this, geometry, material );
-
-	this.animationsMap = {};
-	this.animationsList = [];
-
-	// prepare default animation
-	// (all frames played together in 1 second)
-
-	var numFrames = this.geometry.morphTargets.length;
-
-	var name = "__default";
-
-	var startFrame = 0;
-	var endFrame = numFrames - 1;
-
-	var fps = numFrames / 1;
-
-	this.createAnimation( name, startFrame, endFrame, fps );
-	this.setAnimationWeight( name, 1 );
-
-}
-
-THREE.MorphBlendMesh.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), {
-
-	constructor: THREE.MorphBlendMesh,
-
-	createAnimation: function ( name, start, end, fps ) {
-
-		var animation = {
-
-			start: start,
-			end: end,
-
-			length: end - start + 1,
-
-			fps: fps,
-			duration: ( end - start ) / fps,
-
-			lastFrame: 0,
-			currentFrame: 0,
-
-			active: false,
-
-			time: 0,
-			direction: 1,
-			weight: 1,
-
-			directionBackwards: false,
-			mirroredLoop: false
-
-		};
-
-		this.animationsMap[ name ] = animation;
-		this.animationsList.push( animation );
-
-	},
-
-		autoCreateAnimations: function ( fps ) {
-
-		var pattern = /([a-z]+)_?(\d+)/i;
-
-		var firstAnimation, frameRanges = {};
-
-		var geometry = this.geometry;
-
-		for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
-
-			var morph = geometry.morphTargets[ i ];
-			var chunks = morph.name.match( pattern );
-
-			if ( chunks && chunks.length > 1 ) {
-
-				var name = chunks[ 1 ];
-
-				if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity };
-
-				var range = frameRanges[ name ];
-
-				if ( i < range.start ) range.start = i;
-				if ( i > range.end ) range.end = i;
-
-				if ( ! firstAnimation ) firstAnimation = name;
-
-			}
-
-		}
-
-		for ( var name in frameRanges ) {
-
-			var range = frameRanges[ name ];
-			this.createAnimation( name, range.start, range.end, fps );
-
-		}
-
-		this.firstAnimation = firstAnimation;
-
-	},
-
-	setAnimationDirectionForward: function ( name ) {
-
-		var animation = this.animationsMap[ name ];
-
-		if ( animation ) {
-
-			animation.direction = 1;
-			animation.directionBackwards = false;
-
-		}
-
-	},
-
-	setAnimationDirectionBackward: function ( name ) {
-
-		var animation = this.animationsMap[ name ];
-
-		if ( animation ) {
-
-			animation.direction = - 1;
-			animation.directionBackwards = true;
-
-		}
-
-	},
-
-	setAnimationFPS: function ( name, fps ) {
-
-		var animation = this.animationsMap[ name ];
-
-		if ( animation ) {
-
-			animation.fps = fps;
-			animation.duration = ( animation.end - animation.start ) / animation.fps;
-
-		}
-
-	},
-
-	setAnimationDuration: function ( name, duration ) {
-
-		var animation = this.animationsMap[ name ];
-
-		if ( animation ) {
-
-			animation.duration = duration;
-			animation.fps = ( animation.end - animation.start ) / animation.duration;
-
-		}
-
-	},
-
-	setAnimationWeight: function ( name, weight ) {
-
-		var animation = this.animationsMap[ name ];
-
-		if ( animation ) {
-
-			animation.weight = weight;
-
-		}
-
-	},
-
-	setAnimationTime: function ( name, time ) {
-
-		var animation = this.animationsMap[ name ];
-
-		if ( animation ) {
-
-			animation.time = time;
-
-		}
-
-	},
-
-	getAnimationTime: function ( name ) {
-
-		var time = 0;
-
-		var animation = this.animationsMap[ name ];
-
-		if ( animation ) {
-
-			time = animation.time;
-
-		}
-
-		return time;
-
-	},
-
-	getAnimationDuration: function ( name ) {
-
-		var duration = - 1;
-
-		var animation = this.animationsMap[ name ];
-
-		if ( animation ) {
-
-			duration = animation.duration;
-
-		}
-
-		return duration;
-
-	},
-
-	playAnimation: function ( name ) {
-
-		var animation = this.animationsMap[ name ];
-
-		if ( animation ) {
-
-			animation.time = 0;
-			animation.active = true;
-
-		} else {
-
-			console.warn( "THREE.MorphBlendMesh: animation[" + name + "] undefined in .playAnimation()" );
-
-		}
-
-	},
-
-	stopAnimation: function ( name ) {
-
-		var animation = this.animationsMap[ name ];
-
-		if ( animation ) {
-
-			animation.active = false;
-
-		}
-
-	},
-
-	update: function ( delta ) {
-
-		for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) {
-
-			var animation = this.animationsList[ i ];
-
-			if ( ! animation.active ) continue;
-
-			var frameTime = animation.duration / animation.length;
-
-			animation.time += animation.direction * delta;
-
-			if ( animation.mirroredLoop ) {
-
-				if ( animation.time > animation.duration || animation.time < 0 ) {
-
-					animation.direction *= - 1;
-
-					if ( animation.time > animation.duration ) {
-
-						animation.time = animation.duration;
-						animation.directionBackwards = true;
-
-					}
-
-					if ( animation.time < 0 ) {
-
-						animation.time = 0;
-						animation.directionBackwards = false;
-
-					}
-
-				}
-
-			} else {
-
-				animation.time = animation.time % animation.duration;
-
-				if ( animation.time < 0 ) animation.time += animation.duration;
-
-			}
-
-			var keyframe = animation.start + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 );
-			var weight = animation.weight;
-
-			if ( keyframe !== animation.currentFrame ) {
-
-				this.morphTargetInfluences[ animation.lastFrame ] = 0;
-				this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight;
-
-				this.morphTargetInfluences[ keyframe ] = 0;
-
-				animation.lastFrame = animation.currentFrame;
-				animation.currentFrame = keyframe;
-
-			}
-
-			var mix = ( animation.time % frameTime ) / frameTime;
-
-			if ( animation.directionBackwards ) mix = 1 - mix;
-
-			if ( animation.currentFrame !== animation.lastFrame ) {
-
-				this.morphTargetInfluences[ animation.currentFrame ] = mix * weight;
-				this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight;
-
-			} else {
-
-				this.morphTargetInfluences[ animation.currentFrame ] = weight;
-
-			}
-
-		}
-
-	}
-
-} );
+
+	THREE.Mesh.call( this, geometry, material );
+
+	this.animationsMap = {};
+	this.animationsList = [];
+
+	// prepare default animation
+	// (all frames played together in 1 second)
+
+	var numFrames = this.geometry.morphTargets.length;
+
+	var name = "__default";
+
+	var startFrame = 0;
+	var endFrame = numFrames - 1;
+
+	var fps = numFrames / 1;
+
+	this.createAnimation( name, startFrame, endFrame, fps );
+	this.setAnimationWeight( name, 1 );
+
+}
+
+THREE.MorphBlendMesh.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), {
+
+	constructor: THREE.MorphBlendMesh,
+
+	createAnimation: function ( name, start, end, fps ) {
+
+		var animation = {
+
+			start: start,
+			end: end,
+
+			length: end - start + 1,
+
+			fps: fps,
+			duration: ( end - start ) / fps,
+
+			lastFrame: 0,
+			currentFrame: 0,
+
+			active: false,
+
+			time: 0,
+			direction: 1,
+			weight: 1,
+
+			directionBackwards: false,
+			mirroredLoop: false
+
+		};
+
+		this.animationsMap[ name ] = animation;
+		this.animationsList.push( animation );
+
+	},
+
+		autoCreateAnimations: function ( fps ) {
+
+		var pattern = /([a-z]+)_?(\d+)/i;
+
+		var firstAnimation, frameRanges = {};
+
+		var geometry = this.geometry;
+
+		for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
+
+			var morph = geometry.morphTargets[ i ];
+			var chunks = morph.name.match( pattern );
+
+			if ( chunks && chunks.length > 1 ) {
+
+				var name = chunks[ 1 ];
+
+				if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity };
+
+				var range = frameRanges[ name ];
+
+				if ( i < range.start ) range.start = i;
+				if ( i > range.end ) range.end = i;
+
+				if ( ! firstAnimation ) firstAnimation = name;
+
+			}
+
+		}
+
+		for ( var name in frameRanges ) {
+
+			var range = frameRanges[ name ];
+			this.createAnimation( name, range.start, range.end, fps );
+
+		}
+
+		this.firstAnimation = firstAnimation;
+
+	},
+
+	setAnimationDirectionForward: function ( name ) {
+
+		var animation = this.animationsMap[ name ];
+
+		if ( animation ) {
+
+			animation.direction = 1;
+			animation.directionBackwards = false;
+
+		}
+
+	},
+
+	setAnimationDirectionBackward: function ( name ) {
+
+		var animation = this.animationsMap[ name ];
+
+		if ( animation ) {
+
+			animation.direction = - 1;
+			animation.directionBackwards = true;
+
+		}
+
+	},
+
+	setAnimationFPS: function ( name, fps ) {
+
+		var animation = this.animationsMap[ name ];
+
+		if ( animation ) {
+
+			animation.fps = fps;
+			animation.duration = ( animation.end - animation.start ) / animation.fps;
+
+		}
+
+	},
+
+	setAnimationDuration: function ( name, duration ) {
+
+		var animation = this.animationsMap[ name ];
+
+		if ( animation ) {
+
+			animation.duration = duration;
+			animation.fps = ( animation.end - animation.start ) / animation.duration;
+
+		}
+
+	},
+
+	setAnimationWeight: function ( name, weight ) {
+
+		var animation = this.animationsMap[ name ];
+
+		if ( animation ) {
+
+			animation.weight = weight;
+
+		}
+
+	},
+
+	setAnimationTime: function ( name, time ) {
+
+		var animation = this.animationsMap[ name ];
+
+		if ( animation ) {
+
+			animation.time = time;
+
+		}
+
+	},
+
+	getAnimationTime: function ( name ) {
+
+		var time = 0;
+
+		var animation = this.animationsMap[ name ];
+
+		if ( animation ) {
+
+			time = animation.time;
+
+		}
+
+		return time;
+
+	},
+
+	getAnimationDuration: function ( name ) {
+
+		var duration = - 1;
+
+		var animation = this.animationsMap[ name ];
+
+		if ( animation ) {
+
+			duration = animation.duration;
+
+		}
+
+		return duration;
+
+	},
+
+	playAnimation: function ( name ) {
+
+		var animation = this.animationsMap[ name ];
+
+		if ( animation ) {
+
+			animation.time = 0;
+			animation.active = true;
+
+		} else {
+
+			console.warn( "THREE.MorphBlendMesh: animation[" + name + "] undefined in .playAnimation()" );
+
+		}
+
+	},
+
+	stopAnimation: function ( name ) {
+
+		var animation = this.animationsMap[ name ];
+
+		if ( animation ) {
+
+			animation.active = false;
+
+		}
+
+	},
+
+	update: function ( delta ) {
+
+		for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) {
+
+			var animation = this.animationsList[ i ];
+
+			if ( ! animation.active ) continue;
+
+			var frameTime = animation.duration / animation.length;
+
+			animation.time += animation.direction * delta;
+
+			if ( animation.mirroredLoop ) {
+
+				if ( animation.time > animation.duration || animation.time < 0 ) {
+
+					animation.direction *= - 1;
+
+					if ( animation.time > animation.duration ) {
+
+						animation.time = animation.duration;
+						animation.directionBackwards = true;
+
+					}
+
+					if ( animation.time < 0 ) {
+
+						animation.time = 0;
+						animation.directionBackwards = false;
+
+					}
+
+				}
+
+			} else {
+
+				animation.time = animation.time % animation.duration;
+
+				if ( animation.time < 0 ) animation.time += animation.duration;
+
+			}
+
+			var keyframe = animation.start + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 );
+			var weight = animation.weight;
+
+			if ( keyframe !== animation.currentFrame ) {
+
+				this.morphTargetInfluences[ animation.lastFrame ] = 0;
+				this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight;
+
+				this.morphTargetInfluences[ keyframe ] = 0;
+
+				animation.lastFrame = animation.currentFrame;
+				animation.currentFrame = keyframe;
+
+			}
+
+			var mix = ( animation.time % frameTime ) / frameTime;
+
+			if ( animation.directionBackwards ) mix = 1 - mix;
+
+			if ( animation.currentFrame !== animation.lastFrame ) {
+
+				this.morphTargetInfluences[ animation.currentFrame ] = mix * weight;
+				this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight;
+
+			} else {
+
+				this.morphTargetInfluences[ animation.currentFrame ] = weight;
+
+			}
+
+		}
+
+	}
+
+} );

+ 1 - 1
examples/js/Ocean.js

@@ -1,4 +1,4 @@
-THREE.Ocean = function ( renderer, camera, scene, options ) {
+THREE.Ocean = function ( renderer, camera, scene, options ) {
 
 	// flag used to trigger parameter changes
 	this.changed = true;

+ 2358 - 2358
examples/js/loaders/AssimpLoader.js

@@ -1,2358 +1,2358 @@
-/**
- * @author Virtulous / https://virtulo.us/
- */
-
-THREE.AssimpLoader = function ( manager ) {
-
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-
-};
-
-THREE.AssimpLoader.prototype = {
-
-	constructor: THREE.AssimpLoader,
-
-	crossOrigin: 'Anonymous',
-
-	load: function ( url, onLoad, onProgress, onError ) {
-
-		var scope = this;
-
-		var path = THREE.LoaderUtils.extractUrlBase( url );
-
-		var loader = new THREE.FileLoader( this.manager );
-		loader.setResponseType( 'arraybuffer' );
-
-		loader.load( url, function ( buffer ) {
-
-			onLoad( scope.parse( buffer, path ) );
-
-		}, onProgress, onError );
-
-	},
-
-	setCrossOrigin: function ( value ) {
-
-		this.crossOrigin = value;
-
-	},
-
-	parse: function ( buffer, path ) {
-
-		var textureLoader = new THREE.TextureLoader( this.manager );
-		textureLoader.setPath( path ).setCrossOrigin( this.crossOrigin );
-
-		var Virtulous = {};
-
-		Virtulous.KeyFrame = function ( time, matrix ) {
-
-			this.time = time;
-			this.matrix = matrix.clone();
-			this.position = new THREE.Vector3();
-			this.quaternion = new THREE.Quaternion();
-			this.scale = new THREE.Vector3( 1, 1, 1 );
-			this.matrix.decompose( this.position, this.quaternion, this.scale );
-			this.clone = function () {
-
-				var n = new Virtulous.KeyFrame( this.time, this.matrix );
-				return n;
-
-			};
-			this.lerp = function ( nextKey, time ) {
-
-				time -= this.time;
-				var dist = ( nextKey.time - this.time );
-				var l = time / dist;
-				var l2 = 1 - l;
-				var keypos = this.position;
-				var keyrot = this.quaternion;
-				//      var keyscl =  key.parentspaceScl || key.scl;
-				var key2pos = nextKey.position;
-				var key2rot = nextKey.quaternion;
-				//  var key2scl =  key2.parentspaceScl || key2.scl;
-				Virtulous.KeyFrame.tempAniPos.x = keypos.x * l2 + key2pos.x * l;
-				Virtulous.KeyFrame.tempAniPos.y = keypos.y * l2 + key2pos.y * l;
-				Virtulous.KeyFrame.tempAniPos.z = keypos.z * l2 + key2pos.z * l;
-				//     tempAniScale.x = keyscl[0] * l2 + key2scl[0] * l;
-				//     tempAniScale.y = keyscl[1] * l2 + key2scl[1] * l;
-				//     tempAniScale.z = keyscl[2] * l2 + key2scl[2] * l;
-				Virtulous.KeyFrame.tempAniQuat.set( keyrot.x, keyrot.y, keyrot.z, keyrot.w );
-				Virtulous.KeyFrame.tempAniQuat.slerp( key2rot, l );
-				return Virtulous.KeyFrame.tempAniMatrix.compose( Virtulous.KeyFrame.tempAniPos, Virtulous.KeyFrame.tempAniQuat, Virtulous.KeyFrame.tempAniScale );
-
-			};
-
-		};
-
-		Virtulous.KeyFrame.tempAniPos = new THREE.Vector3();
-		Virtulous.KeyFrame.tempAniQuat = new THREE.Quaternion();
-		Virtulous.KeyFrame.tempAniScale = new THREE.Vector3( 1, 1, 1 );
-		Virtulous.KeyFrame.tempAniMatrix = new THREE.Matrix4();
-		Virtulous.KeyFrameTrack = function () {
-
-			this.keys = [];
-			this.target = null;
-			this.time = 0;
-			this.length = 0;
-			this._accelTable = {};
-			this.fps = 20;
-			this.addKey = function ( key ) {
-
-				this.keys.push( key );
-
-			};
-			this.init = function () {
-
-				this.sortKeys();
-
-				if ( this.keys.length > 0 )
-					this.length = this.keys[ this.keys.length - 1 ].time;
-				else
-					this.length = 0;
-
-				if ( ! this.fps ) return;
-
-				for ( var j = 0; j < this.length * this.fps; j ++ ) {
-
-					for ( var i = 0; i < this.keys.length; i ++ ) {
-
-						if ( this.keys[ i ].time == j ) {
-
-							this._accelTable[ j ] = i;
-							break;
-
-						} else if ( this.keys[ i ].time < j / this.fps && this.keys[ i + 1 ] && this.keys[ i + 1 ].time >= j / this.fps ) {
-
-							this._accelTable[ j ] = i;
-							break;
-
-						}
-
-					}
-
-				}
-
-			};
-
-			this.parseFromThree = function ( data ) {
-
-				var fps = data.fps;
-				this.target = data.node;
-				var track = data.hierarchy[ 0 ].keys;
-				for ( var i = 0; i < track.length; i ++ ) {
-
-					this.addKey( new Virtulous.KeyFrame( i / fps || track[ i ].time, track[ i ].targets[ 0 ].data ) );
-
-				}
-				this.init();
-
-			};
-
-			this.parseFromCollada = function ( data ) {
-
-				var track = data.keys;
-				var fps = this.fps;
-
-				for ( var i = 0; i < track.length; i ++ ) {
-
-					this.addKey( new Virtulous.KeyFrame( i / fps || track[ i ].time, track[ i ].matrix ) );
-
-				}
-
-				this.init();
-
-			};
-
-			this.sortKeys = function () {
-
-				this.keys.sort( this.keySortFunc );
-
-			};
-
-			this.keySortFunc = function ( a, b ) {
-
-				return a.time - b.time;
-
-			};
-
-			this.clone = function () {
-
-				var t = new Virtulous.KeyFrameTrack();
-				t.target = this.target;
-				t.time = this.time;
-				t.length = this.length;
-
-				for ( var i = 0; i < this.keys.length; i ++ ) {
-
-					t.addKey( this.keys[ i ].clone() );
-
-				}
-
-				t.init();
-				return t;
-
-			};
-
-			this.reTarget = function ( root, compareitor ) {
-
-				if ( ! compareitor ) compareitor = Virtulous.TrackTargetNodeNameCompare;
-				this.target = compareitor( root, this.target );
-
-			};
-
-			this.keySearchAccel = function ( time ) {
-
-				time *= this.fps;
-				time = Math.floor( time );
-				return this._accelTable[ time ] || 0;
-
-			};
-
-			this.setTime = function ( time ) {
-
-				time = Math.abs( time );
-				if ( this.length )
-					time = time % this.length + .05;
-				var key0 = null;
-				var key1 = null;
-
-				for ( var i = this.keySearchAccel( time ); i < this.keys.length; i ++ ) {
-
-					if ( this.keys[ i ].time == time ) {
-
-						key0 = this.keys[ i ];
-						key1 = this.keys[ i ];
-						break;
-
-					} else if ( this.keys[ i ].time < time && this.keys[ i + 1 ] && this.keys[ i + 1 ].time > time ) {
-
-						key0 = this.keys[ i ];
-						key1 = this.keys[ i + 1 ];
-						break;
-
-					} else if ( this.keys[ i ].time < time && i == this.keys.length - 1 ) {
-
-						key0 = this.keys[ i ];
-						key1 = this.keys[ 0 ].clone();
-						key1.time += this.length + .05;
-						break;
-
-					}
-
-				}
-
-				if ( key0 && key1 && key0 !== key1 ) {
-
-					this.target.matrixAutoUpdate = false;
-					this.target.matrix.copy( key0.lerp( key1, time ) );
-					this.target.matrixWorldNeedsUpdate = true;
-					return;
-
-				}
-
-				if ( key0 && key1 && key0 == key1 ) {
-
-					this.target.matrixAutoUpdate = false;
-					this.target.matrix.copy( key0.matrix );
-					this.target.matrixWorldNeedsUpdate = true;
-					return;
-
-				}
-
-			};
-
-		};
-
-		Virtulous.TrackTargetNodeNameCompare = function ( root, target ) {
-
-			function find( node, name ) {
-
-				if ( node.name == name )
-					return node;
-
-				for ( var i = 0; i < node.children.length; i ++ ) {
-
-					var r = find( node.children[ i ], name );
-					if ( r ) return r;
-
-				}
-
-				return null;
-
-			}
-
-			return find( root, target.name );
-
-		};
-
-		Virtulous.Animation = function () {
-
-			this.tracks = [];
-			this.length = 0;
-
-			this.addTrack = function ( track ) {
-
-				this.tracks.push( track );
-				this.length = Math.max( track.length, this.length );
-
-			};
-
-			this.setTime = function ( time ) {
-
-				this.time = time;
-
-				for ( var i = 0; i < this.tracks.length; i ++ )
-					this.tracks[ i ].setTime( time );
-
-			};
-
-			this.clone = function ( target, compareitor ) {
-
-				if ( ! compareitor ) compareitor = Virtulous.TrackTargetNodeNameCompare;
-				var n = new Virtulous.Animation();
-				n.target = target;
-				for ( var i = 0; i < this.tracks.length; i ++ ) {
-
-					var track = this.tracks[ i ].clone();
-					track.reTarget( target, compareitor );
-					n.addTrack( track );
-
-				}
-
-				return n;
-
-			};
-
-		};
-
-		var ASSBIN_CHUNK_AICAMERA = 0x1234;
-		var ASSBIN_CHUNK_AILIGHT = 0x1235;
-		var ASSBIN_CHUNK_AITEXTURE = 0x1236;
-		var ASSBIN_CHUNK_AIMESH = 0x1237;
-		var ASSBIN_CHUNK_AINODEANIM = 0x1238;
-		var ASSBIN_CHUNK_AISCENE = 0x1239;
-		var ASSBIN_CHUNK_AIBONE = 0x123a;
-		var ASSBIN_CHUNK_AIANIMATION = 0x123b;
-		var ASSBIN_CHUNK_AINODE = 0x123c;
-		var ASSBIN_CHUNK_AIMATERIAL = 0x123d;
-		var ASSBIN_CHUNK_AIMATERIALPROPERTY = 0x123e;
-		var ASSBIN_MESH_HAS_POSITIONS = 0x1;
-		var ASSBIN_MESH_HAS_NORMALS = 0x2;
-		var ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS = 0x4;
-		var ASSBIN_MESH_HAS_TEXCOORD_BASE = 0x100;
-		var ASSBIN_MESH_HAS_COLOR_BASE = 0x10000;
-		var AI_MAX_NUMBER_OF_COLOR_SETS = 1;
-		var AI_MAX_NUMBER_OF_TEXTURECOORDS = 4;
-		var aiLightSource_UNDEFINED = 0x0;
-		//! A directional light source has a well-defined direction
-		//! but is infinitely far away. That's quite a good
-		//! approximation for sun light.
-		var aiLightSource_DIRECTIONAL = 0x1;
-		//! A point light source has a well-defined position
-		//! in space but no direction - it emits light in all
-		//! directions. A normal bulb is a point light.
-		var aiLightSource_POINT = 0x2;
-		//! A spot light source emits light in a specific
-		//! angle. It has a position and a direction it is pointing to.
-		//! A good example for a spot light is a light spot in
-		//! sport arenas.
-		var aiLightSource_SPOT = 0x3;
-		//! The generic light level of the world, including the bounces
-		//! of all other lightsources.
-		//! Typically, there's at most one ambient light in a scene.
-		//! This light type doesn't have a valid position, direction, or
-		//! other properties, just a color.
-		var aiLightSource_AMBIENT = 0x4;
-		/** Flat shading. Shading is done on per-face base,
-		 *  diffuse only. Also known as 'faceted shading'.
-		 */
-		var aiShadingMode_Flat = 0x1;
-		/** Simple Gouraud shading.
-		 */
-		var aiShadingMode_Gouraud = 0x2;
-		/** Phong-Shading -
-		 */
-		var aiShadingMode_Phong = 0x3;
-		/** Phong-Blinn-Shading
-		 */
-		var aiShadingMode_Blinn = 0x4;
-		/** Toon-Shading per pixel
-		 *
-		 *  Also known as 'comic' shader.
-		 */
-		var aiShadingMode_Toon = 0x5;
-		/** OrenNayar-Shading per pixel
-		 *
-		 *  Extension to standard Lambertian shading, taking the
-		 *  roughness of the material into account
-		 */
-		var aiShadingMode_OrenNayar = 0x6;
-		/** Minnaert-Shading per pixel
-		 *
-		 *  Extension to standard Lambertian shading, taking the
-		 *  "darkness" of the material into account
-		 */
-		var aiShadingMode_Minnaert = 0x7;
-		/** CookTorrance-Shading per pixel
-		 *
-		 *  Special shader for metallic surfaces.
-		 */
-		var aiShadingMode_CookTorrance = 0x8;
-		/** No shading at all. Constant light influence of 1.0.
-		 */
-		var aiShadingMode_NoShading = 0x9;
-		/** Fresnel shading
-		 */
-		var aiShadingMode_Fresnel = 0xa;
-		var aiTextureType_NONE = 0x0;
-		/** The texture is combined with the result of the diffuse
-		 *  lighting equation.
-		 */
-		var aiTextureType_DIFFUSE = 0x1;
-		/** The texture is combined with the result of the specular
-		 *  lighting equation.
-		 */
-		var aiTextureType_SPECULAR = 0x2;
-		/** The texture is combined with the result of the ambient
-		 *  lighting equation.
-		 */
-		var aiTextureType_AMBIENT = 0x3;
-		/** The texture is added to the result of the lighting
-		 *  calculation. It isn't influenced by incoming light.
-		 */
-		var aiTextureType_EMISSIVE = 0x4;
-		/** The texture is a height map.
-		 *
-		 *  By convention, higher gray-scale values stand for
-		 *  higher elevations from the base height.
-		 */
-		var aiTextureType_HEIGHT = 0x5;
-		/** The texture is a (tangent space) normal-map.
-		 *
-		 *  Again, there are several conventions for tangent-space
-		 *  normal maps. Assimp does (intentionally) not
-		 *  distinguish here.
-		 */
-		var aiTextureType_NORMALS = 0x6;
-		/** The texture defines the glossiness of the material.
-		 *
-		 *  The glossiness is in fact the exponent of the specular
-		 *  (phong) lighting equation. Usually there is a conversion
-		 *  function defined to map the linear color values in the
-		 *  texture to a suitable exponent. Have fun.
-		 */
-		var aiTextureType_SHININESS = 0x7;
-		/** The texture defines per-pixel opacity.
-		 *
-		 *  Usually 'white' means opaque and 'black' means
-		 *  'transparency'. Or quite the opposite. Have fun.
-		 */
-		var aiTextureType_OPACITY = 0x8;
-		/** Displacement texture
-		 *
-		 *  The exact purpose and format is application-dependent.
-		 *  Higher color values stand for higher vertex displacements.
-		 */
-		var aiTextureType_DISPLACEMENT = 0x9;
-		/** Lightmap texture (aka Ambient Occlusion)
-		 *
-		 *  Both 'Lightmaps' and dedicated 'ambient occlusion maps' are
-		 *  covered by this material property. The texture contains a
-		 *  scaling value for the final color value of a pixel. Its
-		 *  intensity is not affected by incoming light.
-		 */
-		var aiTextureType_LIGHTMAP = 0xA;
-		/** Reflection texture
-		 *
-		 * Contains the color of a perfect mirror reflection.
-		 * Rarely used, almost never for real-time applications.
-		 */
-		var aiTextureType_REFLECTION = 0xB;
-		/** Unknown texture
-		 *
-		 *  A texture reference that does not match any of the definitions
-		 *  above is considered to be 'unknown'. It is still imported,
-		 *  but is excluded from any further postprocessing.
-		 */
-		var aiTextureType_UNKNOWN = 0xC;
-		var BONESPERVERT = 4;
-
-		function ASSBIN_MESH_HAS_TEXCOORD( n ) {
-
-			return ASSBIN_MESH_HAS_TEXCOORD_BASE << n;
-
-		}
-
-		function ASSBIN_MESH_HAS_COLOR( n ) {
-
-			return ASSBIN_MESH_HAS_COLOR_BASE << n;
-
-		}
-
-		function markBones( scene ) {
-
-			for ( var i in scene.mMeshes ) {
-
-				var mesh = scene.mMeshes[ i ];
-				for ( var k in mesh.mBones ) {
-
-					var boneNode = scene.findNode( mesh.mBones[ k ].mName );
-					if ( boneNode )
-						boneNode.isBone = true;
-
-				}
-
-			}
-
-		}
-		function cloneTreeToBones( root, scene ) {
-
-			var rootBone = new THREE.Bone();
-			rootBone.matrix.copy( root.matrix );
-			rootBone.matrixWorld.copy( root.matrixWorld );
-			rootBone.position.copy( root.position );
-			rootBone.quaternion.copy( root.quaternion );
-			rootBone.scale.copy( root.scale );
-			scene.nodeCount ++;
-			rootBone.name = "bone_" + root.name + scene.nodeCount.toString();
-
-			if ( ! scene.nodeToBoneMap[ root.name ] )
-				scene.nodeToBoneMap[ root.name ] = [];
-			scene.nodeToBoneMap[ root.name ].push( rootBone );
-			for ( var i in root.children ) {
-
-				var child = cloneTreeToBones( root.children[ i ], scene );
-				if ( child )
-					rootBone.add( child );
-
-			}
-
-			return rootBone;
-
-		}
-
-		function sortWeights( indexes, weights ) {
-
-			var pairs = [];
-
-			for ( var i = 0; i < indexes.length; i ++ ) {
-
-				pairs.push( {
-					i: indexes[ i ],
-					w: weights[ i ]
-				} );
-
-			}
-
-			pairs.sort( function ( a, b ) {
-
-				return b.w - a.w;
-
-			 } );
-
-			while ( pairs.length < 4 ) {
-
-				pairs.push( {
-					i: 0,
-					w: 0
-				} );
-
-			}
-
-			if ( pairs.length > 4 )
-				pairs.length = 4;
-			var sum = 0;
-
-			for ( var i = 0; i < 4; i ++ ) {
-
-				sum += pairs[ i ].w * pairs[ i ].w;
-
-			}
-
-			sum = Math.sqrt( sum );
-
-			for ( var i = 0; i < 4; i ++ ) {
-
-				pairs[ i ].w = pairs[ i ].w / sum;
-				indexes[ i ] = pairs[ i ].i;
-				weights[ i ] = pairs[ i ].w;
-
-			}
-
-		}
-
-		function findMatchingBone( root, name ) {
-
-			if ( root.name.indexOf( "bone_" + name ) == 0 )
-				return root;
-
-			for ( var i in root.children ) {
-
-				var ret = findMatchingBone( root.children[ i ], name );
-
-				if ( ret )
-					return ret;
-
-			}
-
-			return undefined;
-
-		}
-
-		function aiMesh() {
-
-			this.mPrimitiveTypes = 0;
-			this.mNumVertices = 0;
-			this.mNumFaces = 0;
-			this.mNumBones = 0;
-			this.mMaterialIndex = 0;
-			this.mVertices = [];
-			this.mNormals = [];
-			this.mTangents = [];
-			this.mBitangents = [];
-			this.mColors = [
-				[]
-			];
-			this.mTextureCoords = [
-				[]
-			];
-			this.mFaces = [];
-			this.mBones = [];
-			this.hookupSkeletons = function ( scene, threeScene ) {
-
-				if ( this.mBones.length == 0 ) return;
-
-				var allBones = [];
-				var offsetMatrix = [];
-				var skeletonRoot = scene.findNode( this.mBones[ 0 ].mName );
-
-				while ( skeletonRoot.mParent && skeletonRoot.mParent.isBone ) {
-
-					skeletonRoot = skeletonRoot.mParent;
-
-				}
-
-				var threeSkeletonRoot = skeletonRoot.toTHREE( scene );
-				var threeSkeletonRootBone = cloneTreeToBones( threeSkeletonRoot, scene );
-				this.threeNode.add( threeSkeletonRootBone );
-
-				for ( var i = 0; i < this.mBones.length; i ++ ) {
-
-					var bone = findMatchingBone( threeSkeletonRootBone, this.mBones[ i ].mName );
-
-					if ( bone ) {
-
-						var tbone = bone;
-						allBones.push( tbone );
-						//tbone.matrixAutoUpdate = false;
-						offsetMatrix.push( this.mBones[ i ].mOffsetMatrix.toTHREE() );
-
-					} else {
-
-						var skeletonRoot = scene.findNode( this.mBones[ i ].mName );
-						if ( ! skeletonRoot ) return;
-						var threeSkeletonRoot = skeletonRoot.toTHREE( scene );
-						var threeSkeletonRootParent = threeSkeletonRoot.parent;
-						var threeSkeletonRootBone = cloneTreeToBones( threeSkeletonRoot, scene );
-						this.threeNode.add( threeSkeletonRootBone );
-						var bone = findMatchingBone( threeSkeletonRootBone, this.mBones[ i ].mName );
-						var tbone = bone;
-						allBones.push( tbone );
-						//tbone.matrixAutoUpdate = false;
-						offsetMatrix.push( this.mBones[ i ].mOffsetMatrix.toTHREE() );
-
-					}
-
-				}
-				var skeleton = new THREE.Skeleton( allBones, offsetMatrix );
-
-				this.threeNode.bind( skeleton, new THREE.Matrix4() );
-				this.threeNode.material.skinning = true;
-
-			};
-
-			this.toTHREE = function ( scene ) {
-
-				if ( this.threeNode ) return this.threeNode;
-				var geometry = new THREE.BufferGeometry();
-				var mat;
-				if ( scene.mMaterials[ this.mMaterialIndex ] )
-					mat = scene.mMaterials[ this.mMaterialIndex ].toTHREE( scene );
-				else
-					mat = new THREE.MeshLambertMaterial();
-				geometry.setIndex( new THREE.BufferAttribute( new Uint32Array( this.mIndexArray ), 1 ) );
-				geometry.addAttribute( 'position', new THREE.BufferAttribute( this.mVertexBuffer, 3 ) );
-				if ( this.mNormalBuffer && this.mNormalBuffer.length > 0 )
-					geometry.addAttribute( 'normal', new THREE.BufferAttribute( this.mNormalBuffer, 3 ) );
-				if ( this.mColorBuffer && this.mColorBuffer.length > 0 )
-					geometry.addAttribute( 'color', new THREE.BufferAttribute( this.mColorBuffer, 4 ) );
-				if ( this.mTexCoordsBuffers[ 0 ] && this.mTexCoordsBuffers[ 0 ].length > 0 )
-					geometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( this.mTexCoordsBuffers[ 0 ] ), 2 ) );
-				if ( this.mTexCoordsBuffers[ 1 ] && this.mTexCoordsBuffers[ 1 ].length > 0 )
-					geometry.addAttribute( 'uv1', new THREE.BufferAttribute( new Float32Array( this.mTexCoordsBuffers[ 1 ] ), 2 ) );
-				if ( this.mTangentBuffer && this.mTangentBuffer.length > 0 )
-					geometry.addAttribute( 'tangents', new THREE.BufferAttribute( this.mTangentBuffer, 3 ) );
-				if ( this.mBitangentBuffer && this.mBitangentBuffer.length > 0 )
-					geometry.addAttribute( 'bitangents', new THREE.BufferAttribute( this.mBitangentBuffer, 3 ) );
-				if ( this.mBones.length > 0 ) {
-
-					var weights = [];
-					var bones = [];
-
-					for ( var i = 0; i < this.mBones.length; i ++ ) {
-
-						for ( var j = 0; j < this.mBones[ i ].mWeights.length; j ++ ) {
-
-							var weight = this.mBones[ i ].mWeights[ j ];
-							if ( weight ) {
-
-								if ( ! weights[ weight.mVertexId ] ) weights[ weight.mVertexId ] = [];
-								if ( ! bones[ weight.mVertexId ] ) bones[ weight.mVertexId ] = [];
-								weights[ weight.mVertexId ].push( weight.mWeight );
-								bones[ weight.mVertexId ].push( parseInt( i ) );
-
-							}
-
-						}
-
-					}
-
-					for ( var i in bones ) {
-
-						sortWeights( bones[ i ], weights[ i ] );
-
-					}
-
-					var _weights = [];
-					var _bones = [];
-
-					for ( var i = 0; i < weights.length; i ++ ) {
-
-						for ( var j = 0; j < 4; j ++ ) {
-
-							if ( weights[ i ] && bones[ i ] ) {
-
-								_weights.push( weights[ i ][ j ] );
-								_bones.push( bones[ i ][ j ] );
-
-							} else {
-
-								_weights.push( 0 );
-								_bones.push( 0 );
-
-							}
-
-						}
-
-					}
-
-					geometry.addAttribute( 'skinWeight', new THREE.BufferAttribute( new Float32Array( _weights ), BONESPERVERT ) );
-					geometry.addAttribute( 'skinIndex', new THREE.BufferAttribute( new Float32Array( _bones ), BONESPERVERT ) );
-
-				}
-
-				var mesh;
-
-				if ( this.mBones.length == 0 )
-					mesh = new THREE.Mesh( geometry, mat );
-
-				if ( this.mBones.length > 0 )
-					mesh = new THREE.SkinnedMesh( geometry, mat );
-
-				this.threeNode = mesh;
-				//mesh.matrixAutoUpdate = false;
-				return mesh;
-
-			};
-
-		}
-
-		function aiFace() {
-
-			this.mNumIndices = 0;
-			this.mIndices = [];
-
-		}
-
-		function aiVector3D() {
-
-			this.x = 0;
-			this.y = 0;
-			this.z = 0;
-
-			this.toTHREE = function () {
-
-				return new THREE.Vector3( this.x, this.y, this.z );
-
-			};
-
-		}
-
-		function aiVector2D() {
-
-			this.x = 0;
-			this.y = 0;
-			this.toTHREE = function () {
-
-				return new THREE.Vector2( this.x, this.y );
-
-			};
-
-		}
-
-		function aiVector4D() {
-
-			this.w = 0;
-			this.x = 0;
-			this.y = 0;
-			this.z = 0;
-			this.toTHREE = function () {
-
-				return new THREE.Vector4( this.w, this.x, this.y, this.z );
-
-			};
-
-		}
-
-		function aiColor4D() {
-
-			this.r = 0;
-			this.g = 0;
-			this.b = 0;
-			this.a = 0;
-			this.toTHREE = function () {
-
-				return new THREE.Color( this.r, this.g, this.b, this.a );
-
-			};
-
-		}
-
-		function aiColor3D() {
-
-			this.r = 0;
-			this.g = 0;
-			this.b = 0;
-			this.a = 0;
-			this.toTHREE = function () {
-
-				return new THREE.Color( this.r, this.g, this.b, 1 );
-
-			};
-
-		}
-
-		function aiQuaternion() {
-
-			this.x = 0;
-			this.y = 0;
-			this.z = 0;
-			this.w = 0;
-			this.toTHREE = function () {
-
-				return new THREE.Quaternion( this.x, this.y, this.z, this.w );
-
-			};
-
-		}
-
-		function aiVertexWeight() {
-
-			this.mVertexId = 0;
-			this.mWeight = 0;
-
-		}
-
-		function aiString() {
-
-			this.data = [];
-			this.toString = function () {
-
-				var str = '';
-				this.data.forEach( function ( i ) {
-
-					str += ( String.fromCharCode( i ) );
-
-				} );
-				return str.replace( /[^\x20-\x7E]+/g, '' );
-
-			};
-
-		}
-
-		function aiVectorKey() {
-
-			this.mTime = 0;
-			this.mValue = null;
-
-		}
-
-		function aiQuatKey() {
-
-			this.mTime = 0;
-			this.mValue = null;
-
-		}
-
-		function aiNode() {
-
-			this.mName = '';
-			this.mTransformation = [];
-			this.mNumChildren = 0;
-			this.mNumMeshes = 0;
-			this.mMeshes = [];
-			this.mChildren = [];
-			this.toTHREE = function ( scene ) {
-
-				if ( this.threeNode ) return this.threeNode;
-				var o = new THREE.Object3D();
-				o.name = this.mName;
-				o.matrix = this.mTransformation.toTHREE();
-
-				for ( var i = 0; i < this.mChildren.length; i ++ ) {
-
-					o.add( this.mChildren[ i ].toTHREE( scene ) );
-
-				}
-
-				for ( var i = 0; i < this.mMeshes.length; i ++ ) {
-
-					o.add( scene.mMeshes[ this.mMeshes[ i ] ].toTHREE( scene ) );
-
-				}
-
-				this.threeNode = o;
-				//o.matrixAutoUpdate = false;
-				o.matrix.decompose( o.position, o.quaternion, o.scale );
-				return o;
-
-			};
-
-		}
-
-		function aiBone() {
-
-			this.mName = '';
-			this.mNumWeights = 0;
-			this.mOffsetMatrix = 0;
-
-		}
-
-		function aiMaterialProperty() {
-
-			this.mKey = "";
-			this.mSemantic = 0;
-			this.mIndex = 0;
-			this.mData = [];
-			this.mDataLength = 0;
-			this.mType = 0;
-			this.dataAsColor = function () {
-
-				var array = ( new Uint8Array( this.mData ) ).buffer;
-				var reader = new DataView( array );
-				var r = reader.getFloat32( 0, true );
-				var g = reader.getFloat32( 4, true );
-				var b = reader.getFloat32( 8, true );
-				//var a = reader.getFloat32(12, true);
-				return new THREE.Color( r, g, b );
-
-			};
-
-			this.dataAsFloat = function () {
-
-				var array = ( new Uint8Array( this.mData ) ).buffer;
-				var reader = new DataView( array );
-				var r = reader.getFloat32( 0, true );
-				return r;
-
-			};
-
-			this.dataAsBool = function () {
-
-				var array = ( new Uint8Array( this.mData ) ).buffer;
-				var reader = new DataView( array );
-				var r = reader.getFloat32( 0, true );
-				return !! r;
-
-			};
-
-			this.dataAsString = function () {
-
-				var s = new aiString();
-				s.data = this.mData;
-				return s.toString();
-
-			};
-
-			this.dataAsMap = function () {
-
-				var s = new aiString();
-				s.data = this.mData;
-				var path = s.toString();
-				path = path.replace( /\\/g, '/' );
-
-				if ( path.indexOf( '/' ) != - 1 ) {
-
-					path = path.substr( path.lastIndexOf( '/' ) + 1 );
-
-				}
-
-				return textureLoader.load( path );
-
-			};
-
-		}
-		var namePropMapping = {
-
-			"?mat.name": "name",
-			"$mat.shadingm": "shading",
-			"$mat.twosided": "twoSided",
-			"$mat.wireframe": "wireframe",
-			"$clr.ambient": "ambient",
-			"$clr.diffuse": "color",
-			"$clr.specular": "specular",
-			"$clr.emissive": "emissive",
-			"$clr.transparent": "transparent",
-			"$clr.reflective": "reflect",
-			"$mat.shininess": "shininess",
-			"$mat.reflectivity": "reflectivity",
-			"$mat.refracti": "refraction",
-			"$tex.file": "map"
-
-		};
-
-		var nameTypeMapping = {
-
-			"?mat.name": "string",
-			"$mat.shadingm": "bool",
-			"$mat.twosided": "bool",
-			"$mat.wireframe": "bool",
-			"$clr.ambient": "color",
-			"$clr.diffuse": "color",
-			"$clr.specular": "color",
-			"$clr.emissive": "color",
-			"$clr.transparent": "color",
-			"$clr.reflective": "color",
-			"$mat.shininess": "float",
-			"$mat.reflectivity": "float",
-			"$mat.refracti": "float",
-			"$tex.file": "map"
-
-		};
-
-		function aiMaterial() {
-
-			this.mNumAllocated = 0;
-			this.mNumProperties = 0;
-			this.mProperties = [];
-			this.toTHREE = function ( scene ) {
-
-				var name = this.mProperties[ 0 ].dataAsString();
-				var mat = new THREE.MeshPhongMaterial();
-
-				for ( var i = 0; i < this.mProperties.length; i ++ ) {
-
-					if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'float' )
-						mat[ namePropMapping[ this.mProperties[ i ].mKey ] ] = this.mProperties[ i ].dataAsFloat();
-					if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'color' )
-						mat[ namePropMapping[ this.mProperties[ i ].mKey ] ] = this.mProperties[ i ].dataAsColor();
-					if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'bool' )
-						mat[ namePropMapping[ this.mProperties[ i ].mKey ] ] = this.mProperties[ i ].dataAsBool();
-					if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'string' )
-						mat[ namePropMapping[ this.mProperties[ i ].mKey ] ] = this.mProperties[ i ].dataAsString();
-					if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'map' ) {
-
-						var prop = this.mProperties[ i ];
-						if ( prop.mSemantic == aiTextureType_DIFFUSE )
-							mat.map = this.mProperties[ i ].dataAsMap();
-						if ( prop.mSemantic == aiTextureType_NORMALS )
-							mat.normalMap = this.mProperties[ i ].dataAsMap();
-						if ( prop.mSemantic == aiTextureType_LIGHTMAP )
-							mat.lightMap = this.mProperties[ i ].dataAsMap();
-						if ( prop.mSemantic == aiTextureType_OPACITY )
-							mat.alphaMap = this.mProperties[ i ].dataAsMap();
-
-					}
-
-				}
-
-				mat.ambient.r = .53;
-				mat.ambient.g = .53;
-				mat.ambient.b = .53;
-				mat.color.r = 1;
-				mat.color.g = 1;
-				mat.color.b = 1;
-				return mat;
-
-			};
-
-		}
-
-
-		function veclerp( v1, v2, l ) {
-
-			var v = new THREE.Vector3();
-			var lm1 = 1 - l;
-			v.x = v1.x * l + v2.x * lm1;
-			v.y = v1.y * l + v2.y * lm1;
-			v.z = v1.z * l + v2.z * lm1;
-			return v;
-
-		}
-
-		function quatlerp( q1, q2, l ) {
-
-			return q1.clone().slerp( q2, 1 - l );
-
-		}
-
-		function sampleTrack( keys, time, lne, lerp ) {
-
-			if ( keys.length == 1 ) return keys[ 0 ].mValue.toTHREE();
-
-			var dist = Infinity;
-			var key = null;
-			var nextKey = null;
-
-			for ( var i = 0; i < keys.length; i ++ ) {
-
-				var timeDist = Math.abs( keys[ i ].mTime - time );
-
-				if ( timeDist < dist && keys[ i ].mTime <= time ) {
-
-					dist = timeDist;
-					key = keys[ i ];
-					nextKey = keys[ i + 1 ];
-
-				}
-
-			}
-
-			if ( ! key ) {
-				
-				return null;
-
-			} else if ( nextKey ) {
-
-				var dT = nextKey.mTime - key.mTime;
-				var T = key.mTime - time;
-				var l = T / dT;
-
-				return lerp( key.mValue.toTHREE(), nextKey.mValue.toTHREE(), l );
-
-			} else {
-
-				nextKey = keys[ 0 ].clone();
-				nextKey.mTime += lne;
-
-				var dT = nextKey.mTime - key.mTime;
-				var T = key.mTime - time;
-				var l = T / dT;
-
-				return lerp( key.mValue.toTHREE(), nextKey.mValue.toTHREE(), l );
-				
-			}
-
-		}
-
-		function aiNodeAnim() {
-
-			this.mNodeName = "";
-			this.mNumPositionKeys = 0;
-			this.mNumRotationKeys = 0;
-			this.mNumScalingKeys = 0;
-			this.mPositionKeys = [];
-			this.mRotationKeys = [];
-			this.mScalingKeys = [];
-			this.mPreState = "";
-			this.mPostState = "";
-			this.init = function ( tps ) {
-
-				if ( ! tps ) tps = 1;
-
-				function t( t ) {
-
-					t.mTime /= tps;
-
-				}
-
-				this.mPositionKeys.forEach( t );
-				this.mRotationKeys.forEach( t );
-				this.mScalingKeys.forEach( t );
-
-			};
-
-			this.sortKeys = function () {
-
-				function comp( a, b ) {
-
-					return a.mTime - b.mTime;
-
-				}
-
-				this.mPositionKeys.sort( comp );
-				this.mRotationKeys.sort( comp );
-				this.mScalingKeys.sort( comp );
-
-			};
-
-			this.getLength = function () {
-
-				return Math.max(
-					Math.max.apply( null, this.mPositionKeys.map( function ( a ) {
-
-						return a.mTime;
-
-					} ) ),
-					Math.max.apply( null, this.mRotationKeys.map( function ( a ) {
-
-						return a.mTime;
-
-					} ) ),
-					Math.max.apply( null, this.mScalingKeys.map( function ( a ) {
-
-						return a.mTime;
-
-				 } ) )
-				);
-
-			};
-
-			this.toTHREE = function ( o, tps ) {
-
-				this.sortKeys();
-				var length = this.getLength();
-				var track = new Virtulous.KeyFrameTrack();
-
-				for ( var i = 0; i < length; i += .05 ) {
-
-					var matrix = new THREE.Matrix4();
-					var time = i;
-					var pos = sampleTrack( this.mPositionKeys, time, length, veclerp );
-					var scale = sampleTrack( this.mScalingKeys, time, length, veclerp );
-					var rotation = sampleTrack( this.mRotationKeys, time, length, quatlerp );
-					matrix.compose( pos, rotation, scale );
-
-					var key = new Virtulous.KeyFrame( time, matrix );
-					track.addKey( key );
-
-				}
-
-				track.target = o.findNode( this.mNodeName ).toTHREE();
-
-				var tracks = [ track ];
-
-				if ( o.nodeToBoneMap[ this.mNodeName ] ) {
-
-					for ( var i = 0; i < o.nodeToBoneMap[ this.mNodeName ].length; i ++ ) {
-
-						var t2 = track.clone();
-						t2.target = o.nodeToBoneMap[ this.mNodeName ][ i ];
-						tracks.push( t2 );
-
-					}
-
-				}
-
-				return tracks;
-
-			};
-
-		}
-
-		function aiAnimation() {
-
-			this.mName = "";
-			this.mDuration = 0;
-			this.mTicksPerSecond = 0;
-			this.mNumChannels = 0;
-			this.mChannels = [];
-			this.toTHREE = function ( root ) {
-
-				var animationHandle = new Virtulous.Animation();
-
-				for ( var i in this.mChannels ) {
-
-					this.mChannels[ i ].init( this.mTicksPerSecond );
-
-					var tracks = this.mChannels[ i ].toTHREE( root );
-
-					for ( var j in tracks ) {
-
-						tracks[ j ].init();
-						animationHandle.addTrack( tracks[ j ] );
-
-					}
-
-				}
-
-				animationHandle.length = Math.max.apply( null, animationHandle.tracks.map( function ( e ) {
-
-					return e.length;
-
-				} ) );
-				return animationHandle;
-
-			};
-
-		}
-
-		function aiTexture() {
-
-			this.mWidth = 0;
-			this.mHeight = 0;
-			this.texAchFormatHint = [];
-			this.pcData = [];
-
-		}
-
-		function aiLight() {
-
-			this.mName = '';
-			this.mType = 0;
-			this.mAttenuationConstant = 0;
-			this.mAttenuationLinear = 0;
-			this.mAttenuationQuadratic = 0;
-			this.mAngleInnerCone = 0;
-			this.mAngleOuterCone = 0;
-			this.mColorDiffuse = null;
-			this.mColorSpecular = null;
-			this.mColorAmbient = null;
-
-		}
-
-		function aiCamera() {
-
-			this.mName = '';
-			this.mPosition = null;
-			this.mLookAt = null;
-			this.mUp = null;
-			this.mHorizontalFOV = 0;
-			this.mClipPlaneNear = 0;
-			this.mClipPlaneFar = 0;
-			this.mAspect = 0;
-
-		}
-
-		function aiScene() {
-
-			this.mFlags = 0;
-			this.mNumMeshes = 0;
-			this.mNumMaterials = 0;
-			this.mNumAnimations = 0;
-			this.mNumTextures = 0;
-			this.mNumLights = 0;
-			this.mNumCameras = 0;
-			this.mRootNode = null;
-			this.mMeshes = [];
-			this.mMaterials = [];
-			this.mAnimations = [];
-			this.mLights = [];
-			this.mCameras = [];
-			this.nodeToBoneMap = {};
-			this.findNode = function ( name, root ) {
-
-				if ( ! root ) {
-
-					root = this.mRootNode;
-
-				}
-
-				if ( root.mName == name ) {
-
-					return root;
-
-				}
-
-				for ( var i = 0; i < root.mChildren.length; i ++ ) {
-
-					var ret = this.findNode( name, root.mChildren[ i ] );
-					if ( ret ) return ret;
-
-				}
-
-				return null;
-
-			};
-
-			this.toTHREE = function () {
-
-				this.nodeCount = 0;
-
-				markBones( this );
-
-				var o = this.mRootNode.toTHREE( this );
-
-				for ( var i in this.mMeshes )
-					this.mMeshes[ i ].hookupSkeletons( this, o );
-
-				if ( this.mAnimations.length > 0 ) {
-
-					var a = this.mAnimations[ 0 ].toTHREE( this );
-
-				}
-
-				return { object: o, animation: a };
-
-			};
-
-		}
-
-		function aiMatrix4() {
-
-			this.elements = [
-				[],
-				[],
-				[],
-				[]
-			];
-			this.toTHREE = function () {
-
-				var m = new THREE.Matrix4();
-
-				for ( var i = 0; i < 4; ++ i ) {
-
-					for ( var i2 = 0; i2 < 4; ++ i2 ) {
-
-						m.elements[ i * 4 + i2 ] = this.elements[ i2 ][ i ];
-
-					}
-
-				}
-
-				return m;
-
-			};
-
-		}
-
-		var littleEndian = true;
-
-		function readFloat( dataview ) {
-
-			var val = dataview.getFloat32( dataview.readOffset, littleEndian );
-			dataview.readOffset += 4;
-			return val;
-
-		}
-
-		function Read_double( dataview ) {
-
-			var val = dataview.getFloat64( dataview.readOffset, littleEndian );
-			dataview.readOffset += 8;
-			return val;
-
-		}
-
-		function Read_uint8_t( dataview ) {
-
-			var val = dataview.getUint8( dataview.readOffset );
-			dataview.readOffset += 1;
-			return val;
-
-		}
-
-		function Read_uint16_t( dataview ) {
-
-			var val = dataview.getUint16( dataview.readOffset, littleEndian );
-			dataview.readOffset += 2;
-			return val;
-
-		}
-
-		function Read_unsigned_int( dataview ) {
-
-			var val = dataview.getUint32( dataview.readOffset, littleEndian );
-			dataview.readOffset += 4;
-			return val;
-
-		}
-
-		function Read_uint32_t( dataview ) {
-
-			var val = dataview.getUint32( dataview.readOffset, littleEndian );
-			dataview.readOffset += 4;
-			return val;
-
-		}
-
-		function Read_aiVector3D( stream ) {
-
-			var v = new aiVector3D();
-			v.x = readFloat( stream );
-			v.y = readFloat( stream );
-			v.z = readFloat( stream );
-			return v;
-
-		}
-
-		function Read_aiVector2D( stream ) {
-
-			var v = new aiVector2D();
-			v.x = readFloat( stream );
-			v.y = readFloat( stream );
-			return v;
-
-		}
-
-		function Read_aiVector4D( stream ) {
-
-			var v = new aiVector4D();
-			v.w = readFloat( stream );
-			v.x = readFloat( stream );
-			v.y = readFloat( stream );
-			v.z = readFloat( stream );
-			return v;
-
-		}
-
-		function Read_aiColor3D( stream ) {
-
-			var c = new aiColor3D();
-			c.r = readFloat( stream );
-			c.g = readFloat( stream );
-			c.b = readFloat( stream );
-			return c;
-
-		}
-
-		function Read_aiColor4D( stream ) {
-
-			var c = new aiColor4D();
-			c.r = readFloat( stream );
-			c.g = readFloat( stream );
-			c.b = readFloat( stream );
-			c.a = readFloat( stream );
-			return c;
-
-		}
-
-		function Read_aiQuaternion( stream ) {
-
-			var v = new aiQuaternion();
-			v.w = readFloat( stream );
-			v.x = readFloat( stream );
-			v.y = readFloat( stream );
-			v.z = readFloat( stream );
-			return v;
-
-		}
-
-		function Read_aiString( stream ) {
-
-			var s = new aiString();
-			var stringlengthbytes = Read_unsigned_int( stream );
-			stream.ReadBytes( s.data, 1, stringlengthbytes );
-			return s.toString();
-
-		}
-
-		function Read_aiVertexWeight( stream ) {
-
-			var w = new aiVertexWeight();
-			w.mVertexId = Read_unsigned_int( stream );
-			w.mWeight = readFloat( stream );
-			return w;
-
-		}
-
-		function Read_aiMatrix4x4( stream ) {
-
-			var m = new aiMatrix4();
-
-			for ( var i = 0; i < 4; ++ i ) {
-
-				for ( var i2 = 0; i2 < 4; ++ i2 ) {
-
-					m.elements[ i ][ i2 ] = readFloat( stream );
-
-				}
-
-			}
-
-			return m;
-
-		}
-
-		function Read_aiVectorKey( stream ) {
-
-			var v = new aiVectorKey();
-			v.mTime = Read_double( stream );
-			v.mValue = Read_aiVector3D( stream );
-			return v;
-
-		}
-
-		function Read_aiQuatKey( stream ) {
-
-			var v = new aiQuatKey();
-			v.mTime = Read_double( stream );
-			v.mValue = Read_aiQuaternion( stream );
-			return v;
-
-		}
-
-		function ReadArray( stream, data, size ) {
-
-			for ( var i = 0; i < size; i ++ ) data[ i ] = Read( stream );
-
-		}
-
-		function ReadArray_aiVector2D( stream, data, size ) {
-
-			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVector2D( stream );
-
-		}
-
-		function ReadArray_aiVector3D( stream, data, size ) {
-
-			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVector3D( stream );
-
-		}
-
-		function ReadArray_aiVector4D( stream, data, size ) {
-
-			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVector4D( stream );
-
-		}
-
-		function ReadArray_aiVertexWeight( stream, data, size ) {
-
-			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVertexWeight( stream );
-
-		}
-
-		function ReadArray_aiColor4D( stream, data, size ) {
-
-			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiColor4D( stream );
-
-		}
-
-		function ReadArray_aiVectorKey( stream, data, size ) {
-
-			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVectorKey( stream );
-
-		}
-
-		function ReadArray_aiQuatKey( stream, data, size ) {
-
-			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiQuatKey( stream );
-
-		}
-
-		function ReadBounds( stream, T /*p*/, n ) {
-
-			// not sure what to do here, the data isn't really useful.
-			return stream.Seek( sizeof( T ) * n, aiOrigin_CUR );
-
-		}
-
-		function ai_assert( bool ) {
-
-			if ( ! bool )
-				throw ( "asset failed" );
-
-		}
-
-		function ReadBinaryNode( stream, parent, depth ) {
-
-			var chunkID = Read_uint32_t( stream );
-			ai_assert( chunkID == ASSBIN_CHUNK_AINODE );
-			/*uint32_t size =*/
-			Read_uint32_t( stream );
-			var node = new aiNode();
-			node.mParent = parent;
-			node.mDepth = depth;
-			node.mName = Read_aiString( stream );
-			node.mTransformation = Read_aiMatrix4x4( stream );
-			node.mNumChildren = Read_unsigned_int( stream );
-			node.mNumMeshes = Read_unsigned_int( stream );
-
-			if ( node.mNumMeshes ) {
-
-				node.mMeshes = [];
-
-				for ( var i = 0; i < node.mNumMeshes; ++ i ) {
-
-					node.mMeshes[ i ] = Read_unsigned_int( stream );
-
-				}
-
-			}
-
-			if ( node.mNumChildren ) {
-
-				node.mChildren = [];
-
-				for ( var i = 0; i < node.mNumChildren; ++ i ) {
-
-					var node2 = ReadBinaryNode( stream, node, depth ++ );
-					node.mChildren[ i ] = node2;
-
-				}
-
-			}
-
-			return node;
-
-		}
-
-		// -----------------------------------------------------------------------------------
-
-		function ReadBinaryBone( stream, b ) {
-
-			var chunkID = Read_uint32_t( stream );
-			ai_assert( chunkID == ASSBIN_CHUNK_AIBONE );
-			/*uint32_t size =*/
-			Read_uint32_t( stream );
-			b.mName = Read_aiString( stream );
-			b.mNumWeights = Read_unsigned_int( stream );
-			b.mOffsetMatrix = Read_aiMatrix4x4( stream );
-			// for the moment we write dumb min/max values for the bones, too.
-			// maybe I'll add a better, hash-like solution later
-			if ( shortened ) {
-
-				ReadBounds( stream, b.mWeights, b.mNumWeights );
-
-			} else {
-
-				// else write as usual
-
-				b.mWeights = [];
-				ReadArray_aiVertexWeight( stream, b.mWeights, b.mNumWeights );
-
-			}
-
-			return b;
-
-		}
-
-		function ReadBinaryMesh( stream, mesh ) {
-
-			var chunkID = Read_uint32_t( stream );
-			ai_assert( chunkID == ASSBIN_CHUNK_AIMESH );
-			/*uint32_t size =*/
-			Read_uint32_t( stream );
-			mesh.mPrimitiveTypes = Read_unsigned_int( stream );
-			mesh.mNumVertices = Read_unsigned_int( stream );
-			mesh.mNumFaces = Read_unsigned_int( stream );
-			mesh.mNumBones = Read_unsigned_int( stream );
-			mesh.mMaterialIndex = Read_unsigned_int( stream );
-			mesh.mNumUVComponents = [];
-			// first of all, write bits for all existent vertex components
-			var c = Read_unsigned_int( stream );
-
-			if ( c & ASSBIN_MESH_HAS_POSITIONS ) {
-
-				if ( shortened ) {
-
-					ReadBounds( stream, mesh.mVertices, mesh.mNumVertices );
-
-				} else {
-
-					// else write as usual
-
-					mesh.mVertices = [];
-					mesh.mVertexBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 3 * 4 );
-					stream.Seek( mesh.mNumVertices * 3 * 4, aiOrigin_CUR );
-
-				}
-
-			}
-
-			if ( c & ASSBIN_MESH_HAS_NORMALS ) {
-
-				if ( shortened ) {
-
-					ReadBounds( stream, mesh.mNormals, mesh.mNumVertices );
-
-				} else {
-
-					// else write as usual
-
-					mesh.mNormals = [];
-					mesh.mNormalBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 3 * 4 );
-					stream.Seek( mesh.mNumVertices * 3 * 4, aiOrigin_CUR );
-
-				}
-
-			}
-
-			if ( c & ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS ) {
-
-				if ( shortened ) {
-
-					ReadBounds( stream, mesh.mTangents, mesh.mNumVertices );
-					ReadBounds( stream, mesh.mBitangents, mesh.mNumVertices );
-
-				} else {
-
-					// else write as usual
-
-					mesh.mTangents = [];
-					mesh.mTangentBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 3 * 4 );
-					stream.Seek( mesh.mNumVertices * 3 * 4, aiOrigin_CUR );
-					mesh.mBitangents = [];
-					mesh.mBitangentBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 3 * 4 );
-					stream.Seek( mesh.mNumVertices * 3 * 4, aiOrigin_CUR );
-
-				}
-
-			}
-
-			for ( var n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++ n ) {
-
-				if ( ! ( c & ASSBIN_MESH_HAS_COLOR( n ) ) ) break;
-
-				if ( shortened ) {
-
-					ReadBounds( stream, mesh.mColors[ n ], mesh.mNumVertices );
-
-				} else {
-
-					// else write as usual
-
-					mesh.mColors[ n ] = [];
-					mesh.mColorBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 4 * 4 );
-					stream.Seek( mesh.mNumVertices * 4 * 4, aiOrigin_CUR );
-
-				}
-
-			}
-
-			mesh.mTexCoordsBuffers = [];
-
-			for ( var n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++ n ) {
-
-				if ( ! ( c & ASSBIN_MESH_HAS_TEXCOORD( n ) ) ) break;
-
-				// write number of UV components
-				mesh.mNumUVComponents[ n ] = Read_unsigned_int( stream );
-
-				if ( shortened ) {
-
-					ReadBounds( stream, mesh.mTextureCoords[ n ], mesh.mNumVertices );
-
-				} else {
-
-				// else write as usual
-
-					mesh.mTextureCoords[ n ] = [];
-					//note that assbin always writes 3d texcoords
-					mesh.mTexCoordsBuffers[ n ] = [];
-
-					for ( var uv = 0; uv < mesh.mNumVertices; uv ++ ) {
-
-						mesh.mTexCoordsBuffers[ n ].push( readFloat( stream ) );
-						mesh.mTexCoordsBuffers[ n ].push( readFloat( stream ) );
-						readFloat( stream );
-
-					}
-
-				}
-
-			}
-			// write faces. There are no floating-point calculations involved
-			// in these, so we can write a simple hash over the face data
-			// to the dump file. We generate a single 32 Bit hash for 512 faces
-			// using Assimp's standard hashing function.
-			if ( shortened ) {
-
-				Read_unsigned_int( stream );
-
-			} else {
-
-				// else write as usual
-
-				// if there are less than 2^16 vertices, we can simply use 16 bit integers ...
-				mesh.mFaces = [];
-
-				var indexCounter = 0;
-				mesh.mIndexArray = [];
-
-				for ( var i = 0; i < mesh.mNumFaces; ++ i ) {
-
-					var f = mesh.mFaces[ i ] = new aiFace();
-					// BOOST_STATIC_ASSERT(AI_MAX_FACE_INDICES <= 0xffff);
-					f.mNumIndices = Read_uint16_t( stream );
-					f.mIndices = [];
-
-					for ( var a = 0; a < f.mNumIndices; ++ a ) {
-
-						if ( mesh.mNumVertices < ( 1 << 16 ) ) {
-
-							f.mIndices[ a ] = Read_uint16_t( stream );
-
-						} else {
-
-							f.mIndices[ a ] = Read_unsigned_int( stream );
-
-						}
-
-
-
-					}
-
-					if ( f.mNumIndices === 3 ) {
-
-						mesh.mIndexArray.push( f.mIndices[ 0 ] );
-						mesh.mIndexArray.push( f.mIndices[ 1 ] );
-						mesh.mIndexArray.push( f.mIndices[ 2 ] );
-
-					} else if ( f.mNumIndices === 4 ) {
-
-						mesh.mIndexArray.push( f.mIndices[ 0 ] );
-						mesh.mIndexArray.push( f.mIndices[ 1 ] );
-						mesh.mIndexArray.push( f.mIndices[ 2 ] );
-						mesh.mIndexArray.push( f.mIndices[ 2 ] );
-						mesh.mIndexArray.push( f.mIndices[ 3 ] );
-						mesh.mIndexArray.push( f.mIndices[ 0 ] );
-
-					} else {
-
-						throw ( new Error( "Sorry, can't currently triangulate polys. Use the triangulate preprocessor in Assimp." ) );
-
-					}
-
-
-
-				}
-
-			}
-			// write bones
-			if ( mesh.mNumBones ) {
-
-				mesh.mBones = [];
-
-				for ( var a = 0; a < mesh.mNumBones; ++ a ) {
-
-					mesh.mBones[ a ] = new aiBone();
-					ReadBinaryBone( stream, mesh.mBones[ a ] );
-
-				}
-
-			}
-
-		}
-
-		function ReadBinaryMaterialProperty( stream, prop ) {
-
-			var chunkID = Read_uint32_t( stream );
-			ai_assert( chunkID == ASSBIN_CHUNK_AIMATERIALPROPERTY );
-			/*uint32_t size =*/
-			Read_uint32_t( stream );
-			prop.mKey = Read_aiString( stream );
-			prop.mSemantic = Read_unsigned_int( stream );
-			prop.mIndex = Read_unsigned_int( stream );
-			prop.mDataLength = Read_unsigned_int( stream );
-			prop.mType = Read_unsigned_int( stream );
-			prop.mData = [];
-			stream.ReadBytes( prop.mData, 1, prop.mDataLength );
-
-		}
-
-		// -----------------------------------------------------------------------------------
-
-		function ReadBinaryMaterial( stream, mat ) {
-
-			var chunkID = Read_uint32_t( stream );
-			ai_assert( chunkID == ASSBIN_CHUNK_AIMATERIAL );
-			/*uint32_t size =*/
-			Read_uint32_t( stream );
-			mat.mNumAllocated = mat.mNumProperties = Read_unsigned_int( stream );
-
-			if ( mat.mNumProperties ) {
-
-				if ( mat.mProperties ) {
-
-					delete mat.mProperties;
-
-				}
-
-				mat.mProperties = [];
-
-				for ( var i = 0; i < mat.mNumProperties; ++ i ) {
-
-					mat.mProperties[ i ] = new aiMaterialProperty();
-					ReadBinaryMaterialProperty( stream, mat.mProperties[ i ] );
-
-				}
-
-			}
-
-		}
-		// -----------------------------------------------------------------------------------
-		function ReadBinaryNodeAnim( stream, nd ) {
-
-			var chunkID = Read_uint32_t( stream );
-			ai_assert( chunkID == ASSBIN_CHUNK_AINODEANIM );
-			/*uint32_t size =*/
-			Read_uint32_t( stream );
-			nd.mNodeName = Read_aiString( stream );
-			nd.mNumPositionKeys = Read_unsigned_int( stream );
-			nd.mNumRotationKeys = Read_unsigned_int( stream );
-			nd.mNumScalingKeys = Read_unsigned_int( stream );
-			nd.mPreState = Read_unsigned_int( stream );
-			nd.mPostState = Read_unsigned_int( stream );
-
-			if ( nd.mNumPositionKeys ) {
-
-				if ( shortened ) {
-
-					ReadBounds( stream, nd.mPositionKeys, nd.mNumPositionKeys );
-
-				} else {
-
-					// else write as usual
-
-					nd.mPositionKeys = [];
-					ReadArray_aiVectorKey( stream, nd.mPositionKeys, nd.mNumPositionKeys );
-
-				}
-
-			}
-
-			if ( nd.mNumRotationKeys ) {
-
-				if ( shortened ) {
-
-					ReadBounds( stream, nd.mRotationKeys, nd.mNumRotationKeys );
-
-				} else {
-
-		 			// else write as usual
-
-					nd.mRotationKeys = [];
-					ReadArray_aiQuatKey( stream, nd.mRotationKeys, nd.mNumRotationKeys );
-
-				}
-
-			}
-
-			if ( nd.mNumScalingKeys ) {
-
-				if ( shortened ) {
-
-					ReadBounds( stream, nd.mScalingKeys, nd.mNumScalingKeys );
-
-				} else {
-
-	 				// else write as usual
-
-					nd.mScalingKeys = [];
-					ReadArray_aiVectorKey( stream, nd.mScalingKeys, nd.mNumScalingKeys );
-
-				}
-
-			}
-
-		}
-		// -----------------------------------------------------------------------------------
-		function ReadBinaryAnim( stream, anim ) {
-
-			var chunkID = Read_uint32_t( stream );
-			ai_assert( chunkID == ASSBIN_CHUNK_AIANIMATION );
-			/*uint32_t size =*/
-			Read_uint32_t( stream );
-			anim.mName = Read_aiString( stream );
-			anim.mDuration = Read_double( stream );
-			anim.mTicksPerSecond = Read_double( stream );
-			anim.mNumChannels = Read_unsigned_int( stream );
-
-			if ( anim.mNumChannels ) {
-
-				anim.mChannels = [];
-
-				for ( var a = 0; a < anim.mNumChannels; ++ a ) {
-
-					anim.mChannels[ a ] = new aiNodeAnim();
-					ReadBinaryNodeAnim( stream, anim.mChannels[ a ] );
-
-				}
-
-			}
-
-		}
-
-		function ReadBinaryTexture( stream, tex ) {
-
-			var chunkID = Read_uint32_t( stream );
-			ai_assert( chunkID == ASSBIN_CHUNK_AITEXTURE );
-			/*uint32_t size =*/
-			Read_uint32_t( stream );
-			tex.mWidth = Read_unsigned_int( stream );
-			tex.mHeight = Read_unsigned_int( stream );
-			stream.ReadBytes( tex.achFormatHint, 1, 4 );
-
-			if ( ! shortened ) {
-
-				if ( ! tex.mHeight ) {
-
-					tex.pcData = [];
-					stream.ReadBytes( tex.pcData, 1, tex.mWidth );
-
-				} else {
-
-					tex.pcData = [];
-					stream.ReadBytes( tex.pcData, 1, tex.mWidth * tex.mHeight * 4 );
-
-				}
-
-			}
-
-		}
-		// -----------------------------------------------------------------------------------
-		function ReadBinaryLight( stream, l ) {
-
-			var chunkID = Read_uint32_t( stream );
-			ai_assert( chunkID == ASSBIN_CHUNK_AILIGHT );
-			/*uint32_t size =*/
-			Read_uint32_t( stream );
-			l.mName = Read_aiString( stream );
-			l.mType = Read_unsigned_int( stream );
-
-			if ( l.mType != aiLightSource_DIRECTIONAL ) {
-
-				l.mAttenuationConstant = readFloat( stream );
-				l.mAttenuationLinear = readFloat( stream );
-				l.mAttenuationQuadratic = readFloat( stream );
-
-			}
-
-			l.mColorDiffuse = Read_aiColor3D( stream );
-			l.mColorSpecular = Read_aiColor3D( stream );
-			l.mColorAmbient = Read_aiColor3D( stream );
-
-			if ( l.mType == aiLightSource_SPOT ) {
-
-				l.mAngleInnerCone = readFloat( stream );
-				l.mAngleOuterCone = readFloat( stream );
-
-			}
-
-		}
-		// -----------------------------------------------------------------------------------
-		function ReadBinaryCamera( stream, cam ) {
-
-			var chunkID = Read_uint32_t( stream );
-			ai_assert( chunkID == ASSBIN_CHUNK_AICAMERA );
-			/*uint32_t size =*/
-			Read_uint32_t( stream );
-			cam.mName = Read_aiString( stream );
-			cam.mPosition = Read_aiVector3D( stream );
-			cam.mLookAt = Read_aiVector3D( stream );
-			cam.mUp = Read_aiVector3D( stream );
-			cam.mHorizontalFOV = readFloat( stream );
-			cam.mClipPlaneNear = readFloat( stream );
-			cam.mClipPlaneFar = readFloat( stream );
-			cam.mAspect = readFloat( stream );
-
-		}
-
-		function ReadBinaryScene( stream, scene ) {
-
-			var chunkID = Read_uint32_t( stream );
-			ai_assert( chunkID == ASSBIN_CHUNK_AISCENE );
-			/*uint32_t size =*/
-			Read_uint32_t( stream );
-			scene.mFlags = Read_unsigned_int( stream );
-			scene.mNumMeshes = Read_unsigned_int( stream );
-			scene.mNumMaterials = Read_unsigned_int( stream );
-			scene.mNumAnimations = Read_unsigned_int( stream );
-			scene.mNumTextures = Read_unsigned_int( stream );
-			scene.mNumLights = Read_unsigned_int( stream );
-			scene.mNumCameras = Read_unsigned_int( stream );
-			// Read node graph
-			scene.mRootNode = new aiNode();
-			scene.mRootNode = ReadBinaryNode( stream, null, 0 );
-			// Read all meshes
-			if ( scene.mNumMeshes ) {
-
-				scene.mMeshes = [];
-
-				for ( var i = 0; i < scene.mNumMeshes; ++ i ) {
-
-					scene.mMeshes[ i ] = new aiMesh();
-					ReadBinaryMesh( stream, scene.mMeshes[ i ] );
-
-				}
-
-			}
-			// Read materials
-			if ( scene.mNumMaterials ) {
-
-				scene.mMaterials = [];
-
-				for ( var i = 0; i < scene.mNumMaterials; ++ i ) {
-
-					scene.mMaterials[ i ] = new aiMaterial();
-					ReadBinaryMaterial( stream, scene.mMaterials[ i ] );
-
-				}
-
-			}
-			// Read all animations
-			if ( scene.mNumAnimations ) {
-
-				scene.mAnimations = [];
-
-				for ( var i = 0; i < scene.mNumAnimations; ++ i ) {
-
-					scene.mAnimations[ i ] = new aiAnimation();
-					ReadBinaryAnim( stream, scene.mAnimations[ i ] );
-
-				}
-
-			}
-			// Read all textures
-			if ( scene.mNumTextures ) {
-
-				scene.mTextures = [];
-
-				for ( var i = 0; i < scene.mNumTextures; ++ i ) {
-
-					scene.mTextures[ i ] = new aiTexture();
-					ReadBinaryTexture( stream, scene.mTextures[ i ] );
-
-				}
-
-			}
-			// Read lights
-			if ( scene.mNumLights ) {
-
-				scene.mLights = [];
-
-				for ( var i = 0; i < scene.mNumLights; ++ i ) {
-
-					scene.mLights[ i ] = new aiLight();
-					ReadBinaryLight( stream, scene.mLights[ i ] );
-
-				}
-
-			}
-			// Read cameras
-			if ( scene.mNumCameras ) {
-
-				scene.mCameras = [];
-
-				for ( var i = 0; i < scene.mNumCameras; ++ i ) {
-
-					scene.mCameras[ i ] = new aiCamera();
-					ReadBinaryCamera( stream, scene.mCameras[ i ] );
-
-				}
-
-			}
-
-		}
-		var aiOrigin_CUR = 0;
-		var aiOrigin_BEG = 1;
-
-		function extendStream( stream ) {
-
-			stream.readOffset = 0;
-			stream.Seek = function ( off, ori ) {
-
-				if ( ori == aiOrigin_CUR ) {
-
-					stream.readOffset += off;
-
-				}
-				if ( ori == aiOrigin_BEG ) {
-
-					stream.readOffset = off;
-
-				}
-
-			};
-
-			stream.ReadBytes = function ( buff, size, n ) {
-
-				var bytes = size * n;
-				for ( var i = 0; i < bytes; i ++ )
-					buff[ i ] = Read_uint8_t( this );
-
-			};
-
-			stream.subArray32 = function ( start, end ) {
-
-				var buff = this.buffer;
-				var newbuff = buff.slice( start, end );
-				return new Float32Array( newbuff );
-
-			};
-
-			stream.subArrayUint16 = function ( start, end ) {
-
-				var buff = this.buffer;
-				var newbuff = buff.slice( start, end );
-				return new Uint16Array( newbuff );
-
-			};
-
-			stream.subArrayUint8 = function ( start, end ) {
-
-				var buff = this.buffer;
-				var newbuff = buff.slice( start, end );
-				return new Uint8Array( newbuff );
-
-			};
-
-			stream.subArrayUint32 = function ( start, end ) {
-
-				var buff = this.buffer;
-				var newbuff = buff.slice( start, end );
-				return new Uint32Array( newbuff );
-
-			};
-
-		}
-
-		var shortened, compressed;
-
-		function InternReadFile( pFiledata ) {
-
-			var pScene = new aiScene();
-			var stream = new DataView( pFiledata );
-			extendStream( stream );
-			stream.Seek( 44, aiOrigin_CUR ); // signature
-			/*unsigned int versionMajor =*/
-			var versionMajor = Read_unsigned_int( stream );
-			/*unsigned int versionMinor =*/
-			var versionMinor = Read_unsigned_int( stream );
-			/*unsigned int versionRevision =*/
-			var versionRevision = Read_unsigned_int( stream );
-			/*unsigned int compileFlags =*/
-			var compileFlags = Read_unsigned_int( stream );
-			shortened = Read_uint16_t( stream ) > 0;
-			compressed = Read_uint16_t( stream ) > 0;
-			if ( shortened )
-				throw "Shortened binaries are not supported!";
-			stream.Seek( 256, aiOrigin_CUR ); // original filename
-			stream.Seek( 128, aiOrigin_CUR ); // options
-			stream.Seek( 64, aiOrigin_CUR ); // padding
-			if ( compressed ) {
-
-				var uncompressedSize = Read_uint32_t( stream );
-				var compressedSize = stream.FileSize() - stream.Tell();
-				var compressedData = [];
-				stream.Read( compressedData, 1, compressedSize );
-				var uncompressedData = [];
-				uncompress( uncompressedData, uncompressedSize, compressedData, compressedSize );
-				var buff = new ArrayBuffer( uncompressedData );
-				ReadBinaryScene( buff, pScene );
-
-			} else {
-
-				ReadBinaryScene( stream, pScene );
-				return pScene.toTHREE();
-
-			}
-
-		}
-
-		return InternReadFile( buffer );
-
-	}
-
-};
+/**
+ * @author Virtulous / https://virtulo.us/
+ */
+
+THREE.AssimpLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.AssimpLoader.prototype = {
+
+	constructor: THREE.AssimpLoader,
+
+	crossOrigin: 'Anonymous',
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var path = THREE.LoaderUtils.extractUrlBase( url );
+
+		var loader = new THREE.FileLoader( this.manager );
+		loader.setResponseType( 'arraybuffer' );
+
+		loader.load( url, function ( buffer ) {
+
+			onLoad( scope.parse( buffer, path ) );
+
+		}, onProgress, onError );
+
+	},
+
+	setCrossOrigin: function ( value ) {
+
+		this.crossOrigin = value;
+
+	},
+
+	parse: function ( buffer, path ) {
+
+		var textureLoader = new THREE.TextureLoader( this.manager );
+		textureLoader.setPath( path ).setCrossOrigin( this.crossOrigin );
+
+		var Virtulous = {};
+
+		Virtulous.KeyFrame = function ( time, matrix ) {
+
+			this.time = time;
+			this.matrix = matrix.clone();
+			this.position = new THREE.Vector3();
+			this.quaternion = new THREE.Quaternion();
+			this.scale = new THREE.Vector3( 1, 1, 1 );
+			this.matrix.decompose( this.position, this.quaternion, this.scale );
+			this.clone = function () {
+
+				var n = new Virtulous.KeyFrame( this.time, this.matrix );
+				return n;
+
+			};
+			this.lerp = function ( nextKey, time ) {
+
+				time -= this.time;
+				var dist = ( nextKey.time - this.time );
+				var l = time / dist;
+				var l2 = 1 - l;
+				var keypos = this.position;
+				var keyrot = this.quaternion;
+				//      var keyscl =  key.parentspaceScl || key.scl;
+				var key2pos = nextKey.position;
+				var key2rot = nextKey.quaternion;
+				//  var key2scl =  key2.parentspaceScl || key2.scl;
+				Virtulous.KeyFrame.tempAniPos.x = keypos.x * l2 + key2pos.x * l;
+				Virtulous.KeyFrame.tempAniPos.y = keypos.y * l2 + key2pos.y * l;
+				Virtulous.KeyFrame.tempAniPos.z = keypos.z * l2 + key2pos.z * l;
+				//     tempAniScale.x = keyscl[0] * l2 + key2scl[0] * l;
+				//     tempAniScale.y = keyscl[1] * l2 + key2scl[1] * l;
+				//     tempAniScale.z = keyscl[2] * l2 + key2scl[2] * l;
+				Virtulous.KeyFrame.tempAniQuat.set( keyrot.x, keyrot.y, keyrot.z, keyrot.w );
+				Virtulous.KeyFrame.tempAniQuat.slerp( key2rot, l );
+				return Virtulous.KeyFrame.tempAniMatrix.compose( Virtulous.KeyFrame.tempAniPos, Virtulous.KeyFrame.tempAniQuat, Virtulous.KeyFrame.tempAniScale );
+
+			};
+
+		};
+
+		Virtulous.KeyFrame.tempAniPos = new THREE.Vector3();
+		Virtulous.KeyFrame.tempAniQuat = new THREE.Quaternion();
+		Virtulous.KeyFrame.tempAniScale = new THREE.Vector3( 1, 1, 1 );
+		Virtulous.KeyFrame.tempAniMatrix = new THREE.Matrix4();
+		Virtulous.KeyFrameTrack = function () {
+
+			this.keys = [];
+			this.target = null;
+			this.time = 0;
+			this.length = 0;
+			this._accelTable = {};
+			this.fps = 20;
+			this.addKey = function ( key ) {
+
+				this.keys.push( key );
+
+			};
+			this.init = function () {
+
+				this.sortKeys();
+
+				if ( this.keys.length > 0 )
+					this.length = this.keys[ this.keys.length - 1 ].time;
+				else
+					this.length = 0;
+
+				if ( ! this.fps ) return;
+
+				for ( var j = 0; j < this.length * this.fps; j ++ ) {
+
+					for ( var i = 0; i < this.keys.length; i ++ ) {
+
+						if ( this.keys[ i ].time == j ) {
+
+							this._accelTable[ j ] = i;
+							break;
+
+						} else if ( this.keys[ i ].time < j / this.fps && this.keys[ i + 1 ] && this.keys[ i + 1 ].time >= j / this.fps ) {
+
+							this._accelTable[ j ] = i;
+							break;
+
+						}
+
+					}
+
+				}
+
+			};
+
+			this.parseFromThree = function ( data ) {
+
+				var fps = data.fps;
+				this.target = data.node;
+				var track = data.hierarchy[ 0 ].keys;
+				for ( var i = 0; i < track.length; i ++ ) {
+
+					this.addKey( new Virtulous.KeyFrame( i / fps || track[ i ].time, track[ i ].targets[ 0 ].data ) );
+
+				}
+				this.init();
+
+			};
+
+			this.parseFromCollada = function ( data ) {
+
+				var track = data.keys;
+				var fps = this.fps;
+
+				for ( var i = 0; i < track.length; i ++ ) {
+
+					this.addKey( new Virtulous.KeyFrame( i / fps || track[ i ].time, track[ i ].matrix ) );
+
+				}
+
+				this.init();
+
+			};
+
+			this.sortKeys = function () {
+
+				this.keys.sort( this.keySortFunc );
+
+			};
+
+			this.keySortFunc = function ( a, b ) {
+
+				return a.time - b.time;
+
+			};
+
+			this.clone = function () {
+
+				var t = new Virtulous.KeyFrameTrack();
+				t.target = this.target;
+				t.time = this.time;
+				t.length = this.length;
+
+				for ( var i = 0; i < this.keys.length; i ++ ) {
+
+					t.addKey( this.keys[ i ].clone() );
+
+				}
+
+				t.init();
+				return t;
+
+			};
+
+			this.reTarget = function ( root, compareitor ) {
+
+				if ( ! compareitor ) compareitor = Virtulous.TrackTargetNodeNameCompare;
+				this.target = compareitor( root, this.target );
+
+			};
+
+			this.keySearchAccel = function ( time ) {
+
+				time *= this.fps;
+				time = Math.floor( time );
+				return this._accelTable[ time ] || 0;
+
+			};
+
+			this.setTime = function ( time ) {
+
+				time = Math.abs( time );
+				if ( this.length )
+					time = time % this.length + .05;
+				var key0 = null;
+				var key1 = null;
+
+				for ( var i = this.keySearchAccel( time ); i < this.keys.length; i ++ ) {
+
+					if ( this.keys[ i ].time == time ) {
+
+						key0 = this.keys[ i ];
+						key1 = this.keys[ i ];
+						break;
+
+					} else if ( this.keys[ i ].time < time && this.keys[ i + 1 ] && this.keys[ i + 1 ].time > time ) {
+
+						key0 = this.keys[ i ];
+						key1 = this.keys[ i + 1 ];
+						break;
+
+					} else if ( this.keys[ i ].time < time && i == this.keys.length - 1 ) {
+
+						key0 = this.keys[ i ];
+						key1 = this.keys[ 0 ].clone();
+						key1.time += this.length + .05;
+						break;
+
+					}
+
+				}
+
+				if ( key0 && key1 && key0 !== key1 ) {
+
+					this.target.matrixAutoUpdate = false;
+					this.target.matrix.copy( key0.lerp( key1, time ) );
+					this.target.matrixWorldNeedsUpdate = true;
+					return;
+
+				}
+
+				if ( key0 && key1 && key0 == key1 ) {
+
+					this.target.matrixAutoUpdate = false;
+					this.target.matrix.copy( key0.matrix );
+					this.target.matrixWorldNeedsUpdate = true;
+					return;
+
+				}
+
+			};
+
+		};
+
+		Virtulous.TrackTargetNodeNameCompare = function ( root, target ) {
+
+			function find( node, name ) {
+
+				if ( node.name == name )
+					return node;
+
+				for ( var i = 0; i < node.children.length; i ++ ) {
+
+					var r = find( node.children[ i ], name );
+					if ( r ) return r;
+
+				}
+
+				return null;
+
+			}
+
+			return find( root, target.name );
+
+		};
+
+		Virtulous.Animation = function () {
+
+			this.tracks = [];
+			this.length = 0;
+
+			this.addTrack = function ( track ) {
+
+				this.tracks.push( track );
+				this.length = Math.max( track.length, this.length );
+
+			};
+
+			this.setTime = function ( time ) {
+
+				this.time = time;
+
+				for ( var i = 0; i < this.tracks.length; i ++ )
+					this.tracks[ i ].setTime( time );
+
+			};
+
+			this.clone = function ( target, compareitor ) {
+
+				if ( ! compareitor ) compareitor = Virtulous.TrackTargetNodeNameCompare;
+				var n = new Virtulous.Animation();
+				n.target = target;
+				for ( var i = 0; i < this.tracks.length; i ++ ) {
+
+					var track = this.tracks[ i ].clone();
+					track.reTarget( target, compareitor );
+					n.addTrack( track );
+
+				}
+
+				return n;
+
+			};
+
+		};
+
+		var ASSBIN_CHUNK_AICAMERA = 0x1234;
+		var ASSBIN_CHUNK_AILIGHT = 0x1235;
+		var ASSBIN_CHUNK_AITEXTURE = 0x1236;
+		var ASSBIN_CHUNK_AIMESH = 0x1237;
+		var ASSBIN_CHUNK_AINODEANIM = 0x1238;
+		var ASSBIN_CHUNK_AISCENE = 0x1239;
+		var ASSBIN_CHUNK_AIBONE = 0x123a;
+		var ASSBIN_CHUNK_AIANIMATION = 0x123b;
+		var ASSBIN_CHUNK_AINODE = 0x123c;
+		var ASSBIN_CHUNK_AIMATERIAL = 0x123d;
+		var ASSBIN_CHUNK_AIMATERIALPROPERTY = 0x123e;
+		var ASSBIN_MESH_HAS_POSITIONS = 0x1;
+		var ASSBIN_MESH_HAS_NORMALS = 0x2;
+		var ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS = 0x4;
+		var ASSBIN_MESH_HAS_TEXCOORD_BASE = 0x100;
+		var ASSBIN_MESH_HAS_COLOR_BASE = 0x10000;
+		var AI_MAX_NUMBER_OF_COLOR_SETS = 1;
+		var AI_MAX_NUMBER_OF_TEXTURECOORDS = 4;
+		var aiLightSource_UNDEFINED = 0x0;
+		//! A directional light source has a well-defined direction
+		//! but is infinitely far away. That's quite a good
+		//! approximation for sun light.
+		var aiLightSource_DIRECTIONAL = 0x1;
+		//! A point light source has a well-defined position
+		//! in space but no direction - it emits light in all
+		//! directions. A normal bulb is a point light.
+		var aiLightSource_POINT = 0x2;
+		//! A spot light source emits light in a specific
+		//! angle. It has a position and a direction it is pointing to.
+		//! A good example for a spot light is a light spot in
+		//! sport arenas.
+		var aiLightSource_SPOT = 0x3;
+		//! The generic light level of the world, including the bounces
+		//! of all other lightsources.
+		//! Typically, there's at most one ambient light in a scene.
+		//! This light type doesn't have a valid position, direction, or
+		//! other properties, just a color.
+		var aiLightSource_AMBIENT = 0x4;
+		/** Flat shading. Shading is done on per-face base,
+		 *  diffuse only. Also known as 'faceted shading'.
+		 */
+		var aiShadingMode_Flat = 0x1;
+		/** Simple Gouraud shading.
+		 */
+		var aiShadingMode_Gouraud = 0x2;
+		/** Phong-Shading -
+		 */
+		var aiShadingMode_Phong = 0x3;
+		/** Phong-Blinn-Shading
+		 */
+		var aiShadingMode_Blinn = 0x4;
+		/** Toon-Shading per pixel
+		 *
+		 *  Also known as 'comic' shader.
+		 */
+		var aiShadingMode_Toon = 0x5;
+		/** OrenNayar-Shading per pixel
+		 *
+		 *  Extension to standard Lambertian shading, taking the
+		 *  roughness of the material into account
+		 */
+		var aiShadingMode_OrenNayar = 0x6;
+		/** Minnaert-Shading per pixel
+		 *
+		 *  Extension to standard Lambertian shading, taking the
+		 *  "darkness" of the material into account
+		 */
+		var aiShadingMode_Minnaert = 0x7;
+		/** CookTorrance-Shading per pixel
+		 *
+		 *  Special shader for metallic surfaces.
+		 */
+		var aiShadingMode_CookTorrance = 0x8;
+		/** No shading at all. Constant light influence of 1.0.
+		 */
+		var aiShadingMode_NoShading = 0x9;
+		/** Fresnel shading
+		 */
+		var aiShadingMode_Fresnel = 0xa;
+		var aiTextureType_NONE = 0x0;
+		/** The texture is combined with the result of the diffuse
+		 *  lighting equation.
+		 */
+		var aiTextureType_DIFFUSE = 0x1;
+		/** The texture is combined with the result of the specular
+		 *  lighting equation.
+		 */
+		var aiTextureType_SPECULAR = 0x2;
+		/** The texture is combined with the result of the ambient
+		 *  lighting equation.
+		 */
+		var aiTextureType_AMBIENT = 0x3;
+		/** The texture is added to the result of the lighting
+		 *  calculation. It isn't influenced by incoming light.
+		 */
+		var aiTextureType_EMISSIVE = 0x4;
+		/** The texture is a height map.
+		 *
+		 *  By convention, higher gray-scale values stand for
+		 *  higher elevations from the base height.
+		 */
+		var aiTextureType_HEIGHT = 0x5;
+		/** The texture is a (tangent space) normal-map.
+		 *
+		 *  Again, there are several conventions for tangent-space
+		 *  normal maps. Assimp does (intentionally) not
+		 *  distinguish here.
+		 */
+		var aiTextureType_NORMALS = 0x6;
+		/** The texture defines the glossiness of the material.
+		 *
+		 *  The glossiness is in fact the exponent of the specular
+		 *  (phong) lighting equation. Usually there is a conversion
+		 *  function defined to map the linear color values in the
+		 *  texture to a suitable exponent. Have fun.
+		 */
+		var aiTextureType_SHININESS = 0x7;
+		/** The texture defines per-pixel opacity.
+		 *
+		 *  Usually 'white' means opaque and 'black' means
+		 *  'transparency'. Or quite the opposite. Have fun.
+		 */
+		var aiTextureType_OPACITY = 0x8;
+		/** Displacement texture
+		 *
+		 *  The exact purpose and format is application-dependent.
+		 *  Higher color values stand for higher vertex displacements.
+		 */
+		var aiTextureType_DISPLACEMENT = 0x9;
+		/** Lightmap texture (aka Ambient Occlusion)
+		 *
+		 *  Both 'Lightmaps' and dedicated 'ambient occlusion maps' are
+		 *  covered by this material property. The texture contains a
+		 *  scaling value for the final color value of a pixel. Its
+		 *  intensity is not affected by incoming light.
+		 */
+		var aiTextureType_LIGHTMAP = 0xA;
+		/** Reflection texture
+		 *
+		 * Contains the color of a perfect mirror reflection.
+		 * Rarely used, almost never for real-time applications.
+		 */
+		var aiTextureType_REFLECTION = 0xB;
+		/** Unknown texture
+		 *
+		 *  A texture reference that does not match any of the definitions
+		 *  above is considered to be 'unknown'. It is still imported,
+		 *  but is excluded from any further postprocessing.
+		 */
+		var aiTextureType_UNKNOWN = 0xC;
+		var BONESPERVERT = 4;
+
+		function ASSBIN_MESH_HAS_TEXCOORD( n ) {
+
+			return ASSBIN_MESH_HAS_TEXCOORD_BASE << n;
+
+		}
+
+		function ASSBIN_MESH_HAS_COLOR( n ) {
+
+			return ASSBIN_MESH_HAS_COLOR_BASE << n;
+
+		}
+
+		function markBones( scene ) {
+
+			for ( var i in scene.mMeshes ) {
+
+				var mesh = scene.mMeshes[ i ];
+				for ( var k in mesh.mBones ) {
+
+					var boneNode = scene.findNode( mesh.mBones[ k ].mName );
+					if ( boneNode )
+						boneNode.isBone = true;
+
+				}
+
+			}
+
+		}
+		function cloneTreeToBones( root, scene ) {
+
+			var rootBone = new THREE.Bone();
+			rootBone.matrix.copy( root.matrix );
+			rootBone.matrixWorld.copy( root.matrixWorld );
+			rootBone.position.copy( root.position );
+			rootBone.quaternion.copy( root.quaternion );
+			rootBone.scale.copy( root.scale );
+			scene.nodeCount ++;
+			rootBone.name = "bone_" + root.name + scene.nodeCount.toString();
+
+			if ( ! scene.nodeToBoneMap[ root.name ] )
+				scene.nodeToBoneMap[ root.name ] = [];
+			scene.nodeToBoneMap[ root.name ].push( rootBone );
+			for ( var i in root.children ) {
+
+				var child = cloneTreeToBones( root.children[ i ], scene );
+				if ( child )
+					rootBone.add( child );
+
+			}
+
+			return rootBone;
+
+		}
+
+		function sortWeights( indexes, weights ) {
+
+			var pairs = [];
+
+			for ( var i = 0; i < indexes.length; i ++ ) {
+
+				pairs.push( {
+					i: indexes[ i ],
+					w: weights[ i ]
+				} );
+
+			}
+
+			pairs.sort( function ( a, b ) {
+
+				return b.w - a.w;
+
+			 } );
+
+			while ( pairs.length < 4 ) {
+
+				pairs.push( {
+					i: 0,
+					w: 0
+				} );
+
+			}
+
+			if ( pairs.length > 4 )
+				pairs.length = 4;
+			var sum = 0;
+
+			for ( var i = 0; i < 4; i ++ ) {
+
+				sum += pairs[ i ].w * pairs[ i ].w;
+
+			}
+
+			sum = Math.sqrt( sum );
+
+			for ( var i = 0; i < 4; i ++ ) {
+
+				pairs[ i ].w = pairs[ i ].w / sum;
+				indexes[ i ] = pairs[ i ].i;
+				weights[ i ] = pairs[ i ].w;
+
+			}
+
+		}
+
+		function findMatchingBone( root, name ) {
+
+			if ( root.name.indexOf( "bone_" + name ) == 0 )
+				return root;
+
+			for ( var i in root.children ) {
+
+				var ret = findMatchingBone( root.children[ i ], name );
+
+				if ( ret )
+					return ret;
+
+			}
+
+			return undefined;
+
+		}
+
+		function aiMesh() {
+
+			this.mPrimitiveTypes = 0;
+			this.mNumVertices = 0;
+			this.mNumFaces = 0;
+			this.mNumBones = 0;
+			this.mMaterialIndex = 0;
+			this.mVertices = [];
+			this.mNormals = [];
+			this.mTangents = [];
+			this.mBitangents = [];
+			this.mColors = [
+				[]
+			];
+			this.mTextureCoords = [
+				[]
+			];
+			this.mFaces = [];
+			this.mBones = [];
+			this.hookupSkeletons = function ( scene, threeScene ) {
+
+				if ( this.mBones.length == 0 ) return;
+
+				var allBones = [];
+				var offsetMatrix = [];
+				var skeletonRoot = scene.findNode( this.mBones[ 0 ].mName );
+
+				while ( skeletonRoot.mParent && skeletonRoot.mParent.isBone ) {
+
+					skeletonRoot = skeletonRoot.mParent;
+
+				}
+
+				var threeSkeletonRoot = skeletonRoot.toTHREE( scene );
+				var threeSkeletonRootBone = cloneTreeToBones( threeSkeletonRoot, scene );
+				this.threeNode.add( threeSkeletonRootBone );
+
+				for ( var i = 0; i < this.mBones.length; i ++ ) {
+
+					var bone = findMatchingBone( threeSkeletonRootBone, this.mBones[ i ].mName );
+
+					if ( bone ) {
+
+						var tbone = bone;
+						allBones.push( tbone );
+						//tbone.matrixAutoUpdate = false;
+						offsetMatrix.push( this.mBones[ i ].mOffsetMatrix.toTHREE() );
+
+					} else {
+
+						var skeletonRoot = scene.findNode( this.mBones[ i ].mName );
+						if ( ! skeletonRoot ) return;
+						var threeSkeletonRoot = skeletonRoot.toTHREE( scene );
+						var threeSkeletonRootParent = threeSkeletonRoot.parent;
+						var threeSkeletonRootBone = cloneTreeToBones( threeSkeletonRoot, scene );
+						this.threeNode.add( threeSkeletonRootBone );
+						var bone = findMatchingBone( threeSkeletonRootBone, this.mBones[ i ].mName );
+						var tbone = bone;
+						allBones.push( tbone );
+						//tbone.matrixAutoUpdate = false;
+						offsetMatrix.push( this.mBones[ i ].mOffsetMatrix.toTHREE() );
+
+					}
+
+				}
+				var skeleton = new THREE.Skeleton( allBones, offsetMatrix );
+
+				this.threeNode.bind( skeleton, new THREE.Matrix4() );
+				this.threeNode.material.skinning = true;
+
+			};
+
+			this.toTHREE = function ( scene ) {
+
+				if ( this.threeNode ) return this.threeNode;
+				var geometry = new THREE.BufferGeometry();
+				var mat;
+				if ( scene.mMaterials[ this.mMaterialIndex ] )
+					mat = scene.mMaterials[ this.mMaterialIndex ].toTHREE( scene );
+				else
+					mat = new THREE.MeshLambertMaterial();
+				geometry.setIndex( new THREE.BufferAttribute( new Uint32Array( this.mIndexArray ), 1 ) );
+				geometry.addAttribute( 'position', new THREE.BufferAttribute( this.mVertexBuffer, 3 ) );
+				if ( this.mNormalBuffer && this.mNormalBuffer.length > 0 )
+					geometry.addAttribute( 'normal', new THREE.BufferAttribute( this.mNormalBuffer, 3 ) );
+				if ( this.mColorBuffer && this.mColorBuffer.length > 0 )
+					geometry.addAttribute( 'color', new THREE.BufferAttribute( this.mColorBuffer, 4 ) );
+				if ( this.mTexCoordsBuffers[ 0 ] && this.mTexCoordsBuffers[ 0 ].length > 0 )
+					geometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( this.mTexCoordsBuffers[ 0 ] ), 2 ) );
+				if ( this.mTexCoordsBuffers[ 1 ] && this.mTexCoordsBuffers[ 1 ].length > 0 )
+					geometry.addAttribute( 'uv1', new THREE.BufferAttribute( new Float32Array( this.mTexCoordsBuffers[ 1 ] ), 2 ) );
+				if ( this.mTangentBuffer && this.mTangentBuffer.length > 0 )
+					geometry.addAttribute( 'tangents', new THREE.BufferAttribute( this.mTangentBuffer, 3 ) );
+				if ( this.mBitangentBuffer && this.mBitangentBuffer.length > 0 )
+					geometry.addAttribute( 'bitangents', new THREE.BufferAttribute( this.mBitangentBuffer, 3 ) );
+				if ( this.mBones.length > 0 ) {
+
+					var weights = [];
+					var bones = [];
+
+					for ( var i = 0; i < this.mBones.length; i ++ ) {
+
+						for ( var j = 0; j < this.mBones[ i ].mWeights.length; j ++ ) {
+
+							var weight = this.mBones[ i ].mWeights[ j ];
+							if ( weight ) {
+
+								if ( ! weights[ weight.mVertexId ] ) weights[ weight.mVertexId ] = [];
+								if ( ! bones[ weight.mVertexId ] ) bones[ weight.mVertexId ] = [];
+								weights[ weight.mVertexId ].push( weight.mWeight );
+								bones[ weight.mVertexId ].push( parseInt( i ) );
+
+							}
+
+						}
+
+					}
+
+					for ( var i in bones ) {
+
+						sortWeights( bones[ i ], weights[ i ] );
+
+					}
+
+					var _weights = [];
+					var _bones = [];
+
+					for ( var i = 0; i < weights.length; i ++ ) {
+
+						for ( var j = 0; j < 4; j ++ ) {
+
+							if ( weights[ i ] && bones[ i ] ) {
+
+								_weights.push( weights[ i ][ j ] );
+								_bones.push( bones[ i ][ j ] );
+
+							} else {
+
+								_weights.push( 0 );
+								_bones.push( 0 );
+
+							}
+
+						}
+
+					}
+
+					geometry.addAttribute( 'skinWeight', new THREE.BufferAttribute( new Float32Array( _weights ), BONESPERVERT ) );
+					geometry.addAttribute( 'skinIndex', new THREE.BufferAttribute( new Float32Array( _bones ), BONESPERVERT ) );
+
+				}
+
+				var mesh;
+
+				if ( this.mBones.length == 0 )
+					mesh = new THREE.Mesh( geometry, mat );
+
+				if ( this.mBones.length > 0 )
+					mesh = new THREE.SkinnedMesh( geometry, mat );
+
+				this.threeNode = mesh;
+				//mesh.matrixAutoUpdate = false;
+				return mesh;
+
+			};
+
+		}
+
+		function aiFace() {
+
+			this.mNumIndices = 0;
+			this.mIndices = [];
+
+		}
+
+		function aiVector3D() {
+
+			this.x = 0;
+			this.y = 0;
+			this.z = 0;
+
+			this.toTHREE = function () {
+
+				return new THREE.Vector3( this.x, this.y, this.z );
+
+			};
+
+		}
+
+		function aiVector2D() {
+
+			this.x = 0;
+			this.y = 0;
+			this.toTHREE = function () {
+
+				return new THREE.Vector2( this.x, this.y );
+
+			};
+
+		}
+
+		function aiVector4D() {
+
+			this.w = 0;
+			this.x = 0;
+			this.y = 0;
+			this.z = 0;
+			this.toTHREE = function () {
+
+				return new THREE.Vector4( this.w, this.x, this.y, this.z );
+
+			};
+
+		}
+
+		function aiColor4D() {
+
+			this.r = 0;
+			this.g = 0;
+			this.b = 0;
+			this.a = 0;
+			this.toTHREE = function () {
+
+				return new THREE.Color( this.r, this.g, this.b, this.a );
+
+			};
+
+		}
+
+		function aiColor3D() {
+
+			this.r = 0;
+			this.g = 0;
+			this.b = 0;
+			this.a = 0;
+			this.toTHREE = function () {
+
+				return new THREE.Color( this.r, this.g, this.b, 1 );
+
+			};
+
+		}
+
+		function aiQuaternion() {
+
+			this.x = 0;
+			this.y = 0;
+			this.z = 0;
+			this.w = 0;
+			this.toTHREE = function () {
+
+				return new THREE.Quaternion( this.x, this.y, this.z, this.w );
+
+			};
+
+		}
+
+		function aiVertexWeight() {
+
+			this.mVertexId = 0;
+			this.mWeight = 0;
+
+		}
+
+		function aiString() {
+
+			this.data = [];
+			this.toString = function () {
+
+				var str = '';
+				this.data.forEach( function ( i ) {
+
+					str += ( String.fromCharCode( i ) );
+
+				} );
+				return str.replace( /[^\x20-\x7E]+/g, '' );
+
+			};
+
+		}
+
+		function aiVectorKey() {
+
+			this.mTime = 0;
+			this.mValue = null;
+
+		}
+
+		function aiQuatKey() {
+
+			this.mTime = 0;
+			this.mValue = null;
+
+		}
+
+		function aiNode() {
+
+			this.mName = '';
+			this.mTransformation = [];
+			this.mNumChildren = 0;
+			this.mNumMeshes = 0;
+			this.mMeshes = [];
+			this.mChildren = [];
+			this.toTHREE = function ( scene ) {
+
+				if ( this.threeNode ) return this.threeNode;
+				var o = new THREE.Object3D();
+				o.name = this.mName;
+				o.matrix = this.mTransformation.toTHREE();
+
+				for ( var i = 0; i < this.mChildren.length; i ++ ) {
+
+					o.add( this.mChildren[ i ].toTHREE( scene ) );
+
+				}
+
+				for ( var i = 0; i < this.mMeshes.length; i ++ ) {
+
+					o.add( scene.mMeshes[ this.mMeshes[ i ] ].toTHREE( scene ) );
+
+				}
+
+				this.threeNode = o;
+				//o.matrixAutoUpdate = false;
+				o.matrix.decompose( o.position, o.quaternion, o.scale );
+				return o;
+
+			};
+
+		}
+
+		function aiBone() {
+
+			this.mName = '';
+			this.mNumWeights = 0;
+			this.mOffsetMatrix = 0;
+
+		}
+
+		function aiMaterialProperty() {
+
+			this.mKey = "";
+			this.mSemantic = 0;
+			this.mIndex = 0;
+			this.mData = [];
+			this.mDataLength = 0;
+			this.mType = 0;
+			this.dataAsColor = function () {
+
+				var array = ( new Uint8Array( this.mData ) ).buffer;
+				var reader = new DataView( array );
+				var r = reader.getFloat32( 0, true );
+				var g = reader.getFloat32( 4, true );
+				var b = reader.getFloat32( 8, true );
+				//var a = reader.getFloat32(12, true);
+				return new THREE.Color( r, g, b );
+
+			};
+
+			this.dataAsFloat = function () {
+
+				var array = ( new Uint8Array( this.mData ) ).buffer;
+				var reader = new DataView( array );
+				var r = reader.getFloat32( 0, true );
+				return r;
+
+			};
+
+			this.dataAsBool = function () {
+
+				var array = ( new Uint8Array( this.mData ) ).buffer;
+				var reader = new DataView( array );
+				var r = reader.getFloat32( 0, true );
+				return !! r;
+
+			};
+
+			this.dataAsString = function () {
+
+				var s = new aiString();
+				s.data = this.mData;
+				return s.toString();
+
+			};
+
+			this.dataAsMap = function () {
+
+				var s = new aiString();
+				s.data = this.mData;
+				var path = s.toString();
+				path = path.replace( /\\/g, '/' );
+
+				if ( path.indexOf( '/' ) != - 1 ) {
+
+					path = path.substr( path.lastIndexOf( '/' ) + 1 );
+
+				}
+
+				return textureLoader.load( path );
+
+			};
+
+		}
+		var namePropMapping = {
+
+			"?mat.name": "name",
+			"$mat.shadingm": "shading",
+			"$mat.twosided": "twoSided",
+			"$mat.wireframe": "wireframe",
+			"$clr.ambient": "ambient",
+			"$clr.diffuse": "color",
+			"$clr.specular": "specular",
+			"$clr.emissive": "emissive",
+			"$clr.transparent": "transparent",
+			"$clr.reflective": "reflect",
+			"$mat.shininess": "shininess",
+			"$mat.reflectivity": "reflectivity",
+			"$mat.refracti": "refraction",
+			"$tex.file": "map"
+
+		};
+
+		var nameTypeMapping = {
+
+			"?mat.name": "string",
+			"$mat.shadingm": "bool",
+			"$mat.twosided": "bool",
+			"$mat.wireframe": "bool",
+			"$clr.ambient": "color",
+			"$clr.diffuse": "color",
+			"$clr.specular": "color",
+			"$clr.emissive": "color",
+			"$clr.transparent": "color",
+			"$clr.reflective": "color",
+			"$mat.shininess": "float",
+			"$mat.reflectivity": "float",
+			"$mat.refracti": "float",
+			"$tex.file": "map"
+
+		};
+
+		function aiMaterial() {
+
+			this.mNumAllocated = 0;
+			this.mNumProperties = 0;
+			this.mProperties = [];
+			this.toTHREE = function ( scene ) {
+
+				var name = this.mProperties[ 0 ].dataAsString();
+				var mat = new THREE.MeshPhongMaterial();
+
+				for ( var i = 0; i < this.mProperties.length; i ++ ) {
+
+					if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'float' )
+						mat[ namePropMapping[ this.mProperties[ i ].mKey ] ] = this.mProperties[ i ].dataAsFloat();
+					if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'color' )
+						mat[ namePropMapping[ this.mProperties[ i ].mKey ] ] = this.mProperties[ i ].dataAsColor();
+					if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'bool' )
+						mat[ namePropMapping[ this.mProperties[ i ].mKey ] ] = this.mProperties[ i ].dataAsBool();
+					if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'string' )
+						mat[ namePropMapping[ this.mProperties[ i ].mKey ] ] = this.mProperties[ i ].dataAsString();
+					if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'map' ) {
+
+						var prop = this.mProperties[ i ];
+						if ( prop.mSemantic == aiTextureType_DIFFUSE )
+							mat.map = this.mProperties[ i ].dataAsMap();
+						if ( prop.mSemantic == aiTextureType_NORMALS )
+							mat.normalMap = this.mProperties[ i ].dataAsMap();
+						if ( prop.mSemantic == aiTextureType_LIGHTMAP )
+							mat.lightMap = this.mProperties[ i ].dataAsMap();
+						if ( prop.mSemantic == aiTextureType_OPACITY )
+							mat.alphaMap = this.mProperties[ i ].dataAsMap();
+
+					}
+
+				}
+
+				mat.ambient.r = .53;
+				mat.ambient.g = .53;
+				mat.ambient.b = .53;
+				mat.color.r = 1;
+				mat.color.g = 1;
+				mat.color.b = 1;
+				return mat;
+
+			};
+
+		}
+
+
+		function veclerp( v1, v2, l ) {
+
+			var v = new THREE.Vector3();
+			var lm1 = 1 - l;
+			v.x = v1.x * l + v2.x * lm1;
+			v.y = v1.y * l + v2.y * lm1;
+			v.z = v1.z * l + v2.z * lm1;
+			return v;
+
+		}
+
+		function quatlerp( q1, q2, l ) {
+
+			return q1.clone().slerp( q2, 1 - l );
+
+		}
+
+		function sampleTrack( keys, time, lne, lerp ) {
+
+			if ( keys.length == 1 ) return keys[ 0 ].mValue.toTHREE();
+
+			var dist = Infinity;
+			var key = null;
+			var nextKey = null;
+
+			for ( var i = 0; i < keys.length; i ++ ) {
+
+				var timeDist = Math.abs( keys[ i ].mTime - time );
+
+				if ( timeDist < dist && keys[ i ].mTime <= time ) {
+
+					dist = timeDist;
+					key = keys[ i ];
+					nextKey = keys[ i + 1 ];
+
+				}
+
+			}
+
+			if ( ! key ) {
+				
+				return null;
+
+			} else if ( nextKey ) {
+
+				var dT = nextKey.mTime - key.mTime;
+				var T = key.mTime - time;
+				var l = T / dT;
+
+				return lerp( key.mValue.toTHREE(), nextKey.mValue.toTHREE(), l );
+
+			} else {
+
+				nextKey = keys[ 0 ].clone();
+				nextKey.mTime += lne;
+
+				var dT = nextKey.mTime - key.mTime;
+				var T = key.mTime - time;
+				var l = T / dT;
+
+				return lerp( key.mValue.toTHREE(), nextKey.mValue.toTHREE(), l );
+				
+			}
+
+		}
+
+		function aiNodeAnim() {
+
+			this.mNodeName = "";
+			this.mNumPositionKeys = 0;
+			this.mNumRotationKeys = 0;
+			this.mNumScalingKeys = 0;
+			this.mPositionKeys = [];
+			this.mRotationKeys = [];
+			this.mScalingKeys = [];
+			this.mPreState = "";
+			this.mPostState = "";
+			this.init = function ( tps ) {
+
+				if ( ! tps ) tps = 1;
+
+				function t( t ) {
+
+					t.mTime /= tps;
+
+				}
+
+				this.mPositionKeys.forEach( t );
+				this.mRotationKeys.forEach( t );
+				this.mScalingKeys.forEach( t );
+
+			};
+
+			this.sortKeys = function () {
+
+				function comp( a, b ) {
+
+					return a.mTime - b.mTime;
+
+				}
+
+				this.mPositionKeys.sort( comp );
+				this.mRotationKeys.sort( comp );
+				this.mScalingKeys.sort( comp );
+
+			};
+
+			this.getLength = function () {
+
+				return Math.max(
+					Math.max.apply( null, this.mPositionKeys.map( function ( a ) {
+
+						return a.mTime;
+
+					} ) ),
+					Math.max.apply( null, this.mRotationKeys.map( function ( a ) {
+
+						return a.mTime;
+
+					} ) ),
+					Math.max.apply( null, this.mScalingKeys.map( function ( a ) {
+
+						return a.mTime;
+
+				 } ) )
+				);
+
+			};
+
+			this.toTHREE = function ( o, tps ) {
+
+				this.sortKeys();
+				var length = this.getLength();
+				var track = new Virtulous.KeyFrameTrack();
+
+				for ( var i = 0; i < length; i += .05 ) {
+
+					var matrix = new THREE.Matrix4();
+					var time = i;
+					var pos = sampleTrack( this.mPositionKeys, time, length, veclerp );
+					var scale = sampleTrack( this.mScalingKeys, time, length, veclerp );
+					var rotation = sampleTrack( this.mRotationKeys, time, length, quatlerp );
+					matrix.compose( pos, rotation, scale );
+
+					var key = new Virtulous.KeyFrame( time, matrix );
+					track.addKey( key );
+
+				}
+
+				track.target = o.findNode( this.mNodeName ).toTHREE();
+
+				var tracks = [ track ];
+
+				if ( o.nodeToBoneMap[ this.mNodeName ] ) {
+
+					for ( var i = 0; i < o.nodeToBoneMap[ this.mNodeName ].length; i ++ ) {
+
+						var t2 = track.clone();
+						t2.target = o.nodeToBoneMap[ this.mNodeName ][ i ];
+						tracks.push( t2 );
+
+					}
+
+				}
+
+				return tracks;
+
+			};
+
+		}
+
+		function aiAnimation() {
+
+			this.mName = "";
+			this.mDuration = 0;
+			this.mTicksPerSecond = 0;
+			this.mNumChannels = 0;
+			this.mChannels = [];
+			this.toTHREE = function ( root ) {
+
+				var animationHandle = new Virtulous.Animation();
+
+				for ( var i in this.mChannels ) {
+
+					this.mChannels[ i ].init( this.mTicksPerSecond );
+
+					var tracks = this.mChannels[ i ].toTHREE( root );
+
+					for ( var j in tracks ) {
+
+						tracks[ j ].init();
+						animationHandle.addTrack( tracks[ j ] );
+
+					}
+
+				}
+
+				animationHandle.length = Math.max.apply( null, animationHandle.tracks.map( function ( e ) {
+
+					return e.length;
+
+				} ) );
+				return animationHandle;
+
+			};
+
+		}
+
+		function aiTexture() {
+
+			this.mWidth = 0;
+			this.mHeight = 0;
+			this.texAchFormatHint = [];
+			this.pcData = [];
+
+		}
+
+		function aiLight() {
+
+			this.mName = '';
+			this.mType = 0;
+			this.mAttenuationConstant = 0;
+			this.mAttenuationLinear = 0;
+			this.mAttenuationQuadratic = 0;
+			this.mAngleInnerCone = 0;
+			this.mAngleOuterCone = 0;
+			this.mColorDiffuse = null;
+			this.mColorSpecular = null;
+			this.mColorAmbient = null;
+
+		}
+
+		function aiCamera() {
+
+			this.mName = '';
+			this.mPosition = null;
+			this.mLookAt = null;
+			this.mUp = null;
+			this.mHorizontalFOV = 0;
+			this.mClipPlaneNear = 0;
+			this.mClipPlaneFar = 0;
+			this.mAspect = 0;
+
+		}
+
+		function aiScene() {
+
+			this.mFlags = 0;
+			this.mNumMeshes = 0;
+			this.mNumMaterials = 0;
+			this.mNumAnimations = 0;
+			this.mNumTextures = 0;
+			this.mNumLights = 0;
+			this.mNumCameras = 0;
+			this.mRootNode = null;
+			this.mMeshes = [];
+			this.mMaterials = [];
+			this.mAnimations = [];
+			this.mLights = [];
+			this.mCameras = [];
+			this.nodeToBoneMap = {};
+			this.findNode = function ( name, root ) {
+
+				if ( ! root ) {
+
+					root = this.mRootNode;
+
+				}
+
+				if ( root.mName == name ) {
+
+					return root;
+
+				}
+
+				for ( var i = 0; i < root.mChildren.length; i ++ ) {
+
+					var ret = this.findNode( name, root.mChildren[ i ] );
+					if ( ret ) return ret;
+
+				}
+
+				return null;
+
+			};
+
+			this.toTHREE = function () {
+
+				this.nodeCount = 0;
+
+				markBones( this );
+
+				var o = this.mRootNode.toTHREE( this );
+
+				for ( var i in this.mMeshes )
+					this.mMeshes[ i ].hookupSkeletons( this, o );
+
+				if ( this.mAnimations.length > 0 ) {
+
+					var a = this.mAnimations[ 0 ].toTHREE( this );
+
+				}
+
+				return { object: o, animation: a };
+
+			};
+
+		}
+
+		function aiMatrix4() {
+
+			this.elements = [
+				[],
+				[],
+				[],
+				[]
+			];
+			this.toTHREE = function () {
+
+				var m = new THREE.Matrix4();
+
+				for ( var i = 0; i < 4; ++ i ) {
+
+					for ( var i2 = 0; i2 < 4; ++ i2 ) {
+
+						m.elements[ i * 4 + i2 ] = this.elements[ i2 ][ i ];
+
+					}
+
+				}
+
+				return m;
+
+			};
+
+		}
+
+		var littleEndian = true;
+
+		function readFloat( dataview ) {
+
+			var val = dataview.getFloat32( dataview.readOffset, littleEndian );
+			dataview.readOffset += 4;
+			return val;
+
+		}
+
+		function Read_double( dataview ) {
+
+			var val = dataview.getFloat64( dataview.readOffset, littleEndian );
+			dataview.readOffset += 8;
+			return val;
+
+		}
+
+		function Read_uint8_t( dataview ) {
+
+			var val = dataview.getUint8( dataview.readOffset );
+			dataview.readOffset += 1;
+			return val;
+
+		}
+
+		function Read_uint16_t( dataview ) {
+
+			var val = dataview.getUint16( dataview.readOffset, littleEndian );
+			dataview.readOffset += 2;
+			return val;
+
+		}
+
+		function Read_unsigned_int( dataview ) {
+
+			var val = dataview.getUint32( dataview.readOffset, littleEndian );
+			dataview.readOffset += 4;
+			return val;
+
+		}
+
+		function Read_uint32_t( dataview ) {
+
+			var val = dataview.getUint32( dataview.readOffset, littleEndian );
+			dataview.readOffset += 4;
+			return val;
+
+		}
+
+		function Read_aiVector3D( stream ) {
+
+			var v = new aiVector3D();
+			v.x = readFloat( stream );
+			v.y = readFloat( stream );
+			v.z = readFloat( stream );
+			return v;
+
+		}
+
+		function Read_aiVector2D( stream ) {
+
+			var v = new aiVector2D();
+			v.x = readFloat( stream );
+			v.y = readFloat( stream );
+			return v;
+
+		}
+
+		function Read_aiVector4D( stream ) {
+
+			var v = new aiVector4D();
+			v.w = readFloat( stream );
+			v.x = readFloat( stream );
+			v.y = readFloat( stream );
+			v.z = readFloat( stream );
+			return v;
+
+		}
+
+		function Read_aiColor3D( stream ) {
+
+			var c = new aiColor3D();
+			c.r = readFloat( stream );
+			c.g = readFloat( stream );
+			c.b = readFloat( stream );
+			return c;
+
+		}
+
+		function Read_aiColor4D( stream ) {
+
+			var c = new aiColor4D();
+			c.r = readFloat( stream );
+			c.g = readFloat( stream );
+			c.b = readFloat( stream );
+			c.a = readFloat( stream );
+			return c;
+
+		}
+
+		function Read_aiQuaternion( stream ) {
+
+			var v = new aiQuaternion();
+			v.w = readFloat( stream );
+			v.x = readFloat( stream );
+			v.y = readFloat( stream );
+			v.z = readFloat( stream );
+			return v;
+
+		}
+
+		function Read_aiString( stream ) {
+
+			var s = new aiString();
+			var stringlengthbytes = Read_unsigned_int( stream );
+			stream.ReadBytes( s.data, 1, stringlengthbytes );
+			return s.toString();
+
+		}
+
+		function Read_aiVertexWeight( stream ) {
+
+			var w = new aiVertexWeight();
+			w.mVertexId = Read_unsigned_int( stream );
+			w.mWeight = readFloat( stream );
+			return w;
+
+		}
+
+		function Read_aiMatrix4x4( stream ) {
+
+			var m = new aiMatrix4();
+
+			for ( var i = 0; i < 4; ++ i ) {
+
+				for ( var i2 = 0; i2 < 4; ++ i2 ) {
+
+					m.elements[ i ][ i2 ] = readFloat( stream );
+
+				}
+
+			}
+
+			return m;
+
+		}
+
+		function Read_aiVectorKey( stream ) {
+
+			var v = new aiVectorKey();
+			v.mTime = Read_double( stream );
+			v.mValue = Read_aiVector3D( stream );
+			return v;
+
+		}
+
+		function Read_aiQuatKey( stream ) {
+
+			var v = new aiQuatKey();
+			v.mTime = Read_double( stream );
+			v.mValue = Read_aiQuaternion( stream );
+			return v;
+
+		}
+
+		function ReadArray( stream, data, size ) {
+
+			for ( var i = 0; i < size; i ++ ) data[ i ] = Read( stream );
+
+		}
+
+		function ReadArray_aiVector2D( stream, data, size ) {
+
+			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVector2D( stream );
+
+		}
+
+		function ReadArray_aiVector3D( stream, data, size ) {
+
+			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVector3D( stream );
+
+		}
+
+		function ReadArray_aiVector4D( stream, data, size ) {
+
+			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVector4D( stream );
+
+		}
+
+		function ReadArray_aiVertexWeight( stream, data, size ) {
+
+			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVertexWeight( stream );
+
+		}
+
+		function ReadArray_aiColor4D( stream, data, size ) {
+
+			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiColor4D( stream );
+
+		}
+
+		function ReadArray_aiVectorKey( stream, data, size ) {
+
+			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVectorKey( stream );
+
+		}
+
+		function ReadArray_aiQuatKey( stream, data, size ) {
+
+			for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiQuatKey( stream );
+
+		}
+
+		function ReadBounds( stream, T /*p*/, n ) {
+
+			// not sure what to do here, the data isn't really useful.
+			return stream.Seek( sizeof( T ) * n, aiOrigin_CUR );
+
+		}
+
+		function ai_assert( bool ) {
+
+			if ( ! bool )
+				throw ( "asset failed" );
+
+		}
+
+		function ReadBinaryNode( stream, parent, depth ) {
+
+			var chunkID = Read_uint32_t( stream );
+			ai_assert( chunkID == ASSBIN_CHUNK_AINODE );
+			/*uint32_t size =*/
+			Read_uint32_t( stream );
+			var node = new aiNode();
+			node.mParent = parent;
+			node.mDepth = depth;
+			node.mName = Read_aiString( stream );
+			node.mTransformation = Read_aiMatrix4x4( stream );
+			node.mNumChildren = Read_unsigned_int( stream );
+			node.mNumMeshes = Read_unsigned_int( stream );
+
+			if ( node.mNumMeshes ) {
+
+				node.mMeshes = [];
+
+				for ( var i = 0; i < node.mNumMeshes; ++ i ) {
+
+					node.mMeshes[ i ] = Read_unsigned_int( stream );
+
+				}
+
+			}
+
+			if ( node.mNumChildren ) {
+
+				node.mChildren = [];
+
+				for ( var i = 0; i < node.mNumChildren; ++ i ) {
+
+					var node2 = ReadBinaryNode( stream, node, depth ++ );
+					node.mChildren[ i ] = node2;
+
+				}
+
+			}
+
+			return node;
+
+		}
+
+		// -----------------------------------------------------------------------------------
+
+		function ReadBinaryBone( stream, b ) {
+
+			var chunkID = Read_uint32_t( stream );
+			ai_assert( chunkID == ASSBIN_CHUNK_AIBONE );
+			/*uint32_t size =*/
+			Read_uint32_t( stream );
+			b.mName = Read_aiString( stream );
+			b.mNumWeights = Read_unsigned_int( stream );
+			b.mOffsetMatrix = Read_aiMatrix4x4( stream );
+			// for the moment we write dumb min/max values for the bones, too.
+			// maybe I'll add a better, hash-like solution later
+			if ( shortened ) {
+
+				ReadBounds( stream, b.mWeights, b.mNumWeights );
+
+			} else {
+
+				// else write as usual
+
+				b.mWeights = [];
+				ReadArray_aiVertexWeight( stream, b.mWeights, b.mNumWeights );
+
+			}
+
+			return b;
+
+		}
+
+		function ReadBinaryMesh( stream, mesh ) {
+
+			var chunkID = Read_uint32_t( stream );
+			ai_assert( chunkID == ASSBIN_CHUNK_AIMESH );
+			/*uint32_t size =*/
+			Read_uint32_t( stream );
+			mesh.mPrimitiveTypes = Read_unsigned_int( stream );
+			mesh.mNumVertices = Read_unsigned_int( stream );
+			mesh.mNumFaces = Read_unsigned_int( stream );
+			mesh.mNumBones = Read_unsigned_int( stream );
+			mesh.mMaterialIndex = Read_unsigned_int( stream );
+			mesh.mNumUVComponents = [];
+			// first of all, write bits for all existent vertex components
+			var c = Read_unsigned_int( stream );
+
+			if ( c & ASSBIN_MESH_HAS_POSITIONS ) {
+
+				if ( shortened ) {
+
+					ReadBounds( stream, mesh.mVertices, mesh.mNumVertices );
+
+				} else {
+
+					// else write as usual
+
+					mesh.mVertices = [];
+					mesh.mVertexBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 3 * 4 );
+					stream.Seek( mesh.mNumVertices * 3 * 4, aiOrigin_CUR );
+
+				}
+
+			}
+
+			if ( c & ASSBIN_MESH_HAS_NORMALS ) {
+
+				if ( shortened ) {
+
+					ReadBounds( stream, mesh.mNormals, mesh.mNumVertices );
+
+				} else {
+
+					// else write as usual
+
+					mesh.mNormals = [];
+					mesh.mNormalBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 3 * 4 );
+					stream.Seek( mesh.mNumVertices * 3 * 4, aiOrigin_CUR );
+
+				}
+
+			}
+
+			if ( c & ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS ) {
+
+				if ( shortened ) {
+
+					ReadBounds( stream, mesh.mTangents, mesh.mNumVertices );
+					ReadBounds( stream, mesh.mBitangents, mesh.mNumVertices );
+
+				} else {
+
+					// else write as usual
+
+					mesh.mTangents = [];
+					mesh.mTangentBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 3 * 4 );
+					stream.Seek( mesh.mNumVertices * 3 * 4, aiOrigin_CUR );
+					mesh.mBitangents = [];
+					mesh.mBitangentBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 3 * 4 );
+					stream.Seek( mesh.mNumVertices * 3 * 4, aiOrigin_CUR );
+
+				}
+
+			}
+
+			for ( var n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++ n ) {
+
+				if ( ! ( c & ASSBIN_MESH_HAS_COLOR( n ) ) ) break;
+
+				if ( shortened ) {
+
+					ReadBounds( stream, mesh.mColors[ n ], mesh.mNumVertices );
+
+				} else {
+
+					// else write as usual
+
+					mesh.mColors[ n ] = [];
+					mesh.mColorBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 4 * 4 );
+					stream.Seek( mesh.mNumVertices * 4 * 4, aiOrigin_CUR );
+
+				}
+
+			}
+
+			mesh.mTexCoordsBuffers = [];
+
+			for ( var n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++ n ) {
+
+				if ( ! ( c & ASSBIN_MESH_HAS_TEXCOORD( n ) ) ) break;
+
+				// write number of UV components
+				mesh.mNumUVComponents[ n ] = Read_unsigned_int( stream );
+
+				if ( shortened ) {
+
+					ReadBounds( stream, mesh.mTextureCoords[ n ], mesh.mNumVertices );
+
+				} else {
+
+				// else write as usual
+
+					mesh.mTextureCoords[ n ] = [];
+					//note that assbin always writes 3d texcoords
+					mesh.mTexCoordsBuffers[ n ] = [];
+
+					for ( var uv = 0; uv < mesh.mNumVertices; uv ++ ) {
+
+						mesh.mTexCoordsBuffers[ n ].push( readFloat( stream ) );
+						mesh.mTexCoordsBuffers[ n ].push( readFloat( stream ) );
+						readFloat( stream );
+
+					}
+
+				}
+
+			}
+			// write faces. There are no floating-point calculations involved
+			// in these, so we can write a simple hash over the face data
+			// to the dump file. We generate a single 32 Bit hash for 512 faces
+			// using Assimp's standard hashing function.
+			if ( shortened ) {
+
+				Read_unsigned_int( stream );
+
+			} else {
+
+				// else write as usual
+
+				// if there are less than 2^16 vertices, we can simply use 16 bit integers ...
+				mesh.mFaces = [];
+
+				var indexCounter = 0;
+				mesh.mIndexArray = [];
+
+				for ( var i = 0; i < mesh.mNumFaces; ++ i ) {
+
+					var f = mesh.mFaces[ i ] = new aiFace();
+					// BOOST_STATIC_ASSERT(AI_MAX_FACE_INDICES <= 0xffff);
+					f.mNumIndices = Read_uint16_t( stream );
+					f.mIndices = [];
+
+					for ( var a = 0; a < f.mNumIndices; ++ a ) {
+
+						if ( mesh.mNumVertices < ( 1 << 16 ) ) {
+
+							f.mIndices[ a ] = Read_uint16_t( stream );
+
+						} else {
+
+							f.mIndices[ a ] = Read_unsigned_int( stream );
+
+						}
+
+
+
+					}
+
+					if ( f.mNumIndices === 3 ) {
+
+						mesh.mIndexArray.push( f.mIndices[ 0 ] );
+						mesh.mIndexArray.push( f.mIndices[ 1 ] );
+						mesh.mIndexArray.push( f.mIndices[ 2 ] );
+
+					} else if ( f.mNumIndices === 4 ) {
+
+						mesh.mIndexArray.push( f.mIndices[ 0 ] );
+						mesh.mIndexArray.push( f.mIndices[ 1 ] );
+						mesh.mIndexArray.push( f.mIndices[ 2 ] );
+						mesh.mIndexArray.push( f.mIndices[ 2 ] );
+						mesh.mIndexArray.push( f.mIndices[ 3 ] );
+						mesh.mIndexArray.push( f.mIndices[ 0 ] );
+
+					} else {
+
+						throw ( new Error( "Sorry, can't currently triangulate polys. Use the triangulate preprocessor in Assimp." ) );
+
+					}
+
+
+
+				}
+
+			}
+			// write bones
+			if ( mesh.mNumBones ) {
+
+				mesh.mBones = [];
+
+				for ( var a = 0; a < mesh.mNumBones; ++ a ) {
+
+					mesh.mBones[ a ] = new aiBone();
+					ReadBinaryBone( stream, mesh.mBones[ a ] );
+
+				}
+
+			}
+
+		}
+
+		function ReadBinaryMaterialProperty( stream, prop ) {
+
+			var chunkID = Read_uint32_t( stream );
+			ai_assert( chunkID == ASSBIN_CHUNK_AIMATERIALPROPERTY );
+			/*uint32_t size =*/
+			Read_uint32_t( stream );
+			prop.mKey = Read_aiString( stream );
+			prop.mSemantic = Read_unsigned_int( stream );
+			prop.mIndex = Read_unsigned_int( stream );
+			prop.mDataLength = Read_unsigned_int( stream );
+			prop.mType = Read_unsigned_int( stream );
+			prop.mData = [];
+			stream.ReadBytes( prop.mData, 1, prop.mDataLength );
+
+		}
+
+		// -----------------------------------------------------------------------------------
+
+		function ReadBinaryMaterial( stream, mat ) {
+
+			var chunkID = Read_uint32_t( stream );
+			ai_assert( chunkID == ASSBIN_CHUNK_AIMATERIAL );
+			/*uint32_t size =*/
+			Read_uint32_t( stream );
+			mat.mNumAllocated = mat.mNumProperties = Read_unsigned_int( stream );
+
+			if ( mat.mNumProperties ) {
+
+				if ( mat.mProperties ) {
+
+					delete mat.mProperties;
+
+				}
+
+				mat.mProperties = [];
+
+				for ( var i = 0; i < mat.mNumProperties; ++ i ) {
+
+					mat.mProperties[ i ] = new aiMaterialProperty();
+					ReadBinaryMaterialProperty( stream, mat.mProperties[ i ] );
+
+				}
+
+			}
+
+		}
+		// -----------------------------------------------------------------------------------
+		function ReadBinaryNodeAnim( stream, nd ) {
+
+			var chunkID = Read_uint32_t( stream );
+			ai_assert( chunkID == ASSBIN_CHUNK_AINODEANIM );
+			/*uint32_t size =*/
+			Read_uint32_t( stream );
+			nd.mNodeName = Read_aiString( stream );
+			nd.mNumPositionKeys = Read_unsigned_int( stream );
+			nd.mNumRotationKeys = Read_unsigned_int( stream );
+			nd.mNumScalingKeys = Read_unsigned_int( stream );
+			nd.mPreState = Read_unsigned_int( stream );
+			nd.mPostState = Read_unsigned_int( stream );
+
+			if ( nd.mNumPositionKeys ) {
+
+				if ( shortened ) {
+
+					ReadBounds( stream, nd.mPositionKeys, nd.mNumPositionKeys );
+
+				} else {
+
+					// else write as usual
+
+					nd.mPositionKeys = [];
+					ReadArray_aiVectorKey( stream, nd.mPositionKeys, nd.mNumPositionKeys );
+
+				}
+
+			}
+
+			if ( nd.mNumRotationKeys ) {
+
+				if ( shortened ) {
+
+					ReadBounds( stream, nd.mRotationKeys, nd.mNumRotationKeys );
+
+				} else {
+
+		 			// else write as usual
+
+					nd.mRotationKeys = [];
+					ReadArray_aiQuatKey( stream, nd.mRotationKeys, nd.mNumRotationKeys );
+
+				}
+
+			}
+
+			if ( nd.mNumScalingKeys ) {
+
+				if ( shortened ) {
+
+					ReadBounds( stream, nd.mScalingKeys, nd.mNumScalingKeys );
+
+				} else {
+
+	 				// else write as usual
+
+					nd.mScalingKeys = [];
+					ReadArray_aiVectorKey( stream, nd.mScalingKeys, nd.mNumScalingKeys );
+
+				}
+
+			}
+
+		}
+		// -----------------------------------------------------------------------------------
+		function ReadBinaryAnim( stream, anim ) {
+
+			var chunkID = Read_uint32_t( stream );
+			ai_assert( chunkID == ASSBIN_CHUNK_AIANIMATION );
+			/*uint32_t size =*/
+			Read_uint32_t( stream );
+			anim.mName = Read_aiString( stream );
+			anim.mDuration = Read_double( stream );
+			anim.mTicksPerSecond = Read_double( stream );
+			anim.mNumChannels = Read_unsigned_int( stream );
+
+			if ( anim.mNumChannels ) {
+
+				anim.mChannels = [];
+
+				for ( var a = 0; a < anim.mNumChannels; ++ a ) {
+
+					anim.mChannels[ a ] = new aiNodeAnim();
+					ReadBinaryNodeAnim( stream, anim.mChannels[ a ] );
+
+				}
+
+			}
+
+		}
+
+		function ReadBinaryTexture( stream, tex ) {
+
+			var chunkID = Read_uint32_t( stream );
+			ai_assert( chunkID == ASSBIN_CHUNK_AITEXTURE );
+			/*uint32_t size =*/
+			Read_uint32_t( stream );
+			tex.mWidth = Read_unsigned_int( stream );
+			tex.mHeight = Read_unsigned_int( stream );
+			stream.ReadBytes( tex.achFormatHint, 1, 4 );
+
+			if ( ! shortened ) {
+
+				if ( ! tex.mHeight ) {
+
+					tex.pcData = [];
+					stream.ReadBytes( tex.pcData, 1, tex.mWidth );
+
+				} else {
+
+					tex.pcData = [];
+					stream.ReadBytes( tex.pcData, 1, tex.mWidth * tex.mHeight * 4 );
+
+				}
+
+			}
+
+		}
+		// -----------------------------------------------------------------------------------
+		function ReadBinaryLight( stream, l ) {
+
+			var chunkID = Read_uint32_t( stream );
+			ai_assert( chunkID == ASSBIN_CHUNK_AILIGHT );
+			/*uint32_t size =*/
+			Read_uint32_t( stream );
+			l.mName = Read_aiString( stream );
+			l.mType = Read_unsigned_int( stream );
+
+			if ( l.mType != aiLightSource_DIRECTIONAL ) {
+
+				l.mAttenuationConstant = readFloat( stream );
+				l.mAttenuationLinear = readFloat( stream );
+				l.mAttenuationQuadratic = readFloat( stream );
+
+			}
+
+			l.mColorDiffuse = Read_aiColor3D( stream );
+			l.mColorSpecular = Read_aiColor3D( stream );
+			l.mColorAmbient = Read_aiColor3D( stream );
+
+			if ( l.mType == aiLightSource_SPOT ) {
+
+				l.mAngleInnerCone = readFloat( stream );
+				l.mAngleOuterCone = readFloat( stream );
+
+			}
+
+		}
+		// -----------------------------------------------------------------------------------
+		function ReadBinaryCamera( stream, cam ) {
+
+			var chunkID = Read_uint32_t( stream );
+			ai_assert( chunkID == ASSBIN_CHUNK_AICAMERA );
+			/*uint32_t size =*/
+			Read_uint32_t( stream );
+			cam.mName = Read_aiString( stream );
+			cam.mPosition = Read_aiVector3D( stream );
+			cam.mLookAt = Read_aiVector3D( stream );
+			cam.mUp = Read_aiVector3D( stream );
+			cam.mHorizontalFOV = readFloat( stream );
+			cam.mClipPlaneNear = readFloat( stream );
+			cam.mClipPlaneFar = readFloat( stream );
+			cam.mAspect = readFloat( stream );
+
+		}
+
+		function ReadBinaryScene( stream, scene ) {
+
+			var chunkID = Read_uint32_t( stream );
+			ai_assert( chunkID == ASSBIN_CHUNK_AISCENE );
+			/*uint32_t size =*/
+			Read_uint32_t( stream );
+			scene.mFlags = Read_unsigned_int( stream );
+			scene.mNumMeshes = Read_unsigned_int( stream );
+			scene.mNumMaterials = Read_unsigned_int( stream );
+			scene.mNumAnimations = Read_unsigned_int( stream );
+			scene.mNumTextures = Read_unsigned_int( stream );
+			scene.mNumLights = Read_unsigned_int( stream );
+			scene.mNumCameras = Read_unsigned_int( stream );
+			// Read node graph
+			scene.mRootNode = new aiNode();
+			scene.mRootNode = ReadBinaryNode( stream, null, 0 );
+			// Read all meshes
+			if ( scene.mNumMeshes ) {
+
+				scene.mMeshes = [];
+
+				for ( var i = 0; i < scene.mNumMeshes; ++ i ) {
+
+					scene.mMeshes[ i ] = new aiMesh();
+					ReadBinaryMesh( stream, scene.mMeshes[ i ] );
+
+				}
+
+			}
+			// Read materials
+			if ( scene.mNumMaterials ) {
+
+				scene.mMaterials = [];
+
+				for ( var i = 0; i < scene.mNumMaterials; ++ i ) {
+
+					scene.mMaterials[ i ] = new aiMaterial();
+					ReadBinaryMaterial( stream, scene.mMaterials[ i ] );
+
+				}
+
+			}
+			// Read all animations
+			if ( scene.mNumAnimations ) {
+
+				scene.mAnimations = [];
+
+				for ( var i = 0; i < scene.mNumAnimations; ++ i ) {
+
+					scene.mAnimations[ i ] = new aiAnimation();
+					ReadBinaryAnim( stream, scene.mAnimations[ i ] );
+
+				}
+
+			}
+			// Read all textures
+			if ( scene.mNumTextures ) {
+
+				scene.mTextures = [];
+
+				for ( var i = 0; i < scene.mNumTextures; ++ i ) {
+
+					scene.mTextures[ i ] = new aiTexture();
+					ReadBinaryTexture( stream, scene.mTextures[ i ] );
+
+				}
+
+			}
+			// Read lights
+			if ( scene.mNumLights ) {
+
+				scene.mLights = [];
+
+				for ( var i = 0; i < scene.mNumLights; ++ i ) {
+
+					scene.mLights[ i ] = new aiLight();
+					ReadBinaryLight( stream, scene.mLights[ i ] );
+
+				}
+
+			}
+			// Read cameras
+			if ( scene.mNumCameras ) {
+
+				scene.mCameras = [];
+
+				for ( var i = 0; i < scene.mNumCameras; ++ i ) {
+
+					scene.mCameras[ i ] = new aiCamera();
+					ReadBinaryCamera( stream, scene.mCameras[ i ] );
+
+				}
+
+			}
+
+		}
+		var aiOrigin_CUR = 0;
+		var aiOrigin_BEG = 1;
+
+		function extendStream( stream ) {
+
+			stream.readOffset = 0;
+			stream.Seek = function ( off, ori ) {
+
+				if ( ori == aiOrigin_CUR ) {
+
+					stream.readOffset += off;
+
+				}
+				if ( ori == aiOrigin_BEG ) {
+
+					stream.readOffset = off;
+
+				}
+
+			};
+
+			stream.ReadBytes = function ( buff, size, n ) {
+
+				var bytes = size * n;
+				for ( var i = 0; i < bytes; i ++ )
+					buff[ i ] = Read_uint8_t( this );
+
+			};
+
+			stream.subArray32 = function ( start, end ) {
+
+				var buff = this.buffer;
+				var newbuff = buff.slice( start, end );
+				return new Float32Array( newbuff );
+
+			};
+
+			stream.subArrayUint16 = function ( start, end ) {
+
+				var buff = this.buffer;
+				var newbuff = buff.slice( start, end );
+				return new Uint16Array( newbuff );
+
+			};
+
+			stream.subArrayUint8 = function ( start, end ) {
+
+				var buff = this.buffer;
+				var newbuff = buff.slice( start, end );
+				return new Uint8Array( newbuff );
+
+			};
+
+			stream.subArrayUint32 = function ( start, end ) {
+
+				var buff = this.buffer;
+				var newbuff = buff.slice( start, end );
+				return new Uint32Array( newbuff );
+
+			};
+
+		}
+
+		var shortened, compressed;
+
+		function InternReadFile( pFiledata ) {
+
+			var pScene = new aiScene();
+			var stream = new DataView( pFiledata );
+			extendStream( stream );
+			stream.Seek( 44, aiOrigin_CUR ); // signature
+			/*unsigned int versionMajor =*/
+			var versionMajor = Read_unsigned_int( stream );
+			/*unsigned int versionMinor =*/
+			var versionMinor = Read_unsigned_int( stream );
+			/*unsigned int versionRevision =*/
+			var versionRevision = Read_unsigned_int( stream );
+			/*unsigned int compileFlags =*/
+			var compileFlags = Read_unsigned_int( stream );
+			shortened = Read_uint16_t( stream ) > 0;
+			compressed = Read_uint16_t( stream ) > 0;
+			if ( shortened )
+				throw "Shortened binaries are not supported!";
+			stream.Seek( 256, aiOrigin_CUR ); // original filename
+			stream.Seek( 128, aiOrigin_CUR ); // options
+			stream.Seek( 64, aiOrigin_CUR ); // padding
+			if ( compressed ) {
+
+				var uncompressedSize = Read_uint32_t( stream );
+				var compressedSize = stream.FileSize() - stream.Tell();
+				var compressedData = [];
+				stream.Read( compressedData, 1, compressedSize );
+				var uncompressedData = [];
+				uncompress( uncompressedData, uncompressedSize, compressedData, compressedSize );
+				var buff = new ArrayBuffer( uncompressedData );
+				ReadBinaryScene( buff, pScene );
+
+			} else {
+
+				ReadBinaryScene( stream, pScene );
+				return pScene.toTHREE();
+
+			}
+
+		}
+
+		return InternReadFile( buffer );
+
+	}
+
+};

+ 260 - 260
examples/js/loaders/LoaderSupport.js

@@ -1310,263 +1310,263 @@ THREE.LoaderSupport.WorkerSupport = (function () {
 
 })();
 
-/**
- * Orchestrate loading of multiple OBJ files/data from an instruction queue with a configurable amount of workers (1-16).
- * Workflow:
- *   prepareWorkers
- *   enqueueForRun
- *   processQueue
- *   tearDown (to force stop)
- *
- * @class
- *
- * @param {string} classDef Class definition to be used for construction
- * @param {THREE.LoaderSupport.ConsoleLogger} logger logger to be used
- */
-THREE.LoaderSupport.WorkerDirector = (function () {
-
-	var LOADER_WORKER_DIRECTOR_VERSION = '2.1.0';
-
-	var Validator = THREE.LoaderSupport.Validator;
-
-	var MAX_WEB_WORKER = 16;
-	var MAX_QUEUE_SIZE = 8192;
-
-	function WorkerDirector( classDef, logger ) {
-		this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() );
-		this.logger.logInfo( 'Using THREE.LoaderSupport.WorkerDirector version: ' + LOADER_WORKER_DIRECTOR_VERSION );
-
-		this.maxQueueSize = MAX_QUEUE_SIZE ;
-		this.maxWebWorkers = MAX_WEB_WORKER;
-		this.crossOrigin = null;
-
-		if ( ! Validator.isValid( classDef ) ) throw 'Provided invalid classDef: ' + classDef;
-
-		this.workerDescription = {
-			classDef: classDef,
-			globalCallbacks: {},
-			workerSupports: {}
-		};
-		this.objectsCompleted = 0;
-		this.instructionQueue = [];
-		this.instructionQueuePointer = 0;
-
-		this.callbackOnFinishedProcessing = null;
-	}
-
-	/**
-	 * Returns the maximum length of the instruction queue.
-	 * @memberOf THREE.LoaderSupport.WorkerDirector
-	 *
-	 * @returns {number}
-	 */
-	WorkerDirector.prototype.getMaxQueueSize = function () {
-		return this.maxQueueSize;
-	};
-
-	/**
-	 * Returns the maximum number of workers.
-	 * @memberOf THREE.LoaderSupport.WorkerDirector
-	 *
-	 * @returns {number}
-	 */
-	WorkerDirector.prototype.getMaxWebWorkers = function () {
-		return this.maxWebWorkers;
-	};
-
-	/**
-	 * Sets the CORS string to be used.
-	 * @memberOf THREE.LoaderSupport.WorkerDirector
-	 *
-	 * @param {string} crossOrigin CORS value
-	 */
-	WorkerDirector.prototype.setCrossOrigin = function ( crossOrigin ) {
-		this.crossOrigin = crossOrigin;
-	};
-
-	/**
-	 * Create or destroy workers according limits. Set the name and register callbacks for dynamically created web workers.
-	 * @memberOf THREE.LoaderSupport.WorkerDirector
-	 *
-	 * @param {THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks} globalCallbacks  Register global callbacks used by all web workers
-	 * @param {number} maxQueueSize Set the maximum size of the instruction queue (1-1024)
-	 * @param {number} maxWebWorkers Set the maximum amount of workers (1-16)
-	 */
-	WorkerDirector.prototype.prepareWorkers = function ( globalCallbacks, maxQueueSize, maxWebWorkers ) {
-		if ( Validator.isValid( globalCallbacks ) ) this.workerDescription.globalCallbacks = globalCallbacks;
-		this.maxQueueSize = Math.min( maxQueueSize, MAX_QUEUE_SIZE );
-		this.maxWebWorkers = Math.min( maxWebWorkers, MAX_WEB_WORKER );
-		this.maxWebWorkers = Math.min( this.maxWebWorkers, this.maxQueueSize );
-		this.objectsCompleted = 0;
-		this.instructionQueue = [];
-		this.instructionQueuePointer = 0;
-
-		for ( var instanceNo = 0; instanceNo < this.maxWebWorkers; instanceNo++ ) {
-
-			this.workerDescription.workerSupports[ instanceNo ] = {
-				instanceNo: instanceNo,
-				inUse: false,
-				terminateRequested: false,
-				workerSupport: new THREE.LoaderSupport.WorkerSupport( this.logger ),
-				loader: null
-			};
-
-		}
-	};
-
-	/**
-	 * Store run instructions in internal instructionQueue.
-	 * @memberOf THREE.LoaderSupport.WorkerDirector
-	 *
-	 * @param {THREE.LoaderSupport.PrepData} prepData
-	 */
-	WorkerDirector.prototype.enqueueForRun = function ( prepData ) {
-		if ( this.instructionQueue.length < this.maxQueueSize ) {
-			this.instructionQueue.push( prepData );
-		}
-	};
-
-	/**
-	 * Returns if any workers are running.
-	 *
-	 * @memberOf THREE.LoaderSupport.WorkerDirector
-	 * @returns {boolean}
-	 */
-	WorkerDirector.prototype.isRunning = function () {
-		var wsKeys = Object.keys( this.workerDescription.workerSupports );
-		return ( ( this.instructionQueue.length > 0 && this.instructionQueuePointer < this.instructionQueue.length ) || wsKeys.length > 0 );
-	};
-
-	/**
-	 * Process the instructionQueue until it is depleted.
-	 * @memberOf THREE.LoaderSupport.WorkerDirector
-	 */
-	WorkerDirector.prototype.processQueue = function () {
-		var prepData, supportDesc;
-		for ( var instanceNo in this.workerDescription.workerSupports ) {
-
-			supportDesc = this.workerDescription.workerSupports[ instanceNo ];
-			if ( ! supportDesc.inUse ) {
-
-				if ( this.instructionQueuePointer < this.instructionQueue.length ) {
-
-					prepData = this.instructionQueue[ this.instructionQueuePointer ];
-					this._kickWorkerRun( prepData, supportDesc );
-					this.instructionQueuePointer++;
-
-				} else {
-
-					this._deregister( supportDesc );
-
-				}
-
-			}
-
-		}
-
-		if ( ! this.isRunning() && this.callbackOnFinishedProcessing !== null ) {
-
-			this.callbackOnFinishedProcessing();
-			this.callbackOnFinishedProcessing = null;
-
-		}
-	};
-
-	WorkerDirector.prototype._kickWorkerRun = function( prepData, supportDesc ) {
-		supportDesc.inUse = true;
-		supportDesc.workerSupport.setTerminateRequested( supportDesc.terminateRequested );
-
-		this.logger.logInfo( '\nAssigning next item from queue to worker (queue length: ' + this.instructionQueue.length + ')\n\n' );
-
-		var scope = this;
-		var prepDataCallbacks = prepData.getCallbacks();
-		var globalCallbacks = this.workerDescription.globalCallbacks;
-		var wrapperOnLoad = function ( event ) {
-			if ( Validator.isValid( globalCallbacks.onLoad ) ) globalCallbacks.onLoad( event );
-			if ( Validator.isValid( prepDataCallbacks.onLoad ) ) prepDataCallbacks.onLoad( event );
-			scope.objectsCompleted++;
-			supportDesc.inUse = false;
-
-			scope.processQueue();
-		};
-
-		var wrapperOnProgress = function ( event ) {
-			if ( Validator.isValid( globalCallbacks.onProgress ) ) globalCallbacks.onProgress( event );
-			if ( Validator.isValid( prepDataCallbacks.onProgress ) ) prepDataCallbacks.onProgress( event );
-		};
-
-		var wrapperOnMeshAlter = function ( event ) {
-			if ( Validator.isValid( globalCallbacks.onMeshAlter ) ) globalCallbacks.onMeshAlter( event );
-			if ( Validator.isValid( prepDataCallbacks.onMeshAlter ) ) prepDataCallbacks.onMeshAlter( event );
-		};
-
-		supportDesc.loader = this._buildLoader( supportDesc.instanceNo );
-
-		var updatedCallbacks = new THREE.LoaderSupport.Callbacks();
-		updatedCallbacks.setCallbackOnLoad( wrapperOnLoad );
-		updatedCallbacks.setCallbackOnProgress( wrapperOnProgress );
-		updatedCallbacks.setCallbackOnMeshAlter( wrapperOnMeshAlter );
-		prepData.callbacks = updatedCallbacks;
-
-		supportDesc.loader.run( prepData, supportDesc.workerSupport );
-	};
-
-	WorkerDirector.prototype._buildLoader = function ( instanceNo ) {
-		var classDef = this.workerDescription.classDef;
-		var loader = Object.create( classDef.prototype );
-		this.workerDescription.classDef.call( loader, THREE.DefaultLoadingManager, this.logger );
-
-		// verify that all required functions are implemented
-		if ( ! loader.hasOwnProperty( 'instanceNo' ) ) throw classDef.name + ' has no property "instanceNo".';
-		loader.instanceNo = instanceNo;
-
-		if ( ! loader.hasOwnProperty( 'workerSupport' ) ) {
-
-			throw classDef.name + ' has no property "workerSupport".';
-
-		}
-		if ( typeof loader.run !== 'function'  ) throw classDef.name + ' has no function "run".';
-		if ( ! loader.hasOwnProperty( 'callbacks' ) || ! Validator.isValid( loader.callbacks ) ) {
-
-			this.logger.logWarn( classDef.name + ' has an invalid property "callbacks". Will change to "THREE.LoaderSupport.Callbacks"' );
-			loader.callbacks = new THREE.LoaderSupport.Callbacks();
-
-		}
-		return loader;
-	};
-
-	WorkerDirector.prototype._deregister = function ( supportDesc ) {
-		if ( Validator.isValid( supportDesc ) ) {
-
-			supportDesc.workerSupport.setTerminateRequested( true );
-			this.logger.logInfo( 'Requested termination of worker #' + supportDesc.instanceNo + '.' );
-
-			var loaderCallbacks = supportDesc.loader.callbacks;
-			if ( Validator.isValid( loaderCallbacks.onProgress ) ) loaderCallbacks.onProgress( { detail: { text: '' } } );
-			delete this.workerDescription.workerSupports[ supportDesc.instanceNo ];
-
-		}
-	};
-
-	/**
-	 * Terminate all workers.
-	 * @memberOf THREE.LoaderSupport.WorkerDirector
-	 *
-	 * @param {callback} callbackOnFinishedProcessing Function called once all workers finished processing.
-	 */
-	WorkerDirector.prototype.tearDown = function ( callbackOnFinishedProcessing ) {
-		this.logger.logInfo( 'WorkerDirector received the deregister call. Terminating all workers!' );
-
-		this.instructionQueuePointer = this.instructionQueue.length;
-		this.callbackOnFinishedProcessing = Validator.verifyInput( callbackOnFinishedProcessing, null );
-
-		for ( var name in this.workerDescription.workerSupports ) {
-
-			this.workerDescription.workerSupports[ name ].terminateRequested = true;
-
-		}
-	};
-
-	return WorkerDirector;
-
-})();
+/**
+ * Orchestrate loading of multiple OBJ files/data from an instruction queue with a configurable amount of workers (1-16).
+ * Workflow:
+ *   prepareWorkers
+ *   enqueueForRun
+ *   processQueue
+ *   tearDown (to force stop)
+ *
+ * @class
+ *
+ * @param {string} classDef Class definition to be used for construction
+ * @param {THREE.LoaderSupport.ConsoleLogger} logger logger to be used
+ */
+THREE.LoaderSupport.WorkerDirector = (function () {
+
+	var LOADER_WORKER_DIRECTOR_VERSION = '2.1.0';
+
+	var Validator = THREE.LoaderSupport.Validator;
+
+	var MAX_WEB_WORKER = 16;
+	var MAX_QUEUE_SIZE = 8192;
+
+	function WorkerDirector( classDef, logger ) {
+		this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() );
+		this.logger.logInfo( 'Using THREE.LoaderSupport.WorkerDirector version: ' + LOADER_WORKER_DIRECTOR_VERSION );
+
+		this.maxQueueSize = MAX_QUEUE_SIZE ;
+		this.maxWebWorkers = MAX_WEB_WORKER;
+		this.crossOrigin = null;
+
+		if ( ! Validator.isValid( classDef ) ) throw 'Provided invalid classDef: ' + classDef;
+
+		this.workerDescription = {
+			classDef: classDef,
+			globalCallbacks: {},
+			workerSupports: {}
+		};
+		this.objectsCompleted = 0;
+		this.instructionQueue = [];
+		this.instructionQueuePointer = 0;
+
+		this.callbackOnFinishedProcessing = null;
+	}
+
+	/**
+	 * Returns the maximum length of the instruction queue.
+	 * @memberOf THREE.LoaderSupport.WorkerDirector
+	 *
+	 * @returns {number}
+	 */
+	WorkerDirector.prototype.getMaxQueueSize = function () {
+		return this.maxQueueSize;
+	};
+
+	/**
+	 * Returns the maximum number of workers.
+	 * @memberOf THREE.LoaderSupport.WorkerDirector
+	 *
+	 * @returns {number}
+	 */
+	WorkerDirector.prototype.getMaxWebWorkers = function () {
+		return this.maxWebWorkers;
+	};
+
+	/**
+	 * Sets the CORS string to be used.
+	 * @memberOf THREE.LoaderSupport.WorkerDirector
+	 *
+	 * @param {string} crossOrigin CORS value
+	 */
+	WorkerDirector.prototype.setCrossOrigin = function ( crossOrigin ) {
+		this.crossOrigin = crossOrigin;
+	};
+
+	/**
+	 * Create or destroy workers according limits. Set the name and register callbacks for dynamically created web workers.
+	 * @memberOf THREE.LoaderSupport.WorkerDirector
+	 *
+	 * @param {THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks} globalCallbacks  Register global callbacks used by all web workers
+	 * @param {number} maxQueueSize Set the maximum size of the instruction queue (1-1024)
+	 * @param {number} maxWebWorkers Set the maximum amount of workers (1-16)
+	 */
+	WorkerDirector.prototype.prepareWorkers = function ( globalCallbacks, maxQueueSize, maxWebWorkers ) {
+		if ( Validator.isValid( globalCallbacks ) ) this.workerDescription.globalCallbacks = globalCallbacks;
+		this.maxQueueSize = Math.min( maxQueueSize, MAX_QUEUE_SIZE );
+		this.maxWebWorkers = Math.min( maxWebWorkers, MAX_WEB_WORKER );
+		this.maxWebWorkers = Math.min( this.maxWebWorkers, this.maxQueueSize );
+		this.objectsCompleted = 0;
+		this.instructionQueue = [];
+		this.instructionQueuePointer = 0;
+
+		for ( var instanceNo = 0; instanceNo < this.maxWebWorkers; instanceNo++ ) {
+
+			this.workerDescription.workerSupports[ instanceNo ] = {
+				instanceNo: instanceNo,
+				inUse: false,
+				terminateRequested: false,
+				workerSupport: new THREE.LoaderSupport.WorkerSupport( this.logger ),
+				loader: null
+			};
+
+		}
+	};
+
+	/**
+	 * Store run instructions in internal instructionQueue.
+	 * @memberOf THREE.LoaderSupport.WorkerDirector
+	 *
+	 * @param {THREE.LoaderSupport.PrepData} prepData
+	 */
+	WorkerDirector.prototype.enqueueForRun = function ( prepData ) {
+		if ( this.instructionQueue.length < this.maxQueueSize ) {
+			this.instructionQueue.push( prepData );
+		}
+	};
+
+	/**
+	 * Returns if any workers are running.
+	 *
+	 * @memberOf THREE.LoaderSupport.WorkerDirector
+	 * @returns {boolean}
+	 */
+	WorkerDirector.prototype.isRunning = function () {
+		var wsKeys = Object.keys( this.workerDescription.workerSupports );
+		return ( ( this.instructionQueue.length > 0 && this.instructionQueuePointer < this.instructionQueue.length ) || wsKeys.length > 0 );
+	};
+
+	/**
+	 * Process the instructionQueue until it is depleted.
+	 * @memberOf THREE.LoaderSupport.WorkerDirector
+	 */
+	WorkerDirector.prototype.processQueue = function () {
+		var prepData, supportDesc;
+		for ( var instanceNo in this.workerDescription.workerSupports ) {
+
+			supportDesc = this.workerDescription.workerSupports[ instanceNo ];
+			if ( ! supportDesc.inUse ) {
+
+				if ( this.instructionQueuePointer < this.instructionQueue.length ) {
+
+					prepData = this.instructionQueue[ this.instructionQueuePointer ];
+					this._kickWorkerRun( prepData, supportDesc );
+					this.instructionQueuePointer++;
+
+				} else {
+
+					this._deregister( supportDesc );
+
+				}
+
+			}
+
+		}
+
+		if ( ! this.isRunning() && this.callbackOnFinishedProcessing !== null ) {
+
+			this.callbackOnFinishedProcessing();
+			this.callbackOnFinishedProcessing = null;
+
+		}
+	};
+
+	WorkerDirector.prototype._kickWorkerRun = function( prepData, supportDesc ) {
+		supportDesc.inUse = true;
+		supportDesc.workerSupport.setTerminateRequested( supportDesc.terminateRequested );
+
+		this.logger.logInfo( '\nAssigning next item from queue to worker (queue length: ' + this.instructionQueue.length + ')\n\n' );
+
+		var scope = this;
+		var prepDataCallbacks = prepData.getCallbacks();
+		var globalCallbacks = this.workerDescription.globalCallbacks;
+		var wrapperOnLoad = function ( event ) {
+			if ( Validator.isValid( globalCallbacks.onLoad ) ) globalCallbacks.onLoad( event );
+			if ( Validator.isValid( prepDataCallbacks.onLoad ) ) prepDataCallbacks.onLoad( event );
+			scope.objectsCompleted++;
+			supportDesc.inUse = false;
+
+			scope.processQueue();
+		};
+
+		var wrapperOnProgress = function ( event ) {
+			if ( Validator.isValid( globalCallbacks.onProgress ) ) globalCallbacks.onProgress( event );
+			if ( Validator.isValid( prepDataCallbacks.onProgress ) ) prepDataCallbacks.onProgress( event );
+		};
+
+		var wrapperOnMeshAlter = function ( event ) {
+			if ( Validator.isValid( globalCallbacks.onMeshAlter ) ) globalCallbacks.onMeshAlter( event );
+			if ( Validator.isValid( prepDataCallbacks.onMeshAlter ) ) prepDataCallbacks.onMeshAlter( event );
+		};
+
+		supportDesc.loader = this._buildLoader( supportDesc.instanceNo );
+
+		var updatedCallbacks = new THREE.LoaderSupport.Callbacks();
+		updatedCallbacks.setCallbackOnLoad( wrapperOnLoad );
+		updatedCallbacks.setCallbackOnProgress( wrapperOnProgress );
+		updatedCallbacks.setCallbackOnMeshAlter( wrapperOnMeshAlter );
+		prepData.callbacks = updatedCallbacks;
+
+		supportDesc.loader.run( prepData, supportDesc.workerSupport );
+	};
+
+	WorkerDirector.prototype._buildLoader = function ( instanceNo ) {
+		var classDef = this.workerDescription.classDef;
+		var loader = Object.create( classDef.prototype );
+		this.workerDescription.classDef.call( loader, THREE.DefaultLoadingManager, this.logger );
+
+		// verify that all required functions are implemented
+		if ( ! loader.hasOwnProperty( 'instanceNo' ) ) throw classDef.name + ' has no property "instanceNo".';
+		loader.instanceNo = instanceNo;
+
+		if ( ! loader.hasOwnProperty( 'workerSupport' ) ) {
+
+			throw classDef.name + ' has no property "workerSupport".';
+
+		}
+		if ( typeof loader.run !== 'function'  ) throw classDef.name + ' has no function "run".';
+		if ( ! loader.hasOwnProperty( 'callbacks' ) || ! Validator.isValid( loader.callbacks ) ) {
+
+			this.logger.logWarn( classDef.name + ' has an invalid property "callbacks". Will change to "THREE.LoaderSupport.Callbacks"' );
+			loader.callbacks = new THREE.LoaderSupport.Callbacks();
+
+		}
+		return loader;
+	};
+
+	WorkerDirector.prototype._deregister = function ( supportDesc ) {
+		if ( Validator.isValid( supportDesc ) ) {
+
+			supportDesc.workerSupport.setTerminateRequested( true );
+			this.logger.logInfo( 'Requested termination of worker #' + supportDesc.instanceNo + '.' );
+
+			var loaderCallbacks = supportDesc.loader.callbacks;
+			if ( Validator.isValid( loaderCallbacks.onProgress ) ) loaderCallbacks.onProgress( { detail: { text: '' } } );
+			delete this.workerDescription.workerSupports[ supportDesc.instanceNo ];
+
+		}
+	};
+
+	/**
+	 * Terminate all workers.
+	 * @memberOf THREE.LoaderSupport.WorkerDirector
+	 *
+	 * @param {callback} callbackOnFinishedProcessing Function called once all workers finished processing.
+	 */
+	WorkerDirector.prototype.tearDown = function ( callbackOnFinishedProcessing ) {
+		this.logger.logInfo( 'WorkerDirector received the deregister call. Terminating all workers!' );
+
+		this.instructionQueuePointer = this.instructionQueue.length;
+		this.callbackOnFinishedProcessing = Validator.verifyInput( callbackOnFinishedProcessing, null );
+
+		for ( var name in this.workerDescription.workerSupports ) {
+
+			this.workerDescription.workerSupports[ name ].terminateRequested = true;
+
+		}
+	};
+
+	return WorkerDirector;
+
+})();

+ 33 - 33
examples/js/loaders/SVGLoader.js

@@ -1,33 +1,33 @@
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author zz85 / http://joshuakoo.com/
- */
-
-THREE.SVGLoader = function ( manager ) {
-
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-
-};
-
-THREE.SVGLoader.prototype = {
-
-	constructor: THREE.SVGLoader,
-
-	load: function ( url, onLoad, onProgress, onError ) {
-
-		var scope = this;
-
-		var parser = new DOMParser();
-
-		var loader = new THREE.FileLoader( scope.manager );
-		loader.load( url, function ( svgString ) {
-
-			var doc = parser.parseFromString( svgString, 'image/svg+xml' ); // application/xml
-
-			onLoad( doc.documentElement );
-
-		}, onProgress, onError );
-
-	}
-
-};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author zz85 / http://joshuakoo.com/
+ */
+
+THREE.SVGLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.SVGLoader.prototype = {
+
+	constructor: THREE.SVGLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var parser = new DOMParser();
+
+		var loader = new THREE.FileLoader( scope.manager );
+		loader.load( url, function ( svgString ) {
+
+			var doc = parser.parseFromString( svgString, 'image/svg+xml' ); // application/xml
+
+			onLoad( doc.documentElement );
+
+		}, onProgress, onError );
+
+	}
+
+};

+ 1142 - 1142
examples/js/loaders/TDSLoader.js

@@ -1,1142 +1,1142 @@
-/*
- * Autodesk 3DS threee.js file loader, based on lib3ds.
- *
- * Loads geometry with uv and materials basic properties with texture support.
- *
- * @author @tentone
- * @author @timknip
- * @class TDSLoader
- * @constructor
- */
-
-'use strict';
-
-THREE.TDSLoader = function ( manager ) {
-
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-	this.debug = false;
-
-	this.group = null;
-	this.position = 0;
-
-	this.materials = [];
-	this.meshes = [];
-
-};
-
-THREE.TDSLoader.prototype = {
-
-	constructor: THREE.TDSLoader,
-
-	/**
-	 * Load 3ds file from url.
-	 *
-	 * @method load
-	 * @param {[type]} url URL for the file.
-	 * @param {Function} onLoad onLoad callback, receives group Object3D as argument.
-	 * @param {Function} onProgress onProgress callback.
-	 * @param {Function} onError onError callback.
-	 */
-	load: function ( url, onLoad, onProgress, onError ) {
-
-		var scope = this;
-
-		var path = this.path !== undefined ? this.path : THREE.LoaderUtils.extractUrlBase( url );
-
-		var loader = new THREE.FileLoader( this.manager );
-
-		loader.setResponseType( 'arraybuffer' );
-
-		loader.load( url, function ( data ) {
-
-			onLoad( scope.parse( data, path ) );
-
-		}, onProgress, onError );
-
-	},
-
-	/**
-	 * Parse arraybuffer data and load 3ds file.
-	 *
-	 * @method parse
-	 * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded.
-	 * @param {String} path Path for external resources.
-	 * @return {Object3D} Group loaded from 3ds file.
-	 */
-	parse: function ( arraybuffer, path ) {
-
-		this.group = new THREE.Group();
-		this.position = 0;
-		this.materials = [];
-		this.meshes = [];
-
-		this.readFile( arraybuffer, path );
-
-		for ( var i = 0; i < this.meshes.length; i ++ ) {
-
-			this.group.add( this.meshes[ i ] );
-
-		}
-
-		return this.group;
-
-	},
-
-	/**
-	 * Decode file content to read 3ds data.
-	 *
-	 * @method readFile
-	 * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded.
-	 */
-	readFile: function ( arraybuffer, path ) {
-
-		var data = new DataView( arraybuffer );
-		var chunk = this.readChunk( data );
-
-		if ( chunk.id === MLIBMAGIC || chunk.id === CMAGIC || chunk.id === M3DMAGIC ) {
-
-			var next = this.nextChunk( data, chunk );
-
-			while ( next !== 0 ) {
-
-				if ( next === M3D_VERSION ) {
-
-					var version = this.readDWord( data );
-					this.debugMessage( '3DS file version: ' + version );
-
-				} else if ( next === MDATA ) {
-
-					this.resetPosition( data );
-					this.readMeshData( data, path );
-
-				} else {
-
-					this.debugMessage( 'Unknown main chunk: ' + next.toString( 16 ) );
-
-				}
-
-				next = this.nextChunk( data, chunk );
-
-			}
-
-		}
-
-		this.debugMessage( 'Parsed ' + this.meshes.length + ' meshes' );
-
-	},
-
-	/**
-	 * Read mesh data chunk.
-	 *
-	 * @method readMeshData
-	 * @param {Dataview} data Dataview in use.
-	 */
-	readMeshData: function ( data, path ) {
-
-		var chunk = this.readChunk( data );
-		var next = this.nextChunk( data, chunk );
-
-		while ( next !== 0 ) {
-
-			if ( next === MESH_VERSION ) {
-
-				var version = + this.readDWord( data );
-				this.debugMessage( 'Mesh Version: ' + version );
-
-			} else if ( next === MASTER_SCALE ) {
-
-				var scale = this.readFloat( data );
-				this.debugMessage( 'Master scale: ' + scale );
-				this.group.scale.set( scale, scale, scale );
-
-			} else if ( next === NAMED_OBJECT ) {
-
-				this.debugMessage( 'Named Object' );
-				this.resetPosition( data );
-				this.readNamedObject( data );
-
-			} else if ( next === MAT_ENTRY ) {
-
-				this.debugMessage( 'Material' );
-				this.resetPosition( data );
-				this.readMaterialEntry( data, path );
-
-			} else {
-
-				this.debugMessage( 'Unknown MDATA chunk: ' + next.toString( 16 ) );
-
-			}
-
-			next = this.nextChunk( data, chunk );
-
-		}
-
-	},
-
-	/**
-	 * Read named object chunk.
-	 *
-	 * @method readNamedObject
-	 * @param {Dataview} data Dataview in use.
-	 */
-	readNamedObject: function ( data ) {
-
-		var chunk = this.readChunk( data );
-		var name = this.readString( data, 64 );
-		chunk.cur = this.position;
-
-		var next = this.nextChunk( data, chunk );
-		while ( next !== 0 ) {
-
-			if ( next === N_TRI_OBJECT ) {
-
-				this.resetPosition( data );
-				var mesh = this.readMesh( data );
-				mesh.name = name;
-				this.meshes.push( mesh );
-
-			} else {
-
-				this.debugMessage( 'Unknown named object chunk: ' + next.toString( 16 ) );
-
-			}
-
-			next = this.nextChunk( data, chunk );
-
-		}
-
-		this.endChunk( chunk );
-
-	},
-
-	/**
-	 * Read material data chunk and add it to the material list.
-	 *
-	 * @method readMaterialEntry
-	 * @param {Dataview} data Dataview in use.
-	 */
-	readMaterialEntry: function ( data, path ) {
-
-		var chunk = this.readChunk( data );
-		var next = this.nextChunk( data, chunk );
-		var material = new THREE.MeshPhongMaterial();
-
-		while ( next !== 0 ) {
-
-			if ( next === MAT_NAME ) {
-
-				material.name = this.readString( data, 64 );
-				this.debugMessage( '   Name: ' + material.name );
-
-			} else if ( next === MAT_WIRE ) {
-
-				this.debugMessage( '   Wireframe' );
-				material.wireframe = true;
-
-			} else if ( next === MAT_WIRE_SIZE ) {
-
-				var value = this.readByte( data );
-				material.wireframeLinewidth = value;
-				this.debugMessage( '   Wireframe Thickness: ' + value );
-
-			} else if ( next === MAT_TWO_SIDE ) {
-
-				material.side = THREE.DoubleSide;
-				this.debugMessage( '   DoubleSided' );
-
-			} else if ( next === MAT_ADDITIVE ) {
-
-				this.debugMessage( '   Additive Blending' );
-				material.blending = THREE.AdditiveBlending;
-
-			} else if ( next === MAT_DIFFUSE ) {
-
-				this.debugMessage( '   Diffuse Color' );
-				material.color = this.readColor( data );
-
-			} else if ( next === MAT_SPECULAR ) {
-
-				this.debugMessage( '   Specular Color' );
-				material.specular = this.readColor( data );
-
-			} else if ( next === MAT_AMBIENT ) {
-
-				this.debugMessage( '   Ambient color' );
-				material.color = this.readColor( data );
-
-			} else if ( next === MAT_SHININESS ) {
-
-				var shininess = this.readWord( data );
-				material.shininess = shininess;
-				this.debugMessage( '   Shininess : ' + shininess );
-
-			} else if ( next === MAT_TEXMAP ) {
-
-				this.debugMessage( '   ColorMap' );
-				this.resetPosition( data );
-				material.map = this.readMap( data, path );
-
-			} else if ( next === MAT_BUMPMAP ) {
-
-				this.debugMessage( '   BumpMap' );
-				this.resetPosition( data );
-				material.bumpMap = this.readMap( data, path );
-
-			} else if ( next === MAT_OPACMAP ) {
-
-				this.debugMessage( '   OpacityMap' );
-				this.resetPosition( data );
-				material.alphaMap = this.readMap( data, path );
-
-			} else if ( next === MAT_SPECMAP ) {
-
-				this.debugMessage( '   SpecularMap' );
-				this.resetPosition( data );
-				material.specularMap = this.readMap( data, path );
-
-			} else {
-
-				this.debugMessage( '   Unknown material chunk: ' + next.toString( 16 ) );
-
-			}
-
-			next = this.nextChunk( data, chunk );
-
-		}
-
-		this.endChunk( chunk );
-
-		this.materials[ material.name ] = material;
-
-	},
-
-	/**
-	 * Read mesh data chunk.
-	 *
-	 * @method readMesh
-	 * @param {Dataview} data Dataview in use.
-	 */
-	readMesh: function ( data ) {
-
-		var chunk = this.readChunk( data );
-		var next = this.nextChunk( data, chunk );
-
-		var useBufferGeometry = false;
-		var geometry = null;
-		var uvs = [];
-
-		if ( useBufferGeometry ) {
-
-			geometry = new THREE.BufferGeometry();
-
-		}	else {
-
-			geometry = new THREE.Geometry();
-
-		}
-
-		var material = new THREE.MeshPhongMaterial();
-		var mesh = new THREE.Mesh( geometry, material );
-		mesh.name = 'mesh';
-
-		while ( next !== 0 ) {
-
-			if ( next === POINT_ARRAY ) {
-
-				var points = this.readWord( data );
-
-				this.debugMessage( '   Vertex: ' + points );
-
-				//BufferGeometry
-
-				if ( useBufferGeometry )	{
-
-					var vertices = [];
-					for ( var i = 0; i < points; i ++ )		{
-
-						vertices.push( this.readFloat( data ) );
-						vertices.push( this.readFloat( data ) );
-						vertices.push( this.readFloat( data ) );
-
-					}
-
-					geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
-
-				} else	{ //Geometry
-
-					for ( var i = 0; i < points; i ++ )		{
-
-						geometry.vertices.push( new THREE.Vector3( this.readFloat( data ), this.readFloat( data ), this.readFloat( data ) ) );
-
-					}
-
-				}
-
-			} else if ( next === FACE_ARRAY ) {
-
-				this.resetPosition( data );
-				this.readFaceArray( data, mesh );
-
-			} else if ( next === TEX_VERTS ) {
-
-				var texels = this.readWord( data );
-
-				this.debugMessage( '   UV: ' + texels );
-
-				//BufferGeometry
-
-				if ( useBufferGeometry )	{
-
-					var uvs = [];
-					for ( var i = 0; i < texels; i ++ )		{
-
-						uvs.push( this.readFloat( data ) );
-						uvs.push( this.readFloat( data ) );
-
-					}
-					geometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( uvs ), 2 ) );
-
-				} else { //Geometry
-
-					uvs = [];
-					for ( var i = 0; i < texels; i ++ )		{
-
-						uvs.push( new THREE.Vector2( this.readFloat( data ), this.readFloat( data ) ) );
-
-					}
-
-				}
-
-			} else if ( next === MESH_MATRIX ) {
-
-				this.debugMessage( '   Tranformation Matrix (TODO)' );
-
-				var values = [];
-				for ( var i = 0; i < 12; i ++ ) {
-
-					values[ i ] = this.readFloat( data );
-
-				}
-
-				var matrix = new THREE.Matrix4();
-
-				//X Line
-				matrix.elements[ 0 ] = values[ 0 ];
-				matrix.elements[ 1 ] = values[ 6 ];
-				matrix.elements[ 2 ] = values[ 3 ];
-				matrix.elements[ 3 ] = values[ 9 ];
-
-				//Y Line
-				matrix.elements[ 4 ] = values[ 2 ];
-				matrix.elements[ 5 ] = values[ 8 ];
-				matrix.elements[ 6 ] = values[ 5 ];
-				matrix.elements[ 7 ] = values[ 11 ];
-
-				//Z Line
-				matrix.elements[ 8 ] = values[ 1 ];
-				matrix.elements[ 9 ] = values[ 7 ];
-				matrix.elements[ 10 ] = values[ 4 ];
-				matrix.elements[ 11 ] = values[ 10 ];
-
-				//W Line
-				matrix.elements[ 12 ] = 0;
-				matrix.elements[ 13 ] = 0;
-				matrix.elements[ 14 ] = 0;
-				matrix.elements[ 15 ] = 1;
-
-				matrix.transpose();
-
-				var inverse = new THREE.Matrix4();
-				inverse.getInverse( matrix, true );
-				geometry.applyMatrix( inverse );
-
-				matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
-
-			} else {
-
-				this.debugMessage( '   Unknown mesh chunk: ' + next.toString( 16 ) );
-
-			}
-
-			next = this.nextChunk( data, chunk );
-
-		}
-
-		this.endChunk( chunk );
-
-		if ( ! useBufferGeometry ) {
-
-			//geometry.faceVertexUvs[0][faceIndex][vertexIndex]
-
-			if ( uvs.length > 0 ) {
-
-				var faceUV = [];
-
-				for ( var i = 0; i < geometry.faces.length; i ++ ) {
-
-					faceUV.push( [ uvs[ geometry.faces[ i ].a ], uvs[ geometry.faces[ i ].b ], uvs[ geometry.faces[ i ].c ] ] );
-
-				}
-
-				geometry.faceVertexUvs[ 0 ] = faceUV;
-
-			}
-
-			geometry.computeVertexNormals();
-
-		}
-
-		return mesh;
-
-	},
-
-	/**
-	 * Read face array data chunk.
-	 *
-	 * @method readFaceArray
-	 * @param {Dataview} data Dataview in use.
-	 * @param {Mesh} mesh Mesh to be filled with the data read.
-	 */
-	readFaceArray: function ( data, mesh ) {
-
-		var chunk = this.readChunk( data );
-		var faces = this.readWord( data );
-
-		this.debugMessage( '   Faces: ' + faces );
-
-		for ( var i = 0; i < faces; ++ i ) {
-
-			mesh.geometry.faces.push( new THREE.Face3( this.readWord( data ), this.readWord( data ), this.readWord( data ) ) );
-
-			var visibility = this.readWord( data );
-
-		}
-
-		//The rest of the FACE_ARRAY chunk is subchunks
-
-		while ( this.position < chunk.end ) {
-
-			var chunk = this.readChunk( data );
-
-			if ( chunk.id === MSH_MAT_GROUP ) {
-
-				this.debugMessage( '      Material Group' );
-
-				this.resetPosition( data );
-
-				var group = this.readMaterialGroup( data );
-
-				var material = this.materials[ group.name ];
-
-				if ( material !== undefined )	{
-
-					mesh.material = material;
-
-					if ( material.name === '' )		{
-
-						material.name = mesh.name;
-
-					}
-
-				}
-
-			} else {
-
-				this.debugMessage( '      Unknown face array chunk: ' + chunk.toString( 16 ) );
-
-			}
-
-			this.endChunk( chunk );
-
-		}
-
-		this.endChunk( chunk );
-
-	},
-
-	/**
-	 * Read texture map data chunk.
-	 *
-	 * @method readMap
-	 * @param {Dataview} data Dataview in use.
-	 * @return {Texture} Texture read from this data chunk.
-	 */
-	readMap: function ( data, path ) {
-
-		var chunk = this.readChunk( data );
-		var next = this.nextChunk( data, chunk );
-		var texture = {};
-
-		var loader = new THREE.TextureLoader( this.manager );
-		loader.setPath( path );
-
-		while ( next !== 0 ) {
-
-			if ( next === MAT_MAPNAME ) {
-
-				var name = this.readString( data, 128 );
-				texture = loader.load( name );
-
-				this.debugMessage( '      File: ' + path + name );
-
-			} else if ( next === MAT_MAP_UOFFSET ) {
-
-				texture.offset.x = this.readFloat( data );
-				this.debugMessage( '      OffsetX: ' + texture.offset.x );
-
-			} else if ( next === MAT_MAP_VOFFSET ) {
-
-				texture.offset.y = this.readFloat( data );
-				this.debugMessage( '      OffsetY: ' + texture.offset.y );
-
-			} else if ( next === MAT_MAP_USCALE ) {
-
-				texture.repeat.x = this.readFloat( data );
-				this.debugMessage( '      RepeatX: ' + texture.repeat.x );
-
-			} else if ( next === MAT_MAP_VSCALE ) {
-
-				texture.repeat.y = this.readFloat( data );
-				this.debugMessage( '      RepeatY: ' + texture.repeat.y );
-
-			} else {
-
-				this.debugMessage( '      Unknown map chunk: ' + next.toString( 16 ) );
-
-			}
-
-			next = this.nextChunk( data, chunk );
-
-		}
-
-		this.endChunk( chunk );
-
-		return texture;
-
-	},
-
-	/**
-	 * Read material group data chunk.
-	 *
-	 * @method readMaterialGroup
-	 * @param {Dataview} data Dataview in use.
-	 * @return {Object} Object with name and index of the object.
-	 */
-	readMaterialGroup: function ( data ) {
-
-		var chunk = this.readChunk( data );
-		var name = this.readString( data, 64 );
-		var numFaces = this.readWord( data );
-
-		this.debugMessage( '         Name: ' + name );
-		this.debugMessage( '         Faces: ' + numFaces );
-
-		var index = [];
-		for ( var i = 0; i < numFaces; ++ i ) {
-
-			index.push( this.readWord( data ) );
-
-		}
-
-		return { name: name, index: index };
-
-	},
-
-	/**
-	 * Read a color value.
-	 *
-	 * @method readColor
-	 * @param {DataView} data Dataview.
-	 * @return {Color} Color value read..
-	 */
-	readColor: function ( data ) {
-
-		var chunk = this.readChunk( data );
-		var color = new THREE.Color();
-
-		if ( chunk.id === COLOR_24 || chunk.id === LIN_COLOR_24 ) {
-
-			var r = this.readByte( data );
-			var g = this.readByte( data );
-			var b = this.readByte( data );
-
-			color.setRGB( r / 255, g / 255, b / 255 );
-
-			this.debugMessage( '      Color: ' + color.r + ', ' + color.g + ', ' + color.b );
-
-		}	else if ( chunk.id === COLOR_F || chunk.id === LIN_COLOR_F ) {
-
-			var r = this.readFloat( data );
-			var g = this.readFloat( data );
-			var b = this.readFloat( data );
-
-			color.setRGB( r, g, b );
-
-			this.debugMessage( '      Color: ' + color.r + ', ' + color.g + ', ' + color.b );
-
-		}	else {
-
-			this.debugMessage( '      Unknown color chunk: ' + chunk.toString( 16 ) );
-
-		}
-
-		this.endChunk( chunk );
-		return color;
-
-	},
-
-	/**
-	 * Read next chunk of data.
-	 *
-	 * @method readChunk
-	 * @param {DataView} data Dataview.
-	 * @return {Object} Chunk of data read.
-	 */
-	readChunk: function ( data ) {
-
-		var chunk = {};
-
-		chunk.cur = this.position;
-		chunk.id = this.readWord( data );
-		chunk.size = this.readDWord( data );
-		chunk.end = chunk.cur + chunk.size;
-		chunk.cur += 6;
-
-		return chunk;
-
-	},
-
-	/**
-	 * Set position to the end of the current chunk of data.
-	 *
-	 * @method endChunk
-	 * @param {Object} chunk Data chunk.
-	 */
-	endChunk: function ( chunk ) {
-
-		this.position = chunk.end;
-
-	},
-
-	/**
-	 * Move to the next data chunk.
-	 *
-	 * @method nextChunk
-	 * @param {DataView} data Dataview.
-	 * @param {Object} chunk Data chunk.
-	 */
-	nextChunk: function ( data, chunk ) {
-
-		if ( chunk.cur >= chunk.end ) {
-
-			return 0;
-
-		}
-
-		this.position = chunk.cur;
-
-		try {
-
-			var next = this.readChunk( data );
-			chunk.cur += next.size;
-			return next.id;
-
-		}	catch ( e ) {
-
-			this.debugMessage( 'Unable to read chunk at ' + this.position );
-			return 0;
-
-		}
-
-	},
-
-	/**
-	 * Reset dataview position.
-	 *
-	 * @method resetPosition
-	 * @param {DataView} data Dataview.
-	 */
-	resetPosition: function () {
-
-		this.position -= 6;
-
-	},
-
-	/**
-	 * Read byte value.
-	 *
-	 * @method readByte
-	 * @param {DataView} data Dataview to read data from.
-	 * @return {Number} Data read from the dataview.
-	 */
-	readByte: function ( data ) {
-
-		var v = data.getUint8( this.position, true );
-		this.position += 1;
-		return v;
-
-	},
-
-	/**
-	 * Read 32 bit float value.
-	 *
-	 * @method readFloat
-	 * @param {DataView} data Dataview to read data from.
-	 * @return {Number} Data read from the dataview.
-	 */
-	readFloat: function ( data ) {
-
-		try {
-
-			var v = data.getFloat32( this.position, true );
-			this.position += 4;
-			return v;
-
-		}	catch ( e ) {
-
-			this.debugMessage( e + ' ' + this.position + ' ' + data.byteLength );
-
-		}
-
-	},
-
-	/**
-	 * Read 32 bit signed integer value.
-	 *
-	 * @method readInt
-	 * @param {DataView} data Dataview to read data from.
-	 * @return {Number} Data read from the dataview.
-	 */
-	readInt: function ( data ) {
-
-		var v = data.getInt32( this.position, true );
-		this.position += 4;
-		return v;
-
-	},
-
-	/**
-	 * Read 16 bit signed integer value.
-	 *
-	 * @method readShort
-	 * @param {DataView} data Dataview to read data from.
-	 * @return {Number} Data read from the dataview.
-	 */
-	readShort: function ( data ) {
-
-		var v = data.getInt16( this.position, true );
-		this.position += 2;
-		return v;
-
-	},
-
-	/**
-	 * Read 64 bit unsigned integer value.
-	 *
-	 * @method readDWord
-	 * @param {DataView} data Dataview to read data from.
-	 * @return {Number} Data read from the dataview.
-	 */
-	readDWord: function ( data ) {
-
-		var v = data.getUint32( this.position, true );
-		this.position += 4;
-		return v;
-
-	},
-
-	/**
-	 * Read 32 bit unsigned integer value.
-	 *
-	 * @method readWord
-	 * @param {DataView} data Dataview to read data from.
-	 * @return {Number} Data read from the dataview.
-	 */
-	readWord: function ( data ) {
-
-		var v = data.getUint16( this.position, true );
-		this.position += 2;
-		return v;
-
-	},
-
-	/**
-	 * Read string value.
-	 *
-	 * @method readString
-	 * @param {DataView} data Dataview to read data from.
-	 * @param {Number} maxLength Max size of the string to be read.
-	 * @return {String} Data read from the dataview.
-	 */
-	readString: function ( data, maxLength ) {
-
-		var s = '';
-
-		for ( var i = 0; i < maxLength; i ++ ) {
-
-			var c = this.readByte( data );
-			if ( ! c ) {
-
-				break;
-
-			}
-
-			s += String.fromCharCode( c );
-
-		}
-
-		return s;
-
-	},
-
-	/**
-	 * Set resource path used to determine the file path to attached resources.
-	 *
-	 * @method setPath
-	 * @param {String} path Path to resources.
-	 * @return Self for chaining.
-	 */
-	setPath: function ( path ) {
-
-		this.path = path;
-
-		return this;
-
-	},
-
-	/**
-	 * Print debug message to the console.
-	 *
-	 * Is controlled by a flag to show or hide debug messages.
-	 *
-	 * @method debugMessage
-	 * @param {Object} message Debug message to print to the console.
-	 */
-	debugMessage: function ( message ) {
-
-		if ( this.debug ) {
-
-			console.log( message );
-
-		}
-
-	}
-};
-
-var NULL_CHUNK = 0x0000;
-var M3DMAGIC = 0x4D4D;
-var SMAGIC = 0x2D2D;
-var LMAGIC = 0x2D3D;
-var MLIBMAGIC = 0x3DAA;
-var MATMAGIC = 0x3DFF;
-var CMAGIC = 0xC23D;
-var M3D_VERSION = 0x0002;
-var M3D_KFVERSION = 0x0005;
-var COLOR_F = 0x0010;
-var COLOR_24 = 0x0011;
-var LIN_COLOR_24 = 0x0012;
-var LIN_COLOR_F = 0x0013;
-var INT_PERCENTAGE = 0x0030;
-var FLOAT_PERCENTAGE = 0x0031;
-var MDATA = 0x3D3D;
-var MESH_VERSION = 0x3D3E;
-var MASTER_SCALE = 0x0100;
-var LO_SHADOW_BIAS = 0x1400;
-var HI_SHADOW_BIAS = 0x1410;
-var SHADOW_MAP_SIZE = 0x1420;
-var SHADOW_SAMPLES = 0x1430;
-var SHADOW_RANGE = 0x1440;
-var SHADOW_FILTER = 0x1450;
-var RAY_BIAS = 0x1460;
-var O_CONSTS = 0x1500;
-var AMBIENT_LIGHT = 0x2100;
-var BIT_MAP = 0x1100;
-var SOLID_BGND = 0x1200;
-var V_GRADIENT = 0x1300;
-var USE_BIT_MAP = 0x1101;
-var USE_SOLID_BGND = 0x1201;
-var USE_V_GRADIENT = 0x1301;
-var FOG = 0x2200;
-var FOG_BGND = 0x2210;
-var LAYER_FOG = 0x2302;
-var DISTANCE_CUE = 0x2300;
-var DCUE_BGND = 0x2310;
-var USE_FOG = 0x2201;
-var USE_LAYER_FOG = 0x2303;
-var USE_DISTANCE_CUE = 0x2301;
-var MAT_ENTRY = 0xAFFF;
-var MAT_NAME = 0xA000;
-var MAT_AMBIENT = 0xA010;
-var MAT_DIFFUSE = 0xA020;
-var MAT_SPECULAR = 0xA030;
-var MAT_SHININESS = 0xA040;
-var MAT_SHIN2PCT = 0xA041;
-var MAT_TRANSPARENCY = 0xA050;
-var MAT_XPFALL = 0xA052;
-var MAT_USE_XPFALL = 0xA240;
-var MAT_REFBLUR = 0xA053;
-var MAT_SHADING = 0xA100;
-var MAT_USE_REFBLUR = 0xA250;
-var MAT_SELF_ILLUM = 0xA084;
-var MAT_TWO_SIDE = 0xA081;
-var MAT_DECAL = 0xA082;
-var MAT_ADDITIVE = 0xA083;
-var MAT_WIRE = 0xA085;
-var MAT_FACEMAP = 0xA088;
-var MAT_TRANSFALLOFF_IN = 0xA08A;
-var MAT_PHONGSOFT = 0xA08C;
-var MAT_WIREABS = 0xA08E;
-var MAT_WIRE_SIZE = 0xA087;
-var MAT_TEXMAP = 0xA200;
-var MAT_SXP_TEXT_DATA = 0xA320;
-var MAT_TEXMASK = 0xA33E;
-var MAT_SXP_TEXTMASK_DATA = 0xA32A;
-var MAT_TEX2MAP = 0xA33A;
-var MAT_SXP_TEXT2_DATA = 0xA321;
-var MAT_TEX2MASK = 0xA340;
-var MAT_SXP_TEXT2MASK_DATA = 0xA32C;
-var MAT_OPACMAP = 0xA210;
-var MAT_SXP_OPAC_DATA = 0xA322;
-var MAT_OPACMASK = 0xA342;
-var MAT_SXP_OPACMASK_DATA = 0xA32E;
-var MAT_BUMPMAP = 0xA230;
-var MAT_SXP_BUMP_DATA = 0xA324;
-var MAT_BUMPMASK = 0xA344;
-var MAT_SXP_BUMPMASK_DATA = 0xA330;
-var MAT_SPECMAP = 0xA204;
-var MAT_SXP_SPEC_DATA = 0xA325;
-var MAT_SPECMASK = 0xA348;
-var MAT_SXP_SPECMASK_DATA = 0xA332;
-var MAT_SHINMAP = 0xA33C;
-var MAT_SXP_SHIN_DATA = 0xA326;
-var MAT_SHINMASK = 0xA346;
-var MAT_SXP_SHINMASK_DATA = 0xA334;
-var MAT_SELFIMAP = 0xA33D;
-var MAT_SXP_SELFI_DATA = 0xA328;
-var MAT_SELFIMASK = 0xA34A;
-var MAT_SXP_SELFIMASK_DATA = 0xA336;
-var MAT_REFLMAP = 0xA220;
-var MAT_REFLMASK = 0xA34C;
-var MAT_SXP_REFLMASK_DATA = 0xA338;
-var MAT_ACUBIC = 0xA310;
-var MAT_MAPNAME = 0xA300;
-var MAT_MAP_TILING = 0xA351;
-var MAT_MAP_TEXBLUR = 0xA353;
-var MAT_MAP_USCALE = 0xA354;
-var MAT_MAP_VSCALE = 0xA356;
-var MAT_MAP_UOFFSET = 0xA358;
-var MAT_MAP_VOFFSET = 0xA35A;
-var MAT_MAP_ANG = 0xA35C;
-var MAT_MAP_COL1 = 0xA360;
-var MAT_MAP_COL2 = 0xA362;
-var MAT_MAP_RCOL = 0xA364;
-var MAT_MAP_GCOL = 0xA366;
-var MAT_MAP_BCOL = 0xA368;
-var NAMED_OBJECT = 0x4000;
-var N_DIRECT_LIGHT = 0x4600;
-var DL_OFF = 0x4620;
-var DL_OUTER_RANGE = 0x465A;
-var DL_INNER_RANGE = 0x4659;
-var DL_MULTIPLIER = 0x465B;
-var DL_EXCLUDE = 0x4654;
-var DL_ATTENUATE = 0x4625;
-var DL_SPOTLIGHT = 0x4610;
-var DL_SPOT_ROLL = 0x4656;
-var DL_SHADOWED = 0x4630;
-var DL_LOCAL_SHADOW2 = 0x4641;
-var DL_SEE_CONE = 0x4650;
-var DL_SPOT_RECTANGULAR = 0x4651;
-var DL_SPOT_ASPECT = 0x4657;
-var DL_SPOT_PROJECTOR = 0x4653;
-var DL_SPOT_OVERSHOOT = 0x4652;
-var DL_RAY_BIAS = 0x4658;
-var DL_RAYSHAD = 0x4627;
-var N_CAMERA = 0x4700;
-var CAM_SEE_CONE = 0x4710;
-var CAM_RANGES = 0x4720;
-var OBJ_HIDDEN = 0x4010;
-var OBJ_VIS_LOFTER = 0x4011;
-var OBJ_DOESNT_CAST = 0x4012;
-var OBJ_DONT_RECVSHADOW = 0x4017;
-var OBJ_MATTE = 0x4013;
-var OBJ_FAST = 0x4014;
-var OBJ_PROCEDURAL = 0x4015;
-var OBJ_FROZEN = 0x4016;
-var N_TRI_OBJECT = 0x4100;
-var POINT_ARRAY = 0x4110;
-var POINT_FLAG_ARRAY = 0x4111;
-var FACE_ARRAY = 0x4120;
-var MSH_MAT_GROUP = 0x4130;
-var SMOOTH_GROUP = 0x4150;
-var MSH_BOXMAP = 0x4190;
-var TEX_VERTS = 0x4140;
-var MESH_MATRIX = 0x4160;
-var MESH_COLOR = 0x4165;
-var MESH_TEXTURE_INFO = 0x4170;
-var KFDATA = 0xB000;
-var KFHDR = 0xB00A;
-var KFSEG = 0xB008;
-var KFCURTIME = 0xB009;
-var AMBIENT_NODE_TAG = 0xB001;
-var OBJECT_NODE_TAG = 0xB002;
-var CAMERA_NODE_TAG = 0xB003;
-var TARGET_NODE_TAG = 0xB004;
-var LIGHT_NODE_TAG = 0xB005;
-var L_TARGET_NODE_TAG = 0xB006;
-var SPOTLIGHT_NODE_TAG = 0xB007;
-var NODE_ID = 0xB030;
-var NODE_HDR = 0xB010;
-var PIVOT = 0xB013;
-var INSTANCE_NAME = 0xB011;
-var MORPH_SMOOTH = 0xB015;
-var BOUNDBOX = 0xB014;
-var POS_TRACK_TAG = 0xB020;
-var COL_TRACK_TAG = 0xB025;
-var ROT_TRACK_TAG = 0xB021;
-var SCL_TRACK_TAG = 0xB022;
-var MORPH_TRACK_TAG = 0xB026;
-var FOV_TRACK_TAG = 0xB023;
-var ROLL_TRACK_TAG = 0xB024;
-var HOT_TRACK_TAG = 0xB027;
-var FALL_TRACK_TAG = 0xB028;
-var HIDE_TRACK_TAG = 0xB029;
-var POLY_2D = 0x5000;
-var SHAPE_OK = 0x5010;
-var SHAPE_NOT_OK = 0x5011;
-var SHAPE_HOOK = 0x5020;
-var PATH_3D = 0x6000;
-var PATH_MATRIX = 0x6005;
-var SHAPE_2D = 0x6010;
-var M_SCALE = 0x6020;
-var M_TWIST = 0x6030;
-var M_TEETER = 0x6040;
-var M_FIT = 0x6050;
-var M_BEVEL = 0x6060;
-var XZ_CURVE = 0x6070;
-var YZ_CURVE = 0x6080;
-var INTERPCT = 0x6090;
-var DEFORM_LIMIT = 0x60A0;
-var USE_CONTOUR = 0x6100;
-var USE_TWEEN = 0x6110;
-var USE_SCALE = 0x6120;
-var USE_TWIST = 0x6130;
-var USE_TEETER = 0x6140;
-var USE_FIT = 0x6150;
-var USE_BEVEL = 0x6160;
-var DEFAULT_VIEW = 0x3000;
-var VIEW_TOP = 0x3010;
-var VIEW_BOTTOM = 0x3020;
-var VIEW_LEFT = 0x3030;
-var VIEW_RIGHT = 0x3040;
-var VIEW_FRONT = 0x3050;
-var VIEW_BACK = 0x3060;
-var VIEW_USER = 0x3070;
-var VIEW_CAMERA = 0x3080;
-var VIEW_WINDOW = 0x3090;
-var VIEWPORT_LAYOUT_OLD = 0x7000;
-var VIEWPORT_DATA_OLD = 0x7010;
-var VIEWPORT_LAYOUT = 0x7001;
-var VIEWPORT_DATA = 0x7011;
-var VIEWPORT_DATA_3 = 0x7012;
-var VIEWPORT_SIZE = 0x7020;
-var NETWORK_VIEW = 0x7030;
+/*
+ * Autodesk 3DS threee.js file loader, based on lib3ds.
+ *
+ * Loads geometry with uv and materials basic properties with texture support.
+ *
+ * @author @tentone
+ * @author @timknip
+ * @class TDSLoader
+ * @constructor
+ */
+
+'use strict';
+
+THREE.TDSLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+	this.debug = false;
+
+	this.group = null;
+	this.position = 0;
+
+	this.materials = [];
+	this.meshes = [];
+
+};
+
+THREE.TDSLoader.prototype = {
+
+	constructor: THREE.TDSLoader,
+
+	/**
+	 * Load 3ds file from url.
+	 *
+	 * @method load
+	 * @param {[type]} url URL for the file.
+	 * @param {Function} onLoad onLoad callback, receives group Object3D as argument.
+	 * @param {Function} onProgress onProgress callback.
+	 * @param {Function} onError onError callback.
+	 */
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var path = this.path !== undefined ? this.path : THREE.LoaderUtils.extractUrlBase( url );
+
+		var loader = new THREE.FileLoader( this.manager );
+
+		loader.setResponseType( 'arraybuffer' );
+
+		loader.load( url, function ( data ) {
+
+			onLoad( scope.parse( data, path ) );
+
+		}, onProgress, onError );
+
+	},
+
+	/**
+	 * Parse arraybuffer data and load 3ds file.
+	 *
+	 * @method parse
+	 * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded.
+	 * @param {String} path Path for external resources.
+	 * @return {Object3D} Group loaded from 3ds file.
+	 */
+	parse: function ( arraybuffer, path ) {
+
+		this.group = new THREE.Group();
+		this.position = 0;
+		this.materials = [];
+		this.meshes = [];
+
+		this.readFile( arraybuffer, path );
+
+		for ( var i = 0; i < this.meshes.length; i ++ ) {
+
+			this.group.add( this.meshes[ i ] );
+
+		}
+
+		return this.group;
+
+	},
+
+	/**
+	 * Decode file content to read 3ds data.
+	 *
+	 * @method readFile
+	 * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded.
+	 */
+	readFile: function ( arraybuffer, path ) {
+
+		var data = new DataView( arraybuffer );
+		var chunk = this.readChunk( data );
+
+		if ( chunk.id === MLIBMAGIC || chunk.id === CMAGIC || chunk.id === M3DMAGIC ) {
+
+			var next = this.nextChunk( data, chunk );
+
+			while ( next !== 0 ) {
+
+				if ( next === M3D_VERSION ) {
+
+					var version = this.readDWord( data );
+					this.debugMessage( '3DS file version: ' + version );
+
+				} else if ( next === MDATA ) {
+
+					this.resetPosition( data );
+					this.readMeshData( data, path );
+
+				} else {
+
+					this.debugMessage( 'Unknown main chunk: ' + next.toString( 16 ) );
+
+				}
+
+				next = this.nextChunk( data, chunk );
+
+			}
+
+		}
+
+		this.debugMessage( 'Parsed ' + this.meshes.length + ' meshes' );
+
+	},
+
+	/**
+	 * Read mesh data chunk.
+	 *
+	 * @method readMeshData
+	 * @param {Dataview} data Dataview in use.
+	 */
+	readMeshData: function ( data, path ) {
+
+		var chunk = this.readChunk( data );
+		var next = this.nextChunk( data, chunk );
+
+		while ( next !== 0 ) {
+
+			if ( next === MESH_VERSION ) {
+
+				var version = + this.readDWord( data );
+				this.debugMessage( 'Mesh Version: ' + version );
+
+			} else if ( next === MASTER_SCALE ) {
+
+				var scale = this.readFloat( data );
+				this.debugMessage( 'Master scale: ' + scale );
+				this.group.scale.set( scale, scale, scale );
+
+			} else if ( next === NAMED_OBJECT ) {
+
+				this.debugMessage( 'Named Object' );
+				this.resetPosition( data );
+				this.readNamedObject( data );
+
+			} else if ( next === MAT_ENTRY ) {
+
+				this.debugMessage( 'Material' );
+				this.resetPosition( data );
+				this.readMaterialEntry( data, path );
+
+			} else {
+
+				this.debugMessage( 'Unknown MDATA chunk: ' + next.toString( 16 ) );
+
+			}
+
+			next = this.nextChunk( data, chunk );
+
+		}
+
+	},
+
+	/**
+	 * Read named object chunk.
+	 *
+	 * @method readNamedObject
+	 * @param {Dataview} data Dataview in use.
+	 */
+	readNamedObject: function ( data ) {
+
+		var chunk = this.readChunk( data );
+		var name = this.readString( data, 64 );
+		chunk.cur = this.position;
+
+		var next = this.nextChunk( data, chunk );
+		while ( next !== 0 ) {
+
+			if ( next === N_TRI_OBJECT ) {
+
+				this.resetPosition( data );
+				var mesh = this.readMesh( data );
+				mesh.name = name;
+				this.meshes.push( mesh );
+
+			} else {
+
+				this.debugMessage( 'Unknown named object chunk: ' + next.toString( 16 ) );
+
+			}
+
+			next = this.nextChunk( data, chunk );
+
+		}
+
+		this.endChunk( chunk );
+
+	},
+
+	/**
+	 * Read material data chunk and add it to the material list.
+	 *
+	 * @method readMaterialEntry
+	 * @param {Dataview} data Dataview in use.
+	 */
+	readMaterialEntry: function ( data, path ) {
+
+		var chunk = this.readChunk( data );
+		var next = this.nextChunk( data, chunk );
+		var material = new THREE.MeshPhongMaterial();
+
+		while ( next !== 0 ) {
+
+			if ( next === MAT_NAME ) {
+
+				material.name = this.readString( data, 64 );
+				this.debugMessage( '   Name: ' + material.name );
+
+			} else if ( next === MAT_WIRE ) {
+
+				this.debugMessage( '   Wireframe' );
+				material.wireframe = true;
+
+			} else if ( next === MAT_WIRE_SIZE ) {
+
+				var value = this.readByte( data );
+				material.wireframeLinewidth = value;
+				this.debugMessage( '   Wireframe Thickness: ' + value );
+
+			} else if ( next === MAT_TWO_SIDE ) {
+
+				material.side = THREE.DoubleSide;
+				this.debugMessage( '   DoubleSided' );
+
+			} else if ( next === MAT_ADDITIVE ) {
+
+				this.debugMessage( '   Additive Blending' );
+				material.blending = THREE.AdditiveBlending;
+
+			} else if ( next === MAT_DIFFUSE ) {
+
+				this.debugMessage( '   Diffuse Color' );
+				material.color = this.readColor( data );
+
+			} else if ( next === MAT_SPECULAR ) {
+
+				this.debugMessage( '   Specular Color' );
+				material.specular = this.readColor( data );
+
+			} else if ( next === MAT_AMBIENT ) {
+
+				this.debugMessage( '   Ambient color' );
+				material.color = this.readColor( data );
+
+			} else if ( next === MAT_SHININESS ) {
+
+				var shininess = this.readWord( data );
+				material.shininess = shininess;
+				this.debugMessage( '   Shininess : ' + shininess );
+
+			} else if ( next === MAT_TEXMAP ) {
+
+				this.debugMessage( '   ColorMap' );
+				this.resetPosition( data );
+				material.map = this.readMap( data, path );
+
+			} else if ( next === MAT_BUMPMAP ) {
+
+				this.debugMessage( '   BumpMap' );
+				this.resetPosition( data );
+				material.bumpMap = this.readMap( data, path );
+
+			} else if ( next === MAT_OPACMAP ) {
+
+				this.debugMessage( '   OpacityMap' );
+				this.resetPosition( data );
+				material.alphaMap = this.readMap( data, path );
+
+			} else if ( next === MAT_SPECMAP ) {
+
+				this.debugMessage( '   SpecularMap' );
+				this.resetPosition( data );
+				material.specularMap = this.readMap( data, path );
+
+			} else {
+
+				this.debugMessage( '   Unknown material chunk: ' + next.toString( 16 ) );
+
+			}
+
+			next = this.nextChunk( data, chunk );
+
+		}
+
+		this.endChunk( chunk );
+
+		this.materials[ material.name ] = material;
+
+	},
+
+	/**
+	 * Read mesh data chunk.
+	 *
+	 * @method readMesh
+	 * @param {Dataview} data Dataview in use.
+	 */
+	readMesh: function ( data ) {
+
+		var chunk = this.readChunk( data );
+		var next = this.nextChunk( data, chunk );
+
+		var useBufferGeometry = false;
+		var geometry = null;
+		var uvs = [];
+
+		if ( useBufferGeometry ) {
+
+			geometry = new THREE.BufferGeometry();
+
+		}	else {
+
+			geometry = new THREE.Geometry();
+
+		}
+
+		var material = new THREE.MeshPhongMaterial();
+		var mesh = new THREE.Mesh( geometry, material );
+		mesh.name = 'mesh';
+
+		while ( next !== 0 ) {
+
+			if ( next === POINT_ARRAY ) {
+
+				var points = this.readWord( data );
+
+				this.debugMessage( '   Vertex: ' + points );
+
+				//BufferGeometry
+
+				if ( useBufferGeometry )	{
+
+					var vertices = [];
+					for ( var i = 0; i < points; i ++ )		{
+
+						vertices.push( this.readFloat( data ) );
+						vertices.push( this.readFloat( data ) );
+						vertices.push( this.readFloat( data ) );
+
+					}
+
+					geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
+
+				} else	{ //Geometry
+
+					for ( var i = 0; i < points; i ++ )		{
+
+						geometry.vertices.push( new THREE.Vector3( this.readFloat( data ), this.readFloat( data ), this.readFloat( data ) ) );
+
+					}
+
+				}
+
+			} else if ( next === FACE_ARRAY ) {
+
+				this.resetPosition( data );
+				this.readFaceArray( data, mesh );
+
+			} else if ( next === TEX_VERTS ) {
+
+				var texels = this.readWord( data );
+
+				this.debugMessage( '   UV: ' + texels );
+
+				//BufferGeometry
+
+				if ( useBufferGeometry )	{
+
+					var uvs = [];
+					for ( var i = 0; i < texels; i ++ )		{
+
+						uvs.push( this.readFloat( data ) );
+						uvs.push( this.readFloat( data ) );
+
+					}
+					geometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( uvs ), 2 ) );
+
+				} else { //Geometry
+
+					uvs = [];
+					for ( var i = 0; i < texels; i ++ )		{
+
+						uvs.push( new THREE.Vector2( this.readFloat( data ), this.readFloat( data ) ) );
+
+					}
+
+				}
+
+			} else if ( next === MESH_MATRIX ) {
+
+				this.debugMessage( '   Tranformation Matrix (TODO)' );
+
+				var values = [];
+				for ( var i = 0; i < 12; i ++ ) {
+
+					values[ i ] = this.readFloat( data );
+
+				}
+
+				var matrix = new THREE.Matrix4();
+
+				//X Line
+				matrix.elements[ 0 ] = values[ 0 ];
+				matrix.elements[ 1 ] = values[ 6 ];
+				matrix.elements[ 2 ] = values[ 3 ];
+				matrix.elements[ 3 ] = values[ 9 ];
+
+				//Y Line
+				matrix.elements[ 4 ] = values[ 2 ];
+				matrix.elements[ 5 ] = values[ 8 ];
+				matrix.elements[ 6 ] = values[ 5 ];
+				matrix.elements[ 7 ] = values[ 11 ];
+
+				//Z Line
+				matrix.elements[ 8 ] = values[ 1 ];
+				matrix.elements[ 9 ] = values[ 7 ];
+				matrix.elements[ 10 ] = values[ 4 ];
+				matrix.elements[ 11 ] = values[ 10 ];
+
+				//W Line
+				matrix.elements[ 12 ] = 0;
+				matrix.elements[ 13 ] = 0;
+				matrix.elements[ 14 ] = 0;
+				matrix.elements[ 15 ] = 1;
+
+				matrix.transpose();
+
+				var inverse = new THREE.Matrix4();
+				inverse.getInverse( matrix, true );
+				geometry.applyMatrix( inverse );
+
+				matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
+
+			} else {
+
+				this.debugMessage( '   Unknown mesh chunk: ' + next.toString( 16 ) );
+
+			}
+
+			next = this.nextChunk( data, chunk );
+
+		}
+
+		this.endChunk( chunk );
+
+		if ( ! useBufferGeometry ) {
+
+			//geometry.faceVertexUvs[0][faceIndex][vertexIndex]
+
+			if ( uvs.length > 0 ) {
+
+				var faceUV = [];
+
+				for ( var i = 0; i < geometry.faces.length; i ++ ) {
+
+					faceUV.push( [ uvs[ geometry.faces[ i ].a ], uvs[ geometry.faces[ i ].b ], uvs[ geometry.faces[ i ].c ] ] );
+
+				}
+
+				geometry.faceVertexUvs[ 0 ] = faceUV;
+
+			}
+
+			geometry.computeVertexNormals();
+
+		}
+
+		return mesh;
+
+	},
+
+	/**
+	 * Read face array data chunk.
+	 *
+	 * @method readFaceArray
+	 * @param {Dataview} data Dataview in use.
+	 * @param {Mesh} mesh Mesh to be filled with the data read.
+	 */
+	readFaceArray: function ( data, mesh ) {
+
+		var chunk = this.readChunk( data );
+		var faces = this.readWord( data );
+
+		this.debugMessage( '   Faces: ' + faces );
+
+		for ( var i = 0; i < faces; ++ i ) {
+
+			mesh.geometry.faces.push( new THREE.Face3( this.readWord( data ), this.readWord( data ), this.readWord( data ) ) );
+
+			var visibility = this.readWord( data );
+
+		}
+
+		//The rest of the FACE_ARRAY chunk is subchunks
+
+		while ( this.position < chunk.end ) {
+
+			var chunk = this.readChunk( data );
+
+			if ( chunk.id === MSH_MAT_GROUP ) {
+
+				this.debugMessage( '      Material Group' );
+
+				this.resetPosition( data );
+
+				var group = this.readMaterialGroup( data );
+
+				var material = this.materials[ group.name ];
+
+				if ( material !== undefined )	{
+
+					mesh.material = material;
+
+					if ( material.name === '' )		{
+
+						material.name = mesh.name;
+
+					}
+
+				}
+
+			} else {
+
+				this.debugMessage( '      Unknown face array chunk: ' + chunk.toString( 16 ) );
+
+			}
+
+			this.endChunk( chunk );
+
+		}
+
+		this.endChunk( chunk );
+
+	},
+
+	/**
+	 * Read texture map data chunk.
+	 *
+	 * @method readMap
+	 * @param {Dataview} data Dataview in use.
+	 * @return {Texture} Texture read from this data chunk.
+	 */
+	readMap: function ( data, path ) {
+
+		var chunk = this.readChunk( data );
+		var next = this.nextChunk( data, chunk );
+		var texture = {};
+
+		var loader = new THREE.TextureLoader( this.manager );
+		loader.setPath( path );
+
+		while ( next !== 0 ) {
+
+			if ( next === MAT_MAPNAME ) {
+
+				var name = this.readString( data, 128 );
+				texture = loader.load( name );
+
+				this.debugMessage( '      File: ' + path + name );
+
+			} else if ( next === MAT_MAP_UOFFSET ) {
+
+				texture.offset.x = this.readFloat( data );
+				this.debugMessage( '      OffsetX: ' + texture.offset.x );
+
+			} else if ( next === MAT_MAP_VOFFSET ) {
+
+				texture.offset.y = this.readFloat( data );
+				this.debugMessage( '      OffsetY: ' + texture.offset.y );
+
+			} else if ( next === MAT_MAP_USCALE ) {
+
+				texture.repeat.x = this.readFloat( data );
+				this.debugMessage( '      RepeatX: ' + texture.repeat.x );
+
+			} else if ( next === MAT_MAP_VSCALE ) {
+
+				texture.repeat.y = this.readFloat( data );
+				this.debugMessage( '      RepeatY: ' + texture.repeat.y );
+
+			} else {
+
+				this.debugMessage( '      Unknown map chunk: ' + next.toString( 16 ) );
+
+			}
+
+			next = this.nextChunk( data, chunk );
+
+		}
+
+		this.endChunk( chunk );
+
+		return texture;
+
+	},
+
+	/**
+	 * Read material group data chunk.
+	 *
+	 * @method readMaterialGroup
+	 * @param {Dataview} data Dataview in use.
+	 * @return {Object} Object with name and index of the object.
+	 */
+	readMaterialGroup: function ( data ) {
+
+		var chunk = this.readChunk( data );
+		var name = this.readString( data, 64 );
+		var numFaces = this.readWord( data );
+
+		this.debugMessage( '         Name: ' + name );
+		this.debugMessage( '         Faces: ' + numFaces );
+
+		var index = [];
+		for ( var i = 0; i < numFaces; ++ i ) {
+
+			index.push( this.readWord( data ) );
+
+		}
+
+		return { name: name, index: index };
+
+	},
+
+	/**
+	 * Read a color value.
+	 *
+	 * @method readColor
+	 * @param {DataView} data Dataview.
+	 * @return {Color} Color value read..
+	 */
+	readColor: function ( data ) {
+
+		var chunk = this.readChunk( data );
+		var color = new THREE.Color();
+
+		if ( chunk.id === COLOR_24 || chunk.id === LIN_COLOR_24 ) {
+
+			var r = this.readByte( data );
+			var g = this.readByte( data );
+			var b = this.readByte( data );
+
+			color.setRGB( r / 255, g / 255, b / 255 );
+
+			this.debugMessage( '      Color: ' + color.r + ', ' + color.g + ', ' + color.b );
+
+		}	else if ( chunk.id === COLOR_F || chunk.id === LIN_COLOR_F ) {
+
+			var r = this.readFloat( data );
+			var g = this.readFloat( data );
+			var b = this.readFloat( data );
+
+			color.setRGB( r, g, b );
+
+			this.debugMessage( '      Color: ' + color.r + ', ' + color.g + ', ' + color.b );
+
+		}	else {
+
+			this.debugMessage( '      Unknown color chunk: ' + chunk.toString( 16 ) );
+
+		}
+
+		this.endChunk( chunk );
+		return color;
+
+	},
+
+	/**
+	 * Read next chunk of data.
+	 *
+	 * @method readChunk
+	 * @param {DataView} data Dataview.
+	 * @return {Object} Chunk of data read.
+	 */
+	readChunk: function ( data ) {
+
+		var chunk = {};
+
+		chunk.cur = this.position;
+		chunk.id = this.readWord( data );
+		chunk.size = this.readDWord( data );
+		chunk.end = chunk.cur + chunk.size;
+		chunk.cur += 6;
+
+		return chunk;
+
+	},
+
+	/**
+	 * Set position to the end of the current chunk of data.
+	 *
+	 * @method endChunk
+	 * @param {Object} chunk Data chunk.
+	 */
+	endChunk: function ( chunk ) {
+
+		this.position = chunk.end;
+
+	},
+
+	/**
+	 * Move to the next data chunk.
+	 *
+	 * @method nextChunk
+	 * @param {DataView} data Dataview.
+	 * @param {Object} chunk Data chunk.
+	 */
+	nextChunk: function ( data, chunk ) {
+
+		if ( chunk.cur >= chunk.end ) {
+
+			return 0;
+
+		}
+
+		this.position = chunk.cur;
+
+		try {
+
+			var next = this.readChunk( data );
+			chunk.cur += next.size;
+			return next.id;
+
+		}	catch ( e ) {
+
+			this.debugMessage( 'Unable to read chunk at ' + this.position );
+			return 0;
+
+		}
+
+	},
+
+	/**
+	 * Reset dataview position.
+	 *
+	 * @method resetPosition
+	 * @param {DataView} data Dataview.
+	 */
+	resetPosition: function () {
+
+		this.position -= 6;
+
+	},
+
+	/**
+	 * Read byte value.
+	 *
+	 * @method readByte
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
+	readByte: function ( data ) {
+
+		var v = data.getUint8( this.position, true );
+		this.position += 1;
+		return v;
+
+	},
+
+	/**
+	 * Read 32 bit float value.
+	 *
+	 * @method readFloat
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
+	readFloat: function ( data ) {
+
+		try {
+
+			var v = data.getFloat32( this.position, true );
+			this.position += 4;
+			return v;
+
+		}	catch ( e ) {
+
+			this.debugMessage( e + ' ' + this.position + ' ' + data.byteLength );
+
+		}
+
+	},
+
+	/**
+	 * Read 32 bit signed integer value.
+	 *
+	 * @method readInt
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
+	readInt: function ( data ) {
+
+		var v = data.getInt32( this.position, true );
+		this.position += 4;
+		return v;
+
+	},
+
+	/**
+	 * Read 16 bit signed integer value.
+	 *
+	 * @method readShort
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
+	readShort: function ( data ) {
+
+		var v = data.getInt16( this.position, true );
+		this.position += 2;
+		return v;
+
+	},
+
+	/**
+	 * Read 64 bit unsigned integer value.
+	 *
+	 * @method readDWord
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
+	readDWord: function ( data ) {
+
+		var v = data.getUint32( this.position, true );
+		this.position += 4;
+		return v;
+
+	},
+
+	/**
+	 * Read 32 bit unsigned integer value.
+	 *
+	 * @method readWord
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
+	readWord: function ( data ) {
+
+		var v = data.getUint16( this.position, true );
+		this.position += 2;
+		return v;
+
+	},
+
+	/**
+	 * Read string value.
+	 *
+	 * @method readString
+	 * @param {DataView} data Dataview to read data from.
+	 * @param {Number} maxLength Max size of the string to be read.
+	 * @return {String} Data read from the dataview.
+	 */
+	readString: function ( data, maxLength ) {
+
+		var s = '';
+
+		for ( var i = 0; i < maxLength; i ++ ) {
+
+			var c = this.readByte( data );
+			if ( ! c ) {
+
+				break;
+
+			}
+
+			s += String.fromCharCode( c );
+
+		}
+
+		return s;
+
+	},
+
+	/**
+	 * Set resource path used to determine the file path to attached resources.
+	 *
+	 * @method setPath
+	 * @param {String} path Path to resources.
+	 * @return Self for chaining.
+	 */
+	setPath: function ( path ) {
+
+		this.path = path;
+
+		return this;
+
+	},
+
+	/**
+	 * Print debug message to the console.
+	 *
+	 * Is controlled by a flag to show or hide debug messages.
+	 *
+	 * @method debugMessage
+	 * @param {Object} message Debug message to print to the console.
+	 */
+	debugMessage: function ( message ) {
+
+		if ( this.debug ) {
+
+			console.log( message );
+
+		}
+
+	}
+};
+
+var NULL_CHUNK = 0x0000;
+var M3DMAGIC = 0x4D4D;
+var SMAGIC = 0x2D2D;
+var LMAGIC = 0x2D3D;
+var MLIBMAGIC = 0x3DAA;
+var MATMAGIC = 0x3DFF;
+var CMAGIC = 0xC23D;
+var M3D_VERSION = 0x0002;
+var M3D_KFVERSION = 0x0005;
+var COLOR_F = 0x0010;
+var COLOR_24 = 0x0011;
+var LIN_COLOR_24 = 0x0012;
+var LIN_COLOR_F = 0x0013;
+var INT_PERCENTAGE = 0x0030;
+var FLOAT_PERCENTAGE = 0x0031;
+var MDATA = 0x3D3D;
+var MESH_VERSION = 0x3D3E;
+var MASTER_SCALE = 0x0100;
+var LO_SHADOW_BIAS = 0x1400;
+var HI_SHADOW_BIAS = 0x1410;
+var SHADOW_MAP_SIZE = 0x1420;
+var SHADOW_SAMPLES = 0x1430;
+var SHADOW_RANGE = 0x1440;
+var SHADOW_FILTER = 0x1450;
+var RAY_BIAS = 0x1460;
+var O_CONSTS = 0x1500;
+var AMBIENT_LIGHT = 0x2100;
+var BIT_MAP = 0x1100;
+var SOLID_BGND = 0x1200;
+var V_GRADIENT = 0x1300;
+var USE_BIT_MAP = 0x1101;
+var USE_SOLID_BGND = 0x1201;
+var USE_V_GRADIENT = 0x1301;
+var FOG = 0x2200;
+var FOG_BGND = 0x2210;
+var LAYER_FOG = 0x2302;
+var DISTANCE_CUE = 0x2300;
+var DCUE_BGND = 0x2310;
+var USE_FOG = 0x2201;
+var USE_LAYER_FOG = 0x2303;
+var USE_DISTANCE_CUE = 0x2301;
+var MAT_ENTRY = 0xAFFF;
+var MAT_NAME = 0xA000;
+var MAT_AMBIENT = 0xA010;
+var MAT_DIFFUSE = 0xA020;
+var MAT_SPECULAR = 0xA030;
+var MAT_SHININESS = 0xA040;
+var MAT_SHIN2PCT = 0xA041;
+var MAT_TRANSPARENCY = 0xA050;
+var MAT_XPFALL = 0xA052;
+var MAT_USE_XPFALL = 0xA240;
+var MAT_REFBLUR = 0xA053;
+var MAT_SHADING = 0xA100;
+var MAT_USE_REFBLUR = 0xA250;
+var MAT_SELF_ILLUM = 0xA084;
+var MAT_TWO_SIDE = 0xA081;
+var MAT_DECAL = 0xA082;
+var MAT_ADDITIVE = 0xA083;
+var MAT_WIRE = 0xA085;
+var MAT_FACEMAP = 0xA088;
+var MAT_TRANSFALLOFF_IN = 0xA08A;
+var MAT_PHONGSOFT = 0xA08C;
+var MAT_WIREABS = 0xA08E;
+var MAT_WIRE_SIZE = 0xA087;
+var MAT_TEXMAP = 0xA200;
+var MAT_SXP_TEXT_DATA = 0xA320;
+var MAT_TEXMASK = 0xA33E;
+var MAT_SXP_TEXTMASK_DATA = 0xA32A;
+var MAT_TEX2MAP = 0xA33A;
+var MAT_SXP_TEXT2_DATA = 0xA321;
+var MAT_TEX2MASK = 0xA340;
+var MAT_SXP_TEXT2MASK_DATA = 0xA32C;
+var MAT_OPACMAP = 0xA210;
+var MAT_SXP_OPAC_DATA = 0xA322;
+var MAT_OPACMASK = 0xA342;
+var MAT_SXP_OPACMASK_DATA = 0xA32E;
+var MAT_BUMPMAP = 0xA230;
+var MAT_SXP_BUMP_DATA = 0xA324;
+var MAT_BUMPMASK = 0xA344;
+var MAT_SXP_BUMPMASK_DATA = 0xA330;
+var MAT_SPECMAP = 0xA204;
+var MAT_SXP_SPEC_DATA = 0xA325;
+var MAT_SPECMASK = 0xA348;
+var MAT_SXP_SPECMASK_DATA = 0xA332;
+var MAT_SHINMAP = 0xA33C;
+var MAT_SXP_SHIN_DATA = 0xA326;
+var MAT_SHINMASK = 0xA346;
+var MAT_SXP_SHINMASK_DATA = 0xA334;
+var MAT_SELFIMAP = 0xA33D;
+var MAT_SXP_SELFI_DATA = 0xA328;
+var MAT_SELFIMASK = 0xA34A;
+var MAT_SXP_SELFIMASK_DATA = 0xA336;
+var MAT_REFLMAP = 0xA220;
+var MAT_REFLMASK = 0xA34C;
+var MAT_SXP_REFLMASK_DATA = 0xA338;
+var MAT_ACUBIC = 0xA310;
+var MAT_MAPNAME = 0xA300;
+var MAT_MAP_TILING = 0xA351;
+var MAT_MAP_TEXBLUR = 0xA353;
+var MAT_MAP_USCALE = 0xA354;
+var MAT_MAP_VSCALE = 0xA356;
+var MAT_MAP_UOFFSET = 0xA358;
+var MAT_MAP_VOFFSET = 0xA35A;
+var MAT_MAP_ANG = 0xA35C;
+var MAT_MAP_COL1 = 0xA360;
+var MAT_MAP_COL2 = 0xA362;
+var MAT_MAP_RCOL = 0xA364;
+var MAT_MAP_GCOL = 0xA366;
+var MAT_MAP_BCOL = 0xA368;
+var NAMED_OBJECT = 0x4000;
+var N_DIRECT_LIGHT = 0x4600;
+var DL_OFF = 0x4620;
+var DL_OUTER_RANGE = 0x465A;
+var DL_INNER_RANGE = 0x4659;
+var DL_MULTIPLIER = 0x465B;
+var DL_EXCLUDE = 0x4654;
+var DL_ATTENUATE = 0x4625;
+var DL_SPOTLIGHT = 0x4610;
+var DL_SPOT_ROLL = 0x4656;
+var DL_SHADOWED = 0x4630;
+var DL_LOCAL_SHADOW2 = 0x4641;
+var DL_SEE_CONE = 0x4650;
+var DL_SPOT_RECTANGULAR = 0x4651;
+var DL_SPOT_ASPECT = 0x4657;
+var DL_SPOT_PROJECTOR = 0x4653;
+var DL_SPOT_OVERSHOOT = 0x4652;
+var DL_RAY_BIAS = 0x4658;
+var DL_RAYSHAD = 0x4627;
+var N_CAMERA = 0x4700;
+var CAM_SEE_CONE = 0x4710;
+var CAM_RANGES = 0x4720;
+var OBJ_HIDDEN = 0x4010;
+var OBJ_VIS_LOFTER = 0x4011;
+var OBJ_DOESNT_CAST = 0x4012;
+var OBJ_DONT_RECVSHADOW = 0x4017;
+var OBJ_MATTE = 0x4013;
+var OBJ_FAST = 0x4014;
+var OBJ_PROCEDURAL = 0x4015;
+var OBJ_FROZEN = 0x4016;
+var N_TRI_OBJECT = 0x4100;
+var POINT_ARRAY = 0x4110;
+var POINT_FLAG_ARRAY = 0x4111;
+var FACE_ARRAY = 0x4120;
+var MSH_MAT_GROUP = 0x4130;
+var SMOOTH_GROUP = 0x4150;
+var MSH_BOXMAP = 0x4190;
+var TEX_VERTS = 0x4140;
+var MESH_MATRIX = 0x4160;
+var MESH_COLOR = 0x4165;
+var MESH_TEXTURE_INFO = 0x4170;
+var KFDATA = 0xB000;
+var KFHDR = 0xB00A;
+var KFSEG = 0xB008;
+var KFCURTIME = 0xB009;
+var AMBIENT_NODE_TAG = 0xB001;
+var OBJECT_NODE_TAG = 0xB002;
+var CAMERA_NODE_TAG = 0xB003;
+var TARGET_NODE_TAG = 0xB004;
+var LIGHT_NODE_TAG = 0xB005;
+var L_TARGET_NODE_TAG = 0xB006;
+var SPOTLIGHT_NODE_TAG = 0xB007;
+var NODE_ID = 0xB030;
+var NODE_HDR = 0xB010;
+var PIVOT = 0xB013;
+var INSTANCE_NAME = 0xB011;
+var MORPH_SMOOTH = 0xB015;
+var BOUNDBOX = 0xB014;
+var POS_TRACK_TAG = 0xB020;
+var COL_TRACK_TAG = 0xB025;
+var ROT_TRACK_TAG = 0xB021;
+var SCL_TRACK_TAG = 0xB022;
+var MORPH_TRACK_TAG = 0xB026;
+var FOV_TRACK_TAG = 0xB023;
+var ROLL_TRACK_TAG = 0xB024;
+var HOT_TRACK_TAG = 0xB027;
+var FALL_TRACK_TAG = 0xB028;
+var HIDE_TRACK_TAG = 0xB029;
+var POLY_2D = 0x5000;
+var SHAPE_OK = 0x5010;
+var SHAPE_NOT_OK = 0x5011;
+var SHAPE_HOOK = 0x5020;
+var PATH_3D = 0x6000;
+var PATH_MATRIX = 0x6005;
+var SHAPE_2D = 0x6010;
+var M_SCALE = 0x6020;
+var M_TWIST = 0x6030;
+var M_TEETER = 0x6040;
+var M_FIT = 0x6050;
+var M_BEVEL = 0x6060;
+var XZ_CURVE = 0x6070;
+var YZ_CURVE = 0x6080;
+var INTERPCT = 0x6090;
+var DEFORM_LIMIT = 0x60A0;
+var USE_CONTOUR = 0x6100;
+var USE_TWEEN = 0x6110;
+var USE_SCALE = 0x6120;
+var USE_TWIST = 0x6130;
+var USE_TEETER = 0x6140;
+var USE_FIT = 0x6150;
+var USE_BEVEL = 0x6160;
+var DEFAULT_VIEW = 0x3000;
+var VIEW_TOP = 0x3010;
+var VIEW_BOTTOM = 0x3020;
+var VIEW_LEFT = 0x3030;
+var VIEW_RIGHT = 0x3040;
+var VIEW_FRONT = 0x3050;
+var VIEW_BACK = 0x3060;
+var VIEW_USER = 0x3070;
+var VIEW_CAMERA = 0x3080;
+var VIEW_WINDOW = 0x3090;
+var VIEWPORT_LAYOUT_OLD = 0x7000;
+var VIEWPORT_DATA_OLD = 0x7010;
+var VIEWPORT_LAYOUT = 0x7001;
+var VIEWPORT_DATA = 0x7011;
+var VIEWPORT_DATA_3 = 0x7012;
+var VIEWPORT_SIZE = 0x7020;
+var NETWORK_VIEW = 0x7030;

+ 3309 - 3309
examples/js/loaders/sea3d/SEA3D.js

@@ -1,3309 +1,3309 @@
-/**
- * 	SEA3D SDK
- * 	@author Sunag / http://www.sunag.com.br/
- */
-
-'use strict';
-
-var SEA3D = { VERSION: 18110 };
-
-SEA3D.getVersion = function () {
-
-	// Max = 16777215 - VV.S.S.BB  | V = Version | S = Subversion | B = Buildversion
-	var v = SEA3D.VERSION.toString(), l = v.length;
-	return v.substring( 0, l - 4 ) + "." + v.substring( l - 4, l - 3 ) + "." + v.substring( l - 3, l - 2 ) + "." + parseFloat( v.substring( l - 2, l ) ).toString();
-
-};
-
-console.log( 'SEA3D ' + SEA3D.getVersion() );
-
-//
-//	STREAM : STANDARD DATA-IO ( LITTLE-ENDIAN )
-//
-
-SEA3D.Stream = function ( buffer ) {
-
-	this.position = 0;
-	this.buffer = buffer || new ArrayBuffer();
-
-};
-
-SEA3D.Stream.NONE = 0;
-
-// 1D = 0 at 31
-SEA3D.Stream.BOOLEAN = 1;
-
-SEA3D.Stream.BYTE = 2;
-SEA3D.Stream.UBYTE = 3;
-
-SEA3D.Stream.SHORT = 4;
-SEA3D.Stream.USHORT = 5;
-
-SEA3D.Stream.INT24 = 6;
-SEA3D.Stream.UINT24 = 7;
-
-SEA3D.Stream.INT = 8;
-SEA3D.Stream.UINT = 9;
-
-SEA3D.Stream.FLOAT = 10;
-SEA3D.Stream.DOUBLE = 11;
-SEA3D.Stream.DECIMAL = 12;
-
-// 2D = 32 at 63
-
-// 3D = 64 at 95
-SEA3D.Stream.VECTOR3D = 74;
-
-// 4D = 96 at 127
-SEA3D.Stream.VECTOR4D = 106;
-
-// Undefined Size = 128 at 255
-SEA3D.Stream.STRING_TINY = 128;
-SEA3D.Stream.STRING_SHORT = 129;
-SEA3D.Stream.STRING_LONG = 130;
-
-SEA3D.Stream.ASSET = 200;
-SEA3D.Stream.GROUP = 255;
-
-SEA3D.Stream.BLEND_MODE = [
-	"normal", "add", "subtract", "multiply", "dividing", "mix", "alpha", "screen", "darken",
-	"overlay", "colorburn", "linearburn", "lighten", "colordodge", "lineardodge",
-	"softlight", "hardlight", "pinlight", "spotlight", "spotlightblend", "hardmix",
-	"average", "difference", "exclusion", "hue", "saturation", "color", "value",
-	"linearlight", "grainextract", "reflect", "glow", "darkercolor", "lightercolor", "phoenix", "negation"
-];
-
-SEA3D.Stream.INTERPOLATION_TABLE =	[
-	"normal", "linear",
-	"sine.in", "sine.out", "sine.inout",
-	"cubic.in", "cubic.out", "cubic.inout",
-	"quint.in", "quint.out", "quint.inout",
-	"circ.in", "circ.out", "circ.inout",
-	"back.in", "back.out", "back.inout",
-	"quad.in", "quad.out", "quad.inout",
-	"quart.in", "quart.out", "quart.inout",
-	"expo.in", "expo.out", "expo.inout",
-	"elastic.in", "elastic.out", "elastic.inout",
-	"bounce.in", "bounce.out", "bounce.inout"
-];
-
-SEA3D.Stream.sizeOf = function ( kind ) {
-
-	if ( kind == 0 ) return 0;
-	else if ( kind >= 1 && kind <= 31 ) return 1;
-	else if ( kind >= 32 && kind <= 63 ) return 2;
-	else if ( kind >= 64 && kind <= 95 ) return 3;
-	else if ( kind >= 96 && kind <= 125 ) return 4;
-	return - 1;
-
-};
-
-SEA3D.Stream.prototype = {
-
-	constructor: SEA3D.Stream,
-
-	set buffer( val ) {
-
-		this.buf = val;
-		this.data = new DataView( val );
-
-	},
-
-	get buffer() {
-
-		return this.buf;
-
-	},
-
-	get bytesAvailable() {
-
-		return this.length - this.position;
-
-	},
-
-	get length() {
-
-		return this.buf.byteLength;
-
-	}
-
-};
-
-SEA3D.Stream.prototype.getByte = function ( pos ) {
-
-	return this.data.getInt8( pos );
-
-};
-
-SEA3D.Stream.prototype.readBytes = function ( len ) {
-
-	var buf = this.buf.slice( this.position, this.position + len );
-	this.position += len;
-	return buf;
-
-};
-
-SEA3D.Stream.prototype.readByte = function () {
-
-	return this.data.getInt8( this.position ++ );
-
-};
-
-SEA3D.Stream.prototype.readUByte = function () {
-
-	return this.data.getUint8( this.position ++ );
-
-};
-
-SEA3D.Stream.prototype.readBool = function () {
-
-	return this.data.getInt8( this.position ++ ) != 0;
-
-};
-
-SEA3D.Stream.prototype.readShort = function () {
-
-	var v = this.data.getInt16( this.position, true );
-	this.position += 2;
-	return v;
-
-};
-
-SEA3D.Stream.prototype.readUShort = function () {
-
-	var v = this.data.getUint16( this.position, true );
-	this.position += 2;
-	return v;
-
-};
-
-SEA3D.Stream.prototype.readUInt24 = function () {
-
-	var v = this.data.getUint32( this.position, true ) & 0xFFFFFF;
-	this.position += 3;
-	return v;
-
-};
-
-SEA3D.Stream.prototype.readUInt24F = function () {
-
-	return this.readUShort() | ( this.readUByte() << 16 );
-
-};
-
-SEA3D.Stream.prototype.readInt = function () {
-
-	var v = this.data.getInt32( this.position, true );
-	this.position += 4;
-	return v;
-
-};
-
-SEA3D.Stream.prototype.readUInt = function () {
-
-	var v = this.data.getUint32( this.position, true );
-	this.position += 4;
-	return v;
-
-};
-
-SEA3D.Stream.prototype.readFloat = function () {
-
-	var v = this.data.getFloat32( this.position, true );
-	this.position += 4;
-	return v;
-
-};
-
-SEA3D.Stream.prototype.readUInteger = function () {
-
-	var v = this.readUByte(),
-		r = v & 0x7F;
-
-	if ( ( v & 0x80 ) != 0 ) {
-
-		v = this.readUByte();
-		r |= ( v & 0x7F ) << 7;
-
-		if ( ( v & 0x80 ) != 0 ) {
-
-			v = this.readUByte();
-			r |= ( v & 0x7F ) << 13;
-
-		}
-
-	}
-
-	return r;
-
-};
-
-SEA3D.Stream.prototype.readVector2 = function () {
-
-	return { x: this.readFloat(), y: this.readFloat() };
-
-};
-
-SEA3D.Stream.prototype.readVector3 = function () {
-
-	return { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() };
-
-};
-
-SEA3D.Stream.prototype.readVector4 = function () {
-
-	return { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), w: this.readFloat() };
-
-};
-
-SEA3D.Stream.prototype.readMatrix = function () {
-
-	var mtx = new Float32Array( 16 );
-
-	mtx[ 0 ] = this.readFloat();
-	mtx[ 1 ] = this.readFloat();
-	mtx[ 2 ] = this.readFloat();
-	mtx[ 3 ] = 0.0;
-	mtx[ 4 ] = this.readFloat();
-	mtx[ 5 ] = this.readFloat();
-	mtx[ 6 ] = this.readFloat();
-	mtx[ 7 ] = 0.0;
-	mtx[ 8 ] = this.readFloat();
-	mtx[ 9 ] = this.readFloat();
-	mtx[ 10 ] = this.readFloat();
-	mtx[ 11 ] = 0.0;
-	mtx[ 12 ] = this.readFloat();
-	mtx[ 13 ] = this.readFloat();
-	mtx[ 14 ] = this.readFloat();
-	mtx[ 15 ] = 1.0;
-
-	return mtx;
-
-};
-
-SEA3D.Stream.prototype.readUTF8 = function ( len ) {
-
-	var buffer = this.readBytes( len );
-
-	return THREE.LoaderUtils.decodeText( new Uint8Array( buffer ) );
-
-};
-
-SEA3D.Stream.prototype.readExt = function () {
-
-	return this.readUTF8( 4 ).replace( /\0/g, "" );
-
-};
-
-SEA3D.Stream.prototype.readUTF8Tiny = function () {
-
-	return this.readUTF8( this.readUByte() );
-
-};
-
-SEA3D.Stream.prototype.readUTF8Short = function () {
-
-	return this.readUTF8( this.readUShort() );
-
-};
-
-SEA3D.Stream.prototype.readUTF8Long = function () {
-
-	return this.readUTF8( this.readUInt() );
-
-};
-
-SEA3D.Stream.prototype.readUByteArray = function ( length ) {
-
-	var v = new Uint8Array( length );
-
-	SEA3D.Stream.memcpy(
-		v.buffer,
-		0,
-		this.buffer,
-		this.position,
-		length
-	);
-
-	this.position += length;
-
-	return v;
-
-};
-
-SEA3D.Stream.prototype.readUShortArray = function ( length ) {
-
-	var v = new Uint16Array( length ),
-		len = length * 2;
-
-	SEA3D.Stream.memcpy(
-		v.buffer,
-		0,
-		this.buffer,
-		this.position,
-		len
-	);
-
-	this.position += len;
-
-	return v;
-
-};
-
-
-SEA3D.Stream.prototype.readUInt24Array = function ( length ) {
-
-	var v = new Uint32Array( length );
-
-	for ( var i = 0; i < length; i ++ ) {
-
-		v[ i ] = this.readUInt24();
-
-	}
-
-	return v;
-
-};
-
-
-SEA3D.Stream.prototype.readUIntArray = function ( length ) {
-
-	var v = new Uint32Array( length ),
-		len = length * 4;
-
-	SEA3D.Stream.memcpy(
-		v.buffer,
-		0,
-		this.buffer,
-		this.position,
-		len
-	);
-
-	this.position += len;
-
-	return v;
-
-};
-
-SEA3D.Stream.prototype.readFloatArray = function ( length ) {
-
-	var v = new Float32Array( length ),
-		len = length * 4;
-
-	SEA3D.Stream.memcpy(
-		v.buffer,
-		0,
-		this.buffer,
-		this.position,
-		len
-	);
-
-	this.position += len;
-
-	return v;
-
-};
-
-
-SEA3D.Stream.prototype.readBlendMode = function () {
-
-	return SEA3D.Stream.BLEND_MODE[ this.readUByte() ];
-
-};
-
-SEA3D.Stream.prototype.readInterpolation = function () {
-
-	return SEA3D.Stream.INTERPOLATION_TABLE[ this.readUByte() ];
-
-};
-
-SEA3D.Stream.prototype.readTags = function ( callback ) {
-
-	var numTag = this.readUByte();
-
-	for ( var i = 0; i < numTag; ++ i ) {
-
-		var kind = this.readUShort();
-		var size = this.readUInt();
-		var pos = this.position;
-
-		callback( kind, this, size );
-
-		this.position = pos += size;
-
-	}
-
-};
-
-SEA3D.Stream.prototype.readProperties = function ( sea3d ) {
-
-	var count = this.readUByte(),
-		props = {}, types = {};
-
-	props.__type = types;
-
-	for ( var i = 0; i < count; i ++ ) {
-
-		var name = this.readUTF8Tiny(),
-			type = this.readUByte();
-
-		types[ name ] = type;
-		props[ name ] = type == SEA3D.Stream.GROUP ? this.readProperties( sea3d ) : this.readToken( type, sea3d );
-
-	}
-
-	return props;
-
-};
-
-SEA3D.Stream.prototype.readAnimationList = function ( sea3d ) {
-
-	var list = [],
-		count = this.readUByte();
-
-	var i = 0;
-	while ( i < count ) {
-
-		var attrib = this.readUByte(),
-			anm = {};
-
-		anm.relative = ( attrib & 1 ) != 0;
-
-		if ( attrib & 2 ) anm.timeScale = this.readFloat();
-
-		anm.tag = sea3d.getObject( this.readUInt() );
-
-		list[ i ++ ] = anm;
-
-	}
-
-	return list;
-
-};
-
-SEA3D.Stream.prototype.readScriptList = function ( sea3d ) {
-
-	var list = [],
-		count = this.readUByte();
-
-	var i = 0;
-	while ( i < count ) {
-
-		var attrib = this.readUByte(),
-			script = {};
-
-		script.priority = ( attrib & 1 ) | ( attrib & 2 );
-
-		if ( attrib & 4 ) {
-
-			var numParams = this.readUByte();
-
-			script.params = {};
-
-			for ( var j = 0; j < numParams; j ++ ) {
-
-				var name = this.readUTF8Tiny();
-
-				script.params[ name ] = this.readObject( sea3d );
-
-			}
-
-		}
-
-		if ( attrib & 8 ) {
-
-			script.method = this.readUTF8Tiny();
-
-		}
-
-		script.tag = sea3d.getObject( this.readUInt() );
-
-		list[ i ++ ] = script;
-
-	}
-
-	return list;
-
-};
-
-SEA3D.Stream.prototype.readObject = function ( sea3d ) {
-
-	return this.readToken( this.readUByte(), sea3d );
-
-};
-
-SEA3D.Stream.prototype.readToken = function ( type, sea3d ) {
-
-	switch ( type )	{
-
-		// 1D
-		case SEA3D.Stream.BOOLEAN:
-			return this.readBool();
-			break;
-
-		case SEA3D.Stream.UBYTE:
-			return this.readUByte();
-			break;
-
-		case SEA3D.Stream.USHORT:
-			return this.readUShort();
-			break;
-
-		case SEA3D.Stream.UINT24:
-			return this.readUInt24();
-			break;
-
-		case SEA3D.Stream.INT:
-			return this.readInt();
-			break;
-
-		case SEA3D.Stream.UINT:
-			return this.readUInt();
-			break;
-
-		case SEA3D.Stream.FLOAT:
-			return this.readFloat();
-			break;
-
-		// 3D
-		case SEA3D.Stream.VECTOR3D:
-			return this.readVector3();
-			break;
-
-		// 4D
-		case SEA3D.Stream.VECTOR4D:
-			return this.readVector4();
-			break;
-
-		// Undefined Values
-		case SEA3D.Stream.STRING_TINY:
-			return this.readUTF8Tiny();
-			break;
-
-		case SEA3D.Stream.STRING_SHORT:
-			return this.readUTF8Short();
-			break;
-
-		case SEA3D.Stream.STRING_LONG:
-			return this.readUTF8Long();
-			break;
-
-		case SEA3D.Stream.ASSET:
-			var asset = this.readUInt();
-			return asset > 0 ? sea3d.getObject( asset - 1 ) : null;
-			break;
-
-		default:
-			console.error( "DataType not found!" );
-
-	}
-
-	return null;
-
-};
-
-SEA3D.Stream.prototype.readVector = function ( type, length, offset ) {
-
-	var size = SEA3D.Stream.sizeOf( type ),
-		i = offset * size,
-		count = i + ( length * size );
-
-	switch ( type )	{
-
-		// 1D
-		case SEA3D.Stream.BOOLEAN:
-
-			return this.readUByteArray( count );
-
-
-		case SEA3D.Stream.UBYTE:
-
-			return this.readUByteArray( count );
-
-
-		case SEA3D.Stream.USHORT:
-
-			return this.readUShortArray( count );
-
-
-		case SEA3D.Stream.UINT24:
-
-			return this.readUInt24Array( count );
-
-
-		case SEA3D.Stream.UINT:
-
-			return this.readUIntArray( count );
-
-
-		case SEA3D.Stream.FLOAT:
-
-			return this.readFloatArray( count );
-
-
-		// 3D
-		case SEA3D.Stream.VECTOR3D:
-
-			return this.readFloatArray( count );
-
-
-		// 4D
-		case SEA3D.Stream.VECTOR4D:
-
-			return this.readFloatArray( count );
-
-	}
-
-};
-
-SEA3D.Stream.prototype.append = function ( data ) {
-
-	var buffer = new ArrayBuffer( this.data.byteLength + data.byteLength );
-
-	SEA3D.Stream.memcpy( buffer, 0, this.data.buffer, 0, this.data.byteLength );
-	SEA3D.Stream.memcpy( buffer, this.data.byteLength, data, 0, data.byteLength );
-
-	this.buffer = buffer;
-
-};
-
-SEA3D.Stream.prototype.concat = function ( position, length ) {
-
-	return new SEA3D.Stream( this.buffer.slice( position, position + length ) );
-
-};
-
-/**
- * @author DataStream.js
- */
-
-SEA3D.Stream.memcpy = function ( dst, dstOffset, src, srcOffset, byteLength ) {
-
-	var dstU8 = new Uint8Array( dst, dstOffset, byteLength );
-	var srcU8 = new Uint8Array( src, srcOffset, byteLength );
-
-	dstU8.set( srcU8 );
-
-};
-
-//
-//	UByteArray
-//
-
-SEA3D.UByteArray = function () {
-
-	this.ubytes = [];
-	this.length = 0;
-
-};
-
-SEA3D.UByteArray.prototype = {
-
-	constructor: SEA3D.UByteArray,
-
-	add: function ( ubytes ) {
-
-		this.ubytes.push( ubytes );
-		this.length += ubytes.byteLength;
-
-	},
-
-	toBuffer: function () {
-
-		var memcpy = new Uint8Array( this.length );
-
-		for ( var i = 0, offset = 0; i < this.ubytes.length; i ++ ) {
-
-			memcpy.set( this.ubytes[ i ], offset );
-			offset += this.ubytes[ i ].byteLength;
-
-		}
-
-		return memcpy.buffer;
-
-	}
-};
-
-//
-//	Math
-//
-
-SEA3D.Math = {
-	RAD_TO_DEG: 180 / Math.PI,
-	DEG_TO_RAD: Math.PI / 180
-};
-
-SEA3D.Math.angle = function ( val ) {
-
-	var ang = 180,
-		inv = val < 0;
-
-	val = ( inv ? - val : val ) % 360;
-
-	if ( val > ang ) {
-
-		val = - ang + ( val - ang );
-
-	}
-
-	return ( inv ? - val : val );
-
-};
-
-SEA3D.Math.angleDiff = function ( a, b ) {
-
-	a *= this.DEG_TO_RAD;
-	b *= this.DEG_TO_RAD;
-
-	return Math.atan2( Math.sin( a - b ), Math.cos( a - b ) ) * this.RAD_TO_DEG;
-
-};
-
-SEA3D.Math.angleArea = function ( angle, target, area ) {
-
-	return Math.abs( this.angleDiff( angle, target ) ) <= area;
-
-};
-
-SEA3D.Math.direction = function ( x1, y1, x2, y2 ) {
-
-	return Math.atan2( y2 - y1, x2 - x1 );
-
-};
-
-SEA3D.Math.physicalLerp = function ( val, to, deltaTime, duration ) {
-
-	var t = deltaTime / duration;
-
-	if ( t > 1 ) t = 1;
-
-	return val + ( ( to - val ) * t );
-
-};
-
-SEA3D.Math.physicalAngle = function ( val, to, deltaTime, duration ) {
-
-	if ( Math.abs( val - to ) > 180 ) {
-
-		if ( val > to ) {
-
-			to += 360;
-
-		} else {
-
-			to -= 360;
-
-		}
-
-	}
-
-	var t = deltaTime / duration;
-
-	if ( t > 1 ) t = 1;
-
-	return this.angle( val + ( ( to - val ) * t ) );
-
-};
-
-SEA3D.Math.zero = function ( value, precision ) {
-
-	precision = precision || 1.0E-3;
-
-	var pValue = value < 0 ? - value : value;
-
-	if ( pValue - precision < 0 ) value = 0;
-
-	return value;
-
-};
-
-SEA3D.Math.round = function ( value, precision ) {
-
-	precision = Math.pow( 10, precision );
-
-	return Math.round( value * precision ) / precision;
-
-};
-
-SEA3D.Math.lerpAngle = function ( val, tar, t ) {
-
-	if ( Math.abs( val - tar ) > 180 ) {
-
-		if ( val > tar ) {
-
-			tar += 360;
-
-		} else {
-
-			tar -= 360;
-
-		}
-
-	}
-
-	val += ( tar - val ) * t;
-
-	return SEA3D.Math.angle( val );
-
-};
-
-SEA3D.Math.lerpColor = function ( val, tar, t ) {
-
-	var a0 = val >> 24 & 0xff,
-		r0 = val >> 16 & 0xff,
-		g0 = val >> 8 & 0xff,
-		b0 = val & 0xff;
-
-	var a1 = tar >> 24 & 0xff,
-		r1 = tar >> 16 & 0xff,
-		g1 = tar >> 8 & 0xff,
-		b1 = tar & 0xff;
-
-	a0 += ( a1 - a0 ) * t;
-	r0 += ( r1 - r0 ) * t;
-	g0 += ( g1 - g0 ) * t;
-	b0 += ( b1 - b0 ) * t;
-
-	return a0 << 24 | r0 << 16 | g0 << 8 | b0;
-
-};
-
-SEA3D.Math.lerp = function ( val, tar, t ) {
-
-	return val + ( ( tar - val ) * t );
-
-};
-
-//
-//	Timer
-//
-
-SEA3D.Timer = function () {
-
-	this.time = this.start = Date.now();
-
-};
-
-SEA3D.Timer.prototype = {
-
-	constructor: SEA3D.Timer,
-
-	get now() {
-
-		return Date.now();
-
-	},
-
-	get deltaTime() {
-
-		return Date.now() - this.time;
-
-	},
-
-	get elapsedTime() {
-
-		return Date.now() - this.start;
-
-	},
-
-	update: function () {
-
-		this.time = Date.now();
-
-	}
-};
-
-//
-//	Object
-//
-
-SEA3D.Object = function ( name, data, type, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.type = type;
-	this.sea3d = sea3d;
-
-};
-
-//
-//	Geometry Base
-//
-
-SEA3D.GeometryBase = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.attrib = data.readUShort();
-
-	this.isBig = ( this.attrib & 1 ) != 0;
-
-	// variable uint
-	data.readVInt = this.isBig ? data.readUInt : data.readUShort;
-
-	this.numVertex = data.readVInt();
-
-	this.length = this.numVertex * 3;
-
-};
-
-//
-//	Geometry
-//
-
-SEA3D.Geometry = function ( name, data, sea3d ) {
-
-	SEA3D.GeometryBase.call( this, name, data, sea3d );
-
-	var i, j, len;
-
-	// NORMAL
-	if ( this.attrib & 4 ) {
-
-		this.normal = data.readFloatArray( this.length );
-
-	}
-
-	// TANGENT
-	if ( this.attrib & 8 ) {
-
-		this.tangent = data.readFloatArray( this.length );
-
-	}
-
-	// UV
-	if ( this.attrib & 32 ) {
-
-		var uvCount = data.readUByte();
-
-		if ( uvCount ) {
-
-			this.uv = [];
-
-			len = this.numVertex * 2;
-
-			i = 0;
-			while ( i < uvCount ) {
-
-				// UV VERTEX DATA
-				this.uv[ i ++ ] = data.readFloatArray( len );
-
-			}
-
-		}
-
-	}
-
-	// JOINT-INDEXES / WEIGHTS
-	if ( this.attrib & 64 ) {
-
-		this.jointPerVertex = data.readUByte();
-
-		var jntLen = this.numVertex * this.jointPerVertex;
-
-		this.joint = data.readUShortArray( jntLen );
-		this.weight = data.readFloatArray( jntLen );
-
-	}
-
-	// VERTEX_COLOR
-	if ( this.attrib & 128 ) {
-
-		var colorAttrib = data.readUByte();
-
-		var colorCount = data.readUByte();
-
-		if ( colorCount ) {
-
-			this.numColor = ( ( ( colorAttrib & 64 ) >> 6 ) | ( ( colorAttrib & 128 ) >> 6 ) ) + 1;
-
-			this.color = [];
-
-			for ( i = 0 & 15; i < colorCount; i ++ ) {
-
-				this.color.push( data.readFloatArray( this.numVertex * this.numColor ) );
-
-			}
-
-		}
-
-	}
-
-	// VERTEX
-	this.vertex = data.readFloatArray( this.length );
-
-	// SUB-MESHES
-	var count = data.readUByte();
-
-	this.groups = [];
-
-	if ( this.attrib & 1024 ) {
-
-		// INDEXES
-		for ( i = 0, len = 0; i < count; i ++ ) {
-
-			j = data.readVInt() * 3;
-
-			this.groups.push( {
-				start: len,
-				count: j
-			} );
-
-			len += j;
-
-		}
-
-		if ( ! ( this.attrib & 2048 ) ) {
-
-			this.indexes = this.isBig ? data.readUIntArray( len ) : data.readUShortArray( len );
-
-		}
-
-	} else {
-
-		// INDEXES
-		var stride = this.isBig ? 4 : 2,
-			bytearray = new SEA3D.UByteArray();
-
-		for ( i = 0, j = 0; i < count; i ++ ) {
-
-			len = data.readVInt() * 3;
-
-			this.groups.push( {
-				start: j,
-				count: len
-			} );
-
-			j += len;
-
-			bytearray.add( data.readUByteArray( len * stride ) );
-
-		}
-
-		this.indexes = this.isBig ? new Uint32Array( bytearray.toBuffer() ) : new Uint16Array( bytearray.toBuffer() );
-
-	}
-
-};
-
-SEA3D.Geometry.prototype = Object.create( SEA3D.GeometryBase.prototype );
-SEA3D.Geometry.prototype.constructor = SEA3D.Geometry;
-
-SEA3D.Geometry.prototype.type = "geo";
-
-//
-//	Object3D
-//
-
-SEA3D.Object3D = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.isStatic = false;
-	this.visible = true;
-
-	this.attrib = data.readUShort();
-
-	if ( this.attrib & 1 ) this.parent = sea3d.getObject( data.readUInt() );
-
-	if ( this.attrib & 2 ) this.animations = data.readAnimationList( sea3d );
-
-	if ( this.attrib & 4 ) this.scripts = data.readScriptList( sea3d );
-
-	if ( this.attrib & 16 ) this.attributes = sea3d.getObject( data.readUInt() );
-
-	if ( this.attrib & 32 ) {
-
-		var objectType = data.readUByte();
-
-		this.isStatic = ( objectType & 1 ) != 0;
-		this.visible = ( objectType & 2 ) == 0;
-
-	}
-
-};
-
-SEA3D.Object3D.prototype.readTag = function ( kind, data, size ) {
-
-};
-
-//
-//	Entity3D
-//
-
-SEA3D.Entity3D = function ( name, data, sea3d ) {
-
-	SEA3D.Object3D.call( this, name, data, sea3d );
-
-	this.castShadows = true;
-
-	if ( this.attrib & 64 ) {
-
-		var lightType = data.readUByte();
-
-		this.castShadows = ( lightType & 1 ) == 0;
-
-	}
-
-};
-
-SEA3D.Entity3D.prototype = Object.create( SEA3D.Object3D.prototype );
-SEA3D.Entity3D.prototype.constructor = SEA3D.Entity3D;
-
-//
-//	Sound3D
-//
-
-SEA3D.Sound3D = function ( name, data, sea3d ) {
-
-	SEA3D.Object3D.call( this, name, data, sea3d );
-
-	this.autoPlay = ( this.attrib & 64 ) != 0;
-
-	if ( this.attrib & 128 ) this.mixer = sea3d.getObject( data.readUInt() );
-
-	this.sound = sea3d.getObject( data.readUInt() );
-	this.volume = data.readFloat();
-
-};
-
-SEA3D.Sound3D.prototype = Object.create( SEA3D.Object3D.prototype );
-SEA3D.Sound3D.prototype.constructor = SEA3D.Sound3D;
-
-//
-//	Sound Point
-//
-
-SEA3D.SoundPoint = function ( name, data, sea3d ) {
-
-	SEA3D.Sound3D.call( this, name, data, sea3d );
-
-	this.position = data.readVector3();
-	this.distance = data.readFloat();
-
-	data.readTags( this.readTag.bind( this ) );
-
-};
-
-SEA3D.SoundPoint.prototype = Object.create( SEA3D.Sound3D.prototype );
-SEA3D.SoundPoint.prototype.constructor = SEA3D.SoundPoint;
-
-SEA3D.SoundPoint.prototype.type = "sp";
-
-//
-//	Container3D
-//
-
-SEA3D.Container3D = function ( name, data, sea3d ) {
-
-	SEA3D.Object3D.call( this, name, data, sea3d );
-
-	this.transform = data.readMatrix();
-
-	data.readTags( this.readTag.bind( this ) );
-
-};
-
-SEA3D.Container3D.prototype = Object.create( SEA3D.Object3D.prototype );
-SEA3D.Container3D.prototype.constructor = SEA3D.Container3D;
-
-SEA3D.Container3D.prototype.type = "c3d";
-
-//
-//	Script URL
-//
-
-SEA3D.ScriptURL = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.url = data.readUTF8( data.length );
-
-};
-
-SEA3D.ScriptURL.prototype.type = "src";
-
-//
-//	Texture URL
-//
-
-SEA3D.TextureURL = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.url = sea3d.config.path + data.readUTF8( data.length );
-
-};
-
-SEA3D.TextureURL.prototype.type = "urlT";
-
-//
-//	CubeMap URL
-//
-
-SEA3D.CubeMapURL = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.faces = [];
-
-	for ( var i = 0; i < 6; i ++ ) {
-
-		this.faces[ i ] = data.readUTF8Tiny();
-
-	}
-
-};
-
-SEA3D.CubeMapURL.prototype.type = "cURL";
-
-//
-//	Actions
-//
-
-SEA3D.Actions = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.count = data.readUInt();
-	this.actions = [];
-
-	for ( var i = 0; i < this.count; i ++ ) {
-
-		var flag = data.readUByte();
-		var kind = data.readUShort();
-
-		var size = data.readUShort();
-
-		var position = data.position;
-		var act = this.actions[ i ] = { kind: kind };
-
-		// range of animation
-		if ( flag & 1 ) {
-
-			// start and count in frames
-			act.range = [ data.readUInt(), data.readUInt() ];
-
-		}
-
-		// time
-		if ( flag & 2 ) {
-
-			act.time = data.readUInt();
-
-		}
-
-		// easing
-		if ( flag & 4 ) {
-
-			act.intrpl = data.readInterpolation();
-
-			if ( act.intrpl.indexOf( 'back.' ) == 0 ) {
-
-				act.intrplParam0 = data.readFloat();
-
-			} else if ( act.intrpl.indexOf( 'elastic.' ) == 0 ) {
-
-				act.intrplParam0 = data.readFloat();
-				act.intrplParam1 = data.readFloat();
-
-			}
-
-		}
-
-		switch ( kind ) {
-
-			case SEA3D.Actions.RTT_TARGET:
-				act.source = sea3d.getObject( data.readUInt() );
-				act.target = sea3d.getObject( data.readUInt() );
-				break;
-
-			case SEA3D.Actions.LOOK_AT:
-				act.source = sea3d.getObject( data.readUInt() );
-				act.target = sea3d.getObject( data.readUInt() );
-				break;
-
-			case SEA3D.Actions.PLAY_SOUND:
-				act.sound = sea3d.getObject( data.readUInt() );
-				act.offset = data.readUInt();
-				break;
-
-			case SEA3D.Actions.PLAY_ANIMATION:
-				act.object = sea3d.getObject( data.readUInt() );
-				act.name = data.readUTF8Tiny();
-				break;
-
-			case SEA3D.Actions.FOG:
-				act.color = data.readUInt24();
-				act.min = data.readFloat();
-				act.max = data.readFloat();
-				break;
-
-			case SEA3D.Actions.ENVIRONMENT:
-				act.texture = sea3d.getObject( data.readUInt() );
-				break;
-
-			case SEA3D.Actions.ENVIRONMENT_COLOR:
-				act.color = data.readUInt24F();
-				break;
-
-			case SEA3D.Actions.CAMERA:
-				act.camera = sea3d.getObject( data.readUInt() );
-				break;
-
-			case SEA3D.Actions.SCRIPTS:
-				act.scripts = data.readScriptList( sea3d );
-				break;
-
-			case SEA3D.Actions.CLASS_OF:
-				act.classof = sea3d.getObject( data.readUInt() );
-				break;
-
-			case SEA3D.Actions.ATTRIBUTES:
-				act.attributes = sea3d.getObject( data.readUInt() );
-				break;
-
-			default:
-				console.log( "Action \"" + kind + "\" not found." );
-				break;
-
-		}
-
-		data.position = position + size;
-
-	}
-
-};
-
-SEA3D.Actions.SCENE = 0;
-SEA3D.Actions.ENVIRONMENT_COLOR = 1;
-SEA3D.Actions.ENVIRONMENT = 2;
-SEA3D.Actions.FOG = 3;
-SEA3D.Actions.PLAY_ANIMATION = 4;
-SEA3D.Actions.PLAY_SOUND = 5;
-SEA3D.Actions.ANIMATION_AUDIO_SYNC = 6;
-SEA3D.Actions.LOOK_AT = 7;
-SEA3D.Actions.RTT_TARGET = 8;
-SEA3D.Actions.CAMERA = 9;
-SEA3D.Actions.SCRIPTS = 10;
-SEA3D.Actions.CLASS_OF = 11;
-SEA3D.Actions.ATTRIBUTES = 12;
-
-SEA3D.Actions.prototype.type = "act";
-
-//
-//	Properties
-//
-
-SEA3D.Properties = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.props = data.readProperties( sea3d );
-	this.props.__name = name;
-
-};
-
-SEA3D.Properties.prototype.type = "prop";
-
-//
-//	File Info
-//
-
-SEA3D.FileInfo = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.info = data.readProperties( sea3d );
-	this.info.__name = name;
-
-	sea3d.info = this.info;
-
-};
-
-SEA3D.FileInfo.prototype.type = "info";
-
-//
-//	Java Script
-//
-
-SEA3D.JavaScript = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.src = data.readUTF8( data.length );
-
-};
-
-SEA3D.JavaScript.prototype.type = "js";
-
-//
-//	Java Script Method
-//
-
-SEA3D.JavaScriptMethod = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	var count = data.readUShort();
-
-	this.methods = {};
-
-	for ( var i = 0; i < count; i ++ ) {
-
-		var flag = data.readUByte();
-		var method = data.readUTF8Tiny();
-
-		this.methods[ method ] = {
-			src: data.readUTF8Long()
-		};
-
-	}
-
-};
-
-SEA3D.JavaScriptMethod.prototype.type = "jsm";
-
-//
-//	GLSL
-//
-
-SEA3D.GLSL = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.src = data.readUTF8( data.length );
-
-};
-
-SEA3D.GLSL.prototype.type = "glsl";
-
-//
-//	Dummy
-//
-
-SEA3D.Dummy = function ( name, data, sea3d ) {
-
-	SEA3D.Object3D.call( this, name, data, sea3d );
-
-	this.transform = data.readMatrix();
-
-	this.width = data.readFloat();
-	this.height = data.readFloat();
-	this.depth = data.readFloat();
-
-	data.readTags( this.readTag.bind( this ) );
-
-};
-
-SEA3D.Dummy.prototype = Object.create( SEA3D.Object3D.prototype );
-SEA3D.Dummy.prototype.constructor = SEA3D.Dummy;
-
-SEA3D.Dummy.prototype.type = "dmy";
-
-//
-//	Line
-//
-
-SEA3D.Line = function ( name, data, sea3d ) {
-
-	SEA3D.Object3D.call( this, name, data, sea3d );
-
-	this.count = ( this.attrib & 64 ? data.readUInt() : data.readUShort() ) * 3;
-	this.closed = ( this.attrib & 128 ) != 0;
-	this.transform = data.readMatrix();
-
-	this.vertex = [];
-
-	var i = 0;
-	while ( i < this.count ) {
-
-		this.vertex[ i ++ ] = data.readFloat();
-
-	}
-
-	data.readTags( this.readTag.bind( this ) );
-
-};
-
-SEA3D.Line.prototype = Object.create( SEA3D.Object3D.prototype );
-SEA3D.Line.prototype.constructor = SEA3D.Line;
-
-SEA3D.Line.prototype.type = "line";
-
-//
-//	Sprite
-//
-
-SEA3D.Sprite = function ( name, data, sea3d ) {
-
-	SEA3D.Object3D.call( this, name, data, sea3d );
-
-	if ( this.attrib & 256 ) {
-
-		this.material = sea3d.getObject( data.readUInt() );
-
-	}
-
-	this.position = data.readVector3();
-
-	this.width = data.readFloat();
-	this.height = data.readFloat();
-
-	data.readTags( this.readTag.bind( this ) );
-
-};
-
-SEA3D.Sprite.prototype = Object.create( SEA3D.Object3D.prototype );
-SEA3D.Sprite.prototype.constructor = SEA3D.Sprite;
-
-SEA3D.Sprite.prototype.type = "m2d";
-
-//
-//	Mesh
-//
-
-SEA3D.Mesh = function ( name, data, sea3d ) {
-
-	SEA3D.Entity3D.call( this, name, data, sea3d );
-
-	// MATERIAL
-	if ( this.attrib & 256 ) {
-
-		this.material = [];
-
-		var len = data.readUByte();
-
-		if ( len == 1 ) this.material[ 0 ] = sea3d.getObject( data.readUInt() );
-		else {
-
-			var i = 0;
-			while ( i < len ) {
-
-				var matIndex = data.readUInt();
-
-				if ( matIndex > 0 ) this.material[ i ++ ] = sea3d.getObject( matIndex - 1 );
-				else this.material[ i ++ ] = undefined;
-
-			}
-
-		}
-
-	}
-
-	if ( this.attrib & 512 ) {
-
-		this.modifiers = [];
-
-		var len = data.readUByte();
-
-		for ( var i = 0; i < len; i ++ ) {
-
-			this.modifiers[ i ] = sea3d.getObject( data.readUInt() );
-
-		}
-
-	}
-
-	if ( this.attrib & 1024 ) {
-
-		this.reference = {
-			type: data.readUByte(),
-			ref: sea3d.getObject( data.readUInt() )
-		};
-
-	}
-
-	this.transform = data.readMatrix();
-
-	this.geometry = sea3d.getObject( data.readUInt() );
-
-	data.readTags( this.readTag.bind( this ) );
-
-};
-
-SEA3D.Mesh.prototype = Object.create( SEA3D.Entity3D.prototype );
-SEA3D.Mesh.prototype.constructor = SEA3D.Mesh;
-
-SEA3D.Mesh.prototype.type = "m3d";
-
-//
-//	Skeleton
-//
-
-SEA3D.Skeleton = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	var length = data.readUShort();
-
-	this.joint = [];
-
-	for ( var i = 0; i < length; i ++ ) {
-
-		this.joint[ i ] = {
-			name: data.readUTF8Tiny(),
-			parentIndex: data.readUShort() - 1,
-			inverseBindMatrix: data.readMatrix()
-		};
-
-	}
-
-};
-
-SEA3D.Skeleton.prototype.type = "skl";
-
-//
-//	Skeleton Local
-//
-
-SEA3D.SkeletonLocal = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	var length = data.readUShort();
-
-	this.joint = [];
-
-	for ( var i = 0; i < length; i ++ ) {
-
-		this.joint[ i ] = {
-			name: data.readUTF8Tiny(),
-			parentIndex: data.readUShort() - 1,
-			// POSITION XYZ
-			x: data.readFloat(),
-			y: data.readFloat(),
-			z: data.readFloat(),
-			// QUATERNION XYZW
-			qx: data.readFloat(),
-			qy: data.readFloat(),
-			qz: data.readFloat(),
-			qw: data.readFloat()
-		};
-
-	}
-
-};
-
-SEA3D.SkeletonLocal.prototype.type = "sklq";
-
-//
-//	Animation Base
-//
-
-SEA3D.AnimationBase = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	var flag = data.readUByte();
-
-	this.sequence = [];
-
-	if ( flag & 1 ) {
-
-		var count = data.readUShort();
-
-		for ( var i = 0; i < count; i ++ ) {
-
-			var seqFlag = data.readUByte();
-
-			this.sequence[ i ] = {
-				name: data.readUTF8Tiny(),
-				start: data.readUInt(),
-				count: data.readUInt(),
-				repeat: ( seqFlag & 1 ) != 0,
-				intrpl: ( seqFlag & 2 ) == 0
-			};
-
-		}
-
-	}
-
-	this.frameRate = data.readUByte();
-	this.numFrames = data.readUInt();
-
-	// no contains sequence
-	if ( this.sequence.length == 0 ) {
-
-		this.sequence[ 0 ] = { name: "root", start: 0, count: this.numFrames, repeat: true, intrpl: true };
-
-	}
-
-};
-
-//
-//	Animation
-//
-
-SEA3D.Animation = function ( name, data, sea3d ) {
-
-	SEA3D.AnimationBase.call( this, name, data, sea3d );
-
-	this.dataList = [];
-
-	for ( var i = 0, l = data.readUByte(); i < l; i ++ ) {
-
-		var kind = data.readUShort(),
-			type = data.readUByte();
-
-		var anmRaw = data.readVector( type, this.numFrames, 0 );
-
-		this.dataList.push( {
-			kind: kind,
-			type: type,
-			blockSize: SEA3D.Stream.sizeOf( type ),
-			data: anmRaw
-		} );
-
-	}
-
-};
-
-SEA3D.Animation.POSITION = 0;
-SEA3D.Animation.ROTATION = 1;
-SEA3D.Animation.SCALE = 2;
-SEA3D.Animation.COLOR = 3;
-SEA3D.Animation.MULTIPLIER = 4;
-SEA3D.Animation.ATTENUATION_START = 5;
-SEA3D.Animation.ATTENUATION_END = 6;
-SEA3D.Animation.FOV = 7;
-SEA3D.Animation.OFFSET_U = 8;
-SEA3D.Animation.OFFSET_V = 9;
-SEA3D.Animation.SCALE_U = 10;
-SEA3D.Animation.SCALE_V = 11;
-SEA3D.Animation.ANGLE = 12;
-SEA3D.Animation.ALPHA = 13;
-SEA3D.Animation.VOLUME = 14;
-
-SEA3D.Animation.MORPH = 250;
-
-SEA3D.Animation.prototype = Object.create( SEA3D.AnimationBase.prototype );
-SEA3D.Animation.prototype.constructor = SEA3D.Animation;
-
-SEA3D.Animation.prototype.type = "anm";
-
-//
-//	Skeleton Animation
-//
-
-SEA3D.SkeletonAnimation = function ( name, data, sea3d ) {
-
-	SEA3D.AnimationBase.call( this, name, data, sea3d );
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.numJoints = data.readUShort();
-
-	this.raw = data.readFloatArray( this.numFrames * this.numJoints * 7 );
-
-};
-
-SEA3D.SkeletonAnimation.prototype.type = "skla";
-
-//
-//	UVW Animation
-//
-
-SEA3D.UVWAnimation = function ( name, data, sea3d ) {
-
-	SEA3D.Animation.call( this, name, data, sea3d );
-
-};
-
-SEA3D.UVWAnimation.prototype.type = "auvw";
-
-//
-//	Morph
-//
-
-SEA3D.Morph = function ( name, data, sea3d ) {
-
-	SEA3D.GeometryBase.call( this, name, data, sea3d );
-
-	var useVertex = ( this.attrib & 2 ) != 0;
-	var useNormal = ( this.attrib & 4 ) != 0;
-
-	var nodeCount = data.readUShort();
-
-	this.node = [];
-
-	for ( var i = 0; i < nodeCount; i ++ ) {
-
-		var nodeName = data.readUTF8Tiny(),
-			verts, norms;
-
-		if ( useVertex ) verts = data.readFloatArray( this.length );
-		if ( useNormal ) norms = data.readFloatArray( this.length );
-
-		this.node[ i ] = { vertex: verts, normal: norms, name: nodeName };
-
-	}
-
-};
-
-SEA3D.Morph.prototype = Object.create( SEA3D.GeometryBase.prototype );
-SEA3D.Morph.prototype.constructor = SEA3D.Morph;
-
-SEA3D.Morph.prototype.type = "mph";
-
-//
-//	Morph Animation
-//
-
-SEA3D.MorphAnimation = function ( name, data, sea3d ) {
-
-	SEA3D.AnimationBase.call( this, name, data, sea3d );
-
-	this.dataList = [];
-
-	for ( var i = 0, l = data.readUByte(); i < l; i ++ ) {
-
-		this.dataList.push( {
-			kind: SEA3D.Animation.MORPH,
-			type: SEA3D.Stream.FLOAT,
-			name: data.readUTF8Tiny(),
-			blockSize: 1,
-			data: data.readVector( SEA3D.Stream.FLOAT, this.numFrames, 0 )
-		} );
-
-	}
-
-};
-
-SEA3D.MorphAnimation.prototype.type = "mpha";
-
-//
-//	Vertex Animation
-//
-
-SEA3D.VertexAnimation = function ( name, data, sea3d ) {
-
-	SEA3D.AnimationBase.call( this, name, data, sea3d );
-
-	var flags = data.readUByte();
-
-	this.isBig = ( flags & 1 ) != 0;
-
-	data.readVInt = this.isBig ? data.readUInt : data.readUShort;
-
-	this.numVertex = data.readVInt();
-
-	this.length = this.numVertex * 3;
-
-	var useVertex = ( flags & 2 ) != 0;
-	var useNormal = ( flags & 4 ) != 0;
-
-	this.frame = [];
-
-	var i, verts, norms;
-
-	for ( i = 0; i < this.numFrames; i ++ ) {
-
-		if ( useVertex ) verts = data.readFloatArray( this.length );
-		if ( useNormal ) norms = data.readFloatArray( this.length );
-
-		this.frame[ i ] = { vertex: verts, normal: norms };
-
-	}
-
-};
-
-SEA3D.VertexAnimation.prototype = Object.create( SEA3D.AnimationBase.prototype );
-SEA3D.VertexAnimation.prototype.constructor = SEA3D.VertexAnimation;
-
-SEA3D.VertexAnimation.prototype.type = "vtxa";
-
-//
-//	Camera
-//
-
-SEA3D.Camera = function ( name, data, sea3d ) {
-
-	SEA3D.Object3D.call( this, name, data, sea3d );
-
-	if ( this.attrib & 64 ) {
-
-		this.dof = {
-			distance: data.readFloat(),
-			range: data.readFloat()
-		};
-
-	}
-
-	this.transform = data.readMatrix();
-
-	this.fov = data.readFloat();
-
-	data.readTags( this.readTag.bind( this ) );
-
-};
-
-SEA3D.Camera.prototype = Object.create( SEA3D.Object3D.prototype );
-SEA3D.Camera.prototype.constructor = SEA3D.Camera;
-
-SEA3D.Camera.prototype.type = "cam";
-
-//
-//	Orthographic Camera
-//
-
-SEA3D.OrthographicCamera = function ( name, data, sea3d ) {
-
-	SEA3D.Object3D.call( this, name, data, sea3d );
-
-	this.transform = data.readMatrix();
-
-	this.height = data.readFloat();
-
-	data.readTags( this.readTag.bind( this ) );
-
-};
-
-SEA3D.OrthographicCamera.prototype = Object.create( SEA3D.Object3D.prototype );
-SEA3D.OrthographicCamera.prototype.constructor = SEA3D.OrthographicCamera;
-
-SEA3D.OrthographicCamera.prototype.type = "camo";
-
-//
-//	Joint Object
-//
-
-SEA3D.JointObject = function ( name, data, sea3d ) {
-
-	SEA3D.Object3D.call( this, name, data, sea3d );
-
-	this.target = sea3d.getObject( data.readUInt() );
-	this.joint = data.readUShort();
-
-	data.readTags( this.readTag.bind( this ) );
-
-};
-
-SEA3D.JointObject.prototype = Object.create( SEA3D.Object3D.prototype );
-SEA3D.JointObject.prototype.constructor = SEA3D.JointObject;
-
-SEA3D.JointObject.prototype.type = "jnt";
-
-//
-//	Light
-//
-
-SEA3D.Light = function ( name, data, sea3d ) {
-
-	SEA3D.Object3D.call( this, name, data, sea3d );
-
-	this.attenStart = Number.MAX_VALUE;
-	this.attenEnd = Number.MAX_VALUE;
-
-	if ( this.attrib & 64 ) {
-
-		var shadowHeader = data.readUByte();
-
-		this.shadow = {};
-
-		this.shadow.opacity = shadowHeader & 1 ? data.readFloat() : 1;
-		this.shadow.color = shadowHeader & 2 ? data.readUInt24() : 0x000000;
-
-	}
-
-	if ( this.attrib & 512 ) {
-
-		this.attenStart = data.readFloat();
-		this.attenEnd = data.readFloat();
-
-	}
-
-	this.color = data.readUInt24();
-	this.multiplier = data.readFloat();
-
-};
-
-SEA3D.Light.prototype = Object.create( SEA3D.Object3D.prototype );
-SEA3D.Light.prototype.constructor = SEA3D.Light;
-
-//
-//	Point Light
-//
-
-SEA3D.PointLight = function ( name, data, sea3d ) {
-
-	SEA3D.Light.call( this, name, data, sea3d );
-
-	if ( this.attrib & 128 ) {
-
-		this.attenuation = {
-			start: data.readFloat(),
-			end: data.readFloat()
-		};
-
-	}
-
-	this.position = data.readVector3();
-
-	data.readTags( this.readTag.bind( this ) );
-
-};
-
-SEA3D.PointLight.prototype = Object.create( SEA3D.Light.prototype );
-SEA3D.PointLight.prototype.constructor = SEA3D.PointLight;
-
-SEA3D.PointLight.prototype.type = "plht";
-
-//
-//	Hemisphere Light
-//
-
-SEA3D.HemisphereLight = function ( name, data, sea3d ) {
-
-	SEA3D.Light.call( this, name, data, sea3d );
-
-	if ( this.attrib & 128 ) {
-
-		this.attenuation = {
-			start: data.readFloat(),
-			end: data.readFloat()
-		};
-
-	}
-
-	this.secondColor = data.readUInt24();
-
-	data.readTags( this.readTag.bind( this ) );
-
-};
-
-SEA3D.HemisphereLight.prototype = Object.create( SEA3D.Light.prototype );
-SEA3D.HemisphereLight.prototype.constructor = SEA3D.HemisphereLight;
-
-SEA3D.HemisphereLight.prototype.type = "hlht";
-
-//
-//	Ambient Light
-//
-
-SEA3D.AmbientLight = function ( name, data, sea3d ) {
-
-	SEA3D.Light.call( this, name, data, sea3d );
-
-	data.readTags( this.readTag.bind( this ) );
-
-};
-
-SEA3D.AmbientLight.prototype = Object.create( SEA3D.Light.prototype );
-SEA3D.AmbientLight.prototype.constructor = SEA3D.AmbientLight;
-
-SEA3D.AmbientLight.prototype.type = "alht";
-
-//
-//	Directional Light
-//
-
-SEA3D.DirectionalLight = function ( name, data, sea3d ) {
-
-	SEA3D.Light.call( this, name, data, sea3d );
-
-	this.transform = data.readMatrix();
-
-	data.readTags( this.readTag.bind( this ) );
-
-};
-
-SEA3D.DirectionalLight.prototype = Object.create( SEA3D.Light.prototype );
-SEA3D.DirectionalLight.prototype.constructor = SEA3D.DirectionalLight;
-
-SEA3D.DirectionalLight.prototype.type = "dlht";
-
-//
-//	Material
-//
-
-SEA3D.Material = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.technique = [];
-	this.tecniquesDict = {};
-
-	this.attrib = data.readUShort();
-
-	this.alpha = 1;
-	this.blendMode = "normal";
-
-	this.doubleSided = ( this.attrib & 1 ) != 0;
-
-	this.receiveLights = ( this.attrib & 2 ) == 0;
-	this.receiveShadows = ( this.attrib & 4 ) == 0;
-	this.receiveFog = ( this.attrib & 8 ) == 0;
-
-	this.repeat = ( this.attrib & 16 ) == 0;
-
-	if ( this.attrib & 32 )
-		this.alpha = data.readFloat();
-
-	if ( this.attrib & 64 )
-		this.blendMode = data.readBlendMode();
-
-	if ( this.attrib & 128 )
-		this.animations = data.readAnimationList( sea3d );
-
-	this.depthWrite = ( this.attrib & 256 ) == 0;
-	this.depthTest = ( this.attrib & 512 ) == 0;
-
-	this.premultipliedAlpha = ( this.attrib & 1024 ) != 0;
-
-	var count = data.readUByte();
-
-	for ( var i = 0; i < count; ++ i ) {
-
-		var kind = data.readUShort();
-		var size = data.readUShort();
-		var pos = data.position;
-		var tech, methodAttrib;
-
-		switch ( kind ) {
-
-			case SEA3D.Material.PHONG:
-			
-				tech = {
-					ambientColor: data.readUInt24(),
-					diffuseColor: data.readUInt24(),
-					specularColor: data.readUInt24(),
-
-					specular: data.readFloat(),
-					gloss: data.readFloat()
-				};
-				
-				break;
-
-			case SEA3D.Material.PHYSICAL:
-			
-				tech = {
-					color: data.readUInt24(),
-					roughness: data.readFloat(),
-					metalness: data.readFloat()
-				};
-				
-				break;
-
-			case SEA3D.Material.ANISOTROPIC:
-				break;
-
-			case SEA3D.Material.COMPOSITE_TEXTURE:
-			
-				tech = {
-					composite: sea3d.getObject( data.readUInt() )
-				};
-				
-				break;
-
-			case SEA3D.Material.DIFFUSE_MAP:
-			case SEA3D.Material.SPECULAR_MAP:
-			case SEA3D.Material.NORMAL_MAP:
-			case SEA3D.Material.AMBIENT_MAP:
-			case SEA3D.Material.ALPHA_MAP:
-			case SEA3D.Material.EMISSIVE_MAP:
-			case SEA3D.Material.ROUGHNESS_MAP:
-			case SEA3D.Material.METALNESS_MAP:
-			
-				tech = {
-					texture: sea3d.getObject( data.readUInt() )
-				};
-				
-				break;
-
-			case SEA3D.Material.REFLECTION:
-			case SEA3D.Material.FRESNEL_REFLECTION:
-			
-				tech = {
-					texture: sea3d.getObject( data.readUInt() ),
-					alpha: data.readFloat()
-				};
-
-				if ( kind == SEA3D.Material.FRESNEL_REFLECTION ) {
-
-					tech.power = data.readFloat();
-					tech.normal = data.readFloat();
-
-				}
-				
-				break;
-
-			case SEA3D.Material.REFRACTION:
-			
-				tech = {
-					texture: sea3d.getObject( data.readUInt() ),
-					alpha: data.readFloat(),
-					ior: data.readFloat()
-				};
-				
-				break;
-
-			case SEA3D.Material.RIM:
-			
-				tech = {
-					color: data.readUInt24(),
-					strength: data.readFloat(),
-					power: data.readFloat(),
-					blendMode: data.readBlendMode()
-				};
-				
-				break;
-
-			case SEA3D.Material.LIGHT_MAP:
-			
-				tech = {
-					texture: sea3d.getObject( data.readUInt() ),
-					channel: data.readUByte(),
-					blendMode: data.readBlendMode()
-				};
-				
-				break;
-
-			case SEA3D.Material.DETAIL_MAP:
-			
-				tech = {
-					texture: sea3d.getObject( data.readUInt() ),
-					scale: data.readFloat(),
-					blendMode: data.readBlendMode()
-				};
-				
-				break;
-
-			case SEA3D.Material.CEL:
-			
-				tech = {
-					color: data.readUInt24(),
-					levels: data.readUByte(),
-					size: data.readFloat(),
-					specularCutOff: data.readFloat(),
-					smoothness: data.readFloat()
-				};
-				
-				break;
-
-			case SEA3D.Material.TRANSLUCENT:
-			
-				tech = {
-					translucency: data.readFloat(),
-					scattering: data.readFloat()
-				};
-				
-				break;
-
-			case SEA3D.Material.BLEND_NORMAL_MAP:
-			
-				methodAttrib = data.readUByte();
-
-				tech = {
-					texture: sea3d.getObject( data.readUInt() ),
-					secondaryTexture: sea3d.getObject( data.readUInt() )
-				};
-
-				if ( methodAttrib & 1 ) {
-
-					tech.offsetX0 = data.readFloat();
-					tech.offsetY0 = data.readFloat();
-
-					tech.offsetX1 = data.readFloat();
-					tech.offsetY1 = data.readFloat();
-
-				} else {
-
-					tech.offsetX0 = tech.offsetY0 =
-					tech.offsetX1 = tech.offsetY1 = 0;
-
-				}
-
-				tech.animate = methodAttrib & 2;
-				
-				break;
-
-			case SEA3D.Material.MIRROR_REFLECTION:
-			
-				tech = {
-					texture: sea3d.getObject( data.readUInt() ),
-					alpha: data.readFloat()
-				};
-				break;
-
-			case SEA3D.Material.EMISSIVE:
-			
-				tech = {
-					color: data.readUInt24F()
-				};
-				
-				break;
-
-			case SEA3D.Material.VERTEX_COLOR:
-			
-				tech = {
-					blendMode: data.readBlendMode()
-				};
-				
-				break;
-
-			case SEA3D.Material.WRAP_LIGHTING:
-			
-				tech = {
-					color: data.readUInt24(),
-					strength: data.readFloat()
-				};
-				
-				break;
-
-			case SEA3D.Material.COLOR_REPLACE:
-			
-				methodAttrib = data.readUByte();
-
-				tech = {
-					red: data.readUInt24(),
-					green: data.readUInt24(),
-					blue: data.readUInt24F()
-				};
-
-				if ( methodAttrib & 1 ) tech.mask = sea3d.getObject( data.readUInt() );
-
-				if ( methodAttrib & 2 ) tech.alpha = data.readFloat();
-
-				break;
-
-			case SEA3D.Material.REFLECTION_SPHERICAL:
-			
-				tech = {
-					texture: sea3d.getObject( data.readUInt() ),
-					alpha: data.readFloat()
-				};
-				
-				break;
-
-			case SEA3D.Material.REFLECTIVITY:
-			
-				methodAttrib = data.readUByte();
-
-				tech = {
-					strength: data.readFloat()
-				};
-
-				if ( methodAttrib & 1 ) tech.mask = sea3d.getObject( data.readUInt() );
-
-				break;
-
-			case SEA3D.Material.CLEAR_COAT:
-			
-				tech = {
-					strength: data.readFloat(),
-					roughness: data.readFloat()
-				};
-				
-				break;
-
-			case SEA3D.Material.FLACCIDITY:
-			
-				methodAttrib = data.readUByte();
-
-				tech = {
-					target: sea3d.getObject( data.readUInt() ),
-					scale: data.readFloat(),
-					spring: data.readFloat(),
-					damping: data.readFloat()
-				};
-
-				if ( methodAttrib & 1 ) tech.mask = sea3d.getObject( data.readUInt() );
-
-				break;
-				
-			default:
-			
-				console.warn( "SEA3D: MaterialTechnique not found:", kind.toString( 16 ) );
-
-				data.position = pos += size;
-				
-				continue;
-
-		}
-
-		tech.kind = kind;
-
-		this.technique.push( tech );
-		this.tecniquesDict[ kind ] = tech;
-
-		data.position = pos += size;
-
-	}
-
-};
-
-SEA3D.Material.PHONG = 0;
-SEA3D.Material.COMPOSITE_TEXTURE = 1;
-SEA3D.Material.DIFFUSE_MAP = 2;
-SEA3D.Material.SPECULAR_MAP = 3;
-SEA3D.Material.REFLECTION = 4;
-SEA3D.Material.REFRACTION = 5;
-SEA3D.Material.NORMAL_MAP = 6;
-SEA3D.Material.FRESNEL_REFLECTION = 7;
-SEA3D.Material.RIM = 8;
-SEA3D.Material.LIGHT_MAP = 9;
-SEA3D.Material.DETAIL_MAP = 10;
-SEA3D.Material.CEL = 11;
-SEA3D.Material.TRANSLUCENT = 12;
-SEA3D.Material.BLEND_NORMAL_MAP = 13;
-SEA3D.Material.MIRROR_REFLECTION = 14;
-SEA3D.Material.AMBIENT_MAP = 15;
-SEA3D.Material.ALPHA_MAP = 16;
-SEA3D.Material.EMISSIVE_MAP = 17;
-SEA3D.Material.VERTEX_COLOR = 18;
-SEA3D.Material.WRAP_LIGHTING = 19;
-SEA3D.Material.COLOR_REPLACE = 20;
-SEA3D.Material.REFLECTION_SPHERICAL = 21;
-SEA3D.Material.ANISOTROPIC = 22;
-SEA3D.Material.EMISSIVE = 23;
-SEA3D.Material.PHYSICAL = 24;
-SEA3D.Material.ROUGHNESS_MAP = 25;
-SEA3D.Material.METALNESS_MAP = 26;
-SEA3D.Material.REFLECTIVITY = 27;
-SEA3D.Material.CLEAR_COAT = 28;
-SEA3D.Material.FLACCIDITY = 29;
-
-SEA3D.Material.prototype.type = "mat";
-
-//
-//	Composite
-//
-
-SEA3D.Composite = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	var layerCount = data.readUByte();
-
-	this.layer = [];
-
-	for ( var i = 0; i < layerCount; i ++ ) {
-
-		this.layer[ i ] = new SEA3D.Composite.prototype.Layer( data, sea3d );
-
-	}
-
-};
-
-SEA3D.Composite.prototype.getLayerByName = function ( name ) {
-
-	for ( var i = 0; i < this.layer.length; i ++ ) {
-
-		if ( this.layer[ i ].name == name ) {
-
-			return this.layer[ i ];
-
-		}
-
-	}
-
-};
-
-SEA3D.Composite.prototype.Layer = function ( data, sea3d ) {
-
-	var attrib = data.readUShort();
-
-	if ( attrib & 1 ) this.texture = new SEA3D.Composite.LayerBitmap( data, sea3d );
-	else this.color = data.readUInt24();
-
-	if ( attrib & 2 ) {
-
-		this.mask = new SEA3D.Composite.LayerBitmap( data, sea3d );
-
-	}
-
-	if ( attrib & 4 ) {
-
-		this.name = data.readUTF8Tiny();
-
-	}
-
-	this.blendMode = attrib & 8 ? data.readBlendMode() : "normal";
-
-	this.opacity = attrib & 16 ? data.readFloat() : 1;
-
-};
-
-SEA3D.Composite.LayerBitmap = function ( data, sea3d ) {
-
-	this.map = sea3d.getObject( data.readUInt() );
-
-	var attrib = data.readUShort();
-
-	this.channel = attrib & 1 ? data.readUByte() : 0;
-	this.repeat = attrib & 2 == 0;
-	this.offsetU = attrib & 4 ? data.readFloat() : 0;
-	this.offsetV = attrib & 8 ? data.readFloat() : 0;
-	this.scaleU = attrib & 16 ? data.readFloat() : 1;
-	this.scaleV = attrib & 32 ? data.readFloat() : 1;
-	this.rotation = attrib & 64 ? data.readFloat() : 0;
-
-	if ( attrib & 128 ) this.animation = data.readAnimationList( sea3d );
-
-};
-
-SEA3D.Composite.prototype.type = "ctex";
-
-//
-//	Planar Render
-//
-
-SEA3D.PlanarRender = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.attrib = data.readUByte();
-
-	this.quality = ( this.attrib & 1 ) | ( this.attrib & 2 );
-	this.transform = data.readMatrix();
-
-};
-
-SEA3D.PlanarRender.prototype.type = "rttp";
-
-//
-//	Cube Render
-//
-
-SEA3D.CubeRender = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.attrib = data.readUByte();
-
-	this.quality = ( this.attrib & 1 ) | ( this.attrib & 2 );
-	this.position = data.readVector3();
-
-};
-
-SEA3D.CubeRender.prototype.type = "rttc";
-
-//
-//	Cube Maps
-//
-
-SEA3D.CubeMap = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.transparent = false;
-
-	this.extension = data.readExt();
-
-	this.faces = [];
-
-	for ( var i = 0; i < 6; i ++ ) {
-
-		var size = data.readUInt();
-
-		this.faces[ i ] = data.concat( data.position, size );
-
-		data.position += size;
-
-	}
-
-};
-
-SEA3D.CubeMap.prototype.type = "cmap";
-
-//
-//	JPEG
-//
-
-SEA3D.JPEG = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.transparent = false;
-
-};
-
-SEA3D.JPEG.prototype.type = "jpg";
-
-//
-//	JPEG_XR
-//
-
-SEA3D.JPEG_XR = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.transparent = true;
-
-};
-
-SEA3D.JPEG_XR.prototype.type = "wdp";
-
-//
-//	PNG
-//
-
-SEA3D.PNG = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.transparent = data.getByte( 25 ) == 0x06;
-
-};
-
-SEA3D.PNG.prototype.type = "png";
-
-//
-//	GIF
-//
-
-SEA3D.GIF = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.transparent = data.getByte( 11 ) > 0;
-
-};
-
-SEA3D.GIF.prototype.type = "gif";
-
-//
-//	OGG
-//
-
-SEA3D.OGG = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-};
-
-SEA3D.OGG.prototype.type = "ogg";
-
-//
-//	MP3
-//
-
-SEA3D.MP3 = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-};
-
-SEA3D.MP3.prototype.type = "mp3";
-
-//
-//	Texture Update
-//
-
-SEA3D.TextureUpdate = function ( name, data, sea3d ) {
-
-	this.name = name;
-	this.data = data;
-	this.sea3d = sea3d;
-
-	this.index = data.readUInt();
-	this.bytes = data.concat( data.position, data.length - data.position );
-
-};
-
-SEA3D.TextureUpdate.prototype.type = "uTex";
-
-//
-//	FILE FORMAT
-//
-
-SEA3D.File = function ( config ) {
-
-	this.config = {
-		streaming: true,
-		timeLimit: 60,
-		progressive: false
-	};
-
-	if ( config ) {
-
-		if ( config.streaming !== undefined ) this.config.streaming = config.streaming;
-		if ( config.timeLimit !== undefined ) this.config.timeLimit = config.timeLimit;
-		if ( config.progressive !== undefined ) this.config.progressive = config.progressive;
-		if ( config.path !== undefined ) this.config.path = config.path;
-
-	}
-
-	this.version = SEA3D.VERSION;
-	this.objects = [];
-	this.typeClass = {};
-	this.typeRead = {};
-	this.typeUnique = {};
-	this.position =
-	this.dataPosition = 0;
-	this.scope = this;
-
-	// SEA3D
-	this.addClass( SEA3D.FileInfo, true );
-	this.addClass( SEA3D.Geometry, true );
-	this.addClass( SEA3D.Mesh );
-	this.addClass( SEA3D.Sprite );
-	this.addClass( SEA3D.Material );
-	this.addClass( SEA3D.Composite );
-	this.addClass( SEA3D.PointLight );
-	this.addClass( SEA3D.DirectionalLight );
-	this.addClass( SEA3D.HemisphereLight );
-	this.addClass( SEA3D.AmbientLight );
-	this.addClass( SEA3D.Animation, true );
-	this.addClass( SEA3D.Skeleton, true );
-	this.addClass( SEA3D.SkeletonLocal, true );
-	this.addClass( SEA3D.SkeletonAnimation, true );
-	this.addClass( SEA3D.UVWAnimation, true );
-	this.addClass( SEA3D.JointObject );
-	this.addClass( SEA3D.Camera );
-	this.addClass( SEA3D.OrthographicCamera );
-	this.addClass( SEA3D.Morph, true );
-	this.addClass( SEA3D.MorphAnimation, true );
-	this.addClass( SEA3D.VertexAnimation, true );
-	this.addClass( SEA3D.CubeMap, true );
-	this.addClass( SEA3D.Dummy );
-	this.addClass( SEA3D.Line );
-	this.addClass( SEA3D.SoundPoint );
-	this.addClass( SEA3D.PlanarRender );
-	this.addClass( SEA3D.CubeRender );
-	this.addClass( SEA3D.Actions );
-	this.addClass( SEA3D.Container3D );
-	this.addClass( SEA3D.Properties );
-
-	// URL BASED
-	this.addClass( SEA3D.ScriptURL, true );
-	this.addClass( SEA3D.TextureURL, true );
-	this.addClass( SEA3D.CubeMapURL, true );
-
-	// UNIVERSAL
-	this.addClass( SEA3D.JPEG, true );
-	this.addClass( SEA3D.JPEG_XR, true );
-	this.addClass( SEA3D.PNG, true );
-	this.addClass( SEA3D.GIF, true );
-	this.addClass( SEA3D.OGG, true );
-	this.addClass( SEA3D.MP3, true );
-	this.addClass( SEA3D.JavaScript, true );
-	this.addClass( SEA3D.JavaScriptMethod, true );
-	this.addClass( SEA3D.GLSL, true );
-
-	// Updaters
-	this.addClass( SEA3D.TextureUpdate, true );
-
-	// Extensions
-	var i = SEA3D.File.Extensions.length;
-
-	while ( i -- ) {
-
-		SEA3D.File.Extensions[ i ].call( this );
-
-	}
-
-};
-
-SEA3D.File.Extensions = [];
-SEA3D.File.CompressionLibs = {};
-SEA3D.File.DecompressionMethod = {};
-
-SEA3D.File.setExtension = function ( callback ) {
-
-	SEA3D.File.Extensions.push( callback );
-
-};
-
-SEA3D.File.setDecompressionEngine = function ( id, name, method ) {
-
-	SEA3D.File.CompressionLibs[ id ] = name;
-	SEA3D.File.DecompressionMethod[ id ] = method;
-
-};
-
-SEA3D.File.prototype.addClass = function ( clazz, unique ) {
-
-	this.typeClass[ clazz.prototype.type ] = clazz;
-	this.typeUnique[ clazz.prototype.type ] = unique === true;
-
-};
-
-SEA3D.File.prototype.readHead = function () {
-
-	if ( this.stream.bytesAvailable < 16 )
-		return false;
-
-	if ( this.stream.readUTF8( 3 ) != "SEA" )
-		throw new Error( "Invalid SEA3D format." );
-
-	this.sign = this.stream.readUTF8( 3 );
-
-	this.version = this.stream.readUInt24();
-
-	if ( this.stream.readUByte() != 0 ) {
-
-		throw new Error( "Protection algorithm not compatible." );
-
-	}
-
-	this.compressionID = this.stream.readUByte();
-
-	this.compressionAlgorithm = SEA3D.File.CompressionLibs[ this.compressionID ];
-	this.decompressionMethod = SEA3D.File.DecompressionMethod[ this.compressionID ];
-
-	if ( this.compressionID > 0 && ! this.decompressionMethod ) {
-
-		throw new Error( "Compression algorithm not compatible." );
-
-	}
-
-	this.length = this.stream.readUInt();
-
-	this.dataPosition = this.stream.position;
-
-	this.objects.length = 0;
-
-	this.state = this.readBody;
-
-	if ( this.onHead ) {
-
-		this.onHead( {
-			file: this,
-			sign: this.sign
-		} );
-
-	}
-
-	return true;
-
-};
-
-SEA3D.File.prototype.getObject = function ( index ) {
-
-	return this.objects[ index ];
-
-};
-
-SEA3D.File.prototype.getObjectByName = function ( name ) {
-
-	return this.objects[ name ];
-
-};
-
-SEA3D.File.prototype.readSEAObject = function () {
-
-	if ( this.stream.bytesAvailable < 4 )
-		return null;
-
-	var size = this.stream.readUInt(),
-		position = this.stream.position;
-
-	if ( this.stream.bytesAvailable < size )
-		return null;
-
-	var flag = this.stream.readUByte(),
-		type = this.stream.readExt(),
-		meta = null;
-
-	var name = flag & 1 ? this.stream.readUTF8Tiny() : "",
-		compressed = ( flag & 2 ) != 0,
-		streaming = ( flag & 4 ) != 0;
-
-	if ( flag & 8 ) {
-
-		var metalen = this.stream.readUShort();
-		var metabytes = this.stream.concat( this.stream.position, metalen );
-
-		this.stream.position += metalen;
-
-		if ( compressed && this.decompressionMethod ) {
-
-			metabytes.buffer = this.decompressionMethod( metabytes.buffer );
-
-		}
-
-		meta = metabytes.readProperties( this );
-
-	}
-
-	size -= this.stream.position - position;
-	position = this.stream.position;
-
-	var data = this.stream.concat( position, size ),
-		obj;
-
-	if ( this.typeClass[ type ] ) {
-
-		if ( compressed && this.decompressionMethod ) {
-
-			data.buffer = this.decompressionMethod( data.buffer );
-
-		}
-
-		obj = new this.typeClass[ type ]( name, data, this );
-
-		if ( ( this.config.streaming && streaming || this.config.forceStreaming ) && this.typeRead[ type ] ) {
-
-			this.typeRead[ type ].call( this.scope, obj );
-
-		}
-
-	} else {
-
-		obj = new SEA3D.Object( name, data, type, this );
-
-		console.warn( "SEA3D: Unknown format \"" + type + "\" of file \"" + name + "\". Add a module referring for this format." );
-
-	}
-
-	obj.streaming = streaming;
-	obj.metadata = meta;
-
-	this.objects.push( this.objects[ obj.name + "." + obj.type ] = obj );
-
-	this.dataPosition = position + size;
-
-	++ this.position;
-
-	return obj;
-
-};
-
-SEA3D.File.prototype.isDone = function () {
-
-	return this.position == this.length;
-
-};
-
-SEA3D.File.prototype.readBody = function () {
-
-	this.timer.update();
-
-	if ( ! this.resume ) return false;
-
-	while ( this.position < this.length ) {
-
-		if ( this.timer.deltaTime < this.config.timeLimit ) {
-
-			this.stream.position = this.dataPosition;
-
-			var sea = this.readSEAObject();
-
-			if ( sea ) this.dispatchCompleteObject( sea );
-			else return false;
-
-		} else return false;
-
-	}
-
-	this.state = this.readComplete;
-
-	return true;
-
-};
-
-SEA3D.File.prototype.initParse = function () {
-
-	this.timer = new SEA3D.Timer();
-	this.position = 0;
-	this.resume = true;
-
-};
-
-SEA3D.File.prototype.parse = function () {
-
-	this.initParse();
-
-	if ( isFinite( this.config.timeLimit ) ) requestAnimationFrame( this.parseObject.bind( this ) );
-	else this.parseObject();
-
-};
-
-SEA3D.File.prototype.parseObject = function () {
-
-	this.timer.update();
-
-	while ( this.position < this.length && this.timer.deltaTime < this.config.timeLimit ) {
-
-		var obj = this.objects[ this.position ++ ],
-			type = obj.type;
-
-		if ( ! this.typeUnique[ type ] ) delete obj.tag;
-
-		if ( ( obj.streaming || this.config.forceStreaming ) && this.typeRead[ type ] ) {
-
-			if ( obj.tag == undefined ) {
-
-				this.typeRead[ type ].call( this.scope, obj );
-
-			}
-
-		}
-
-	}
-
-	if ( this.position == this.length ) {
-
-		var elapsedTime = this.timer.elapsedTime;
-		var message = elapsedTime + "ms, " + this.objects.length + " objects";
-
-		if ( this.onParseComplete ) {
-
-			this.onParseComplete( {
-				file: this,
-				timeTotal: elapsedTime,
-				message: message
-			} );
-
-		} else console.log( "SEA3D Parse Complete:", message );
-
-	} else {
-
-		if ( this.onParseProgress ) {
-
-			this.onParseProgress( {
-				file: this,
-				loaded: this.position,
-				total: this.length
-			} );
-
-		}
-
-		setTimeout( this.parseObject.bind( this ), 10 );
-
-	}
-
-};
-
-SEA3D.File.prototype.readComplete = function () {
-
-	this.stream.position = this.dataPosition;
-
-	if ( this.stream.readUInt24F() != 0x5EA3D1 )
-		console.warn( "SEA3D file is corrupted." );
-
-	delete this.state;
-
-	return false;
-
-};
-
-SEA3D.File.prototype.readState = function () {
-
-	while ( this.state() ) continue;
-
-	if ( this.state ) {
-
-		requestAnimationFrame( this.readState.bind( this ) );
-
-		this.dispatchProgress();
-
-	} else {
-
-		this.dispatchComplete();
-
-	}
-
-};
-
-SEA3D.File.prototype.append = function( buffer ) {
-
-	if (this.state) {
-
-		this.stream.append( buffer );
-
-	} else {
-
-		this.read( buffer );
-
-	}
-
-};
-
-SEA3D.File.prototype.read = function ( buffer ) {
-
-	if ( ! buffer ) throw new Error( "No data found." );
-
-	this.initParse();
-
-	this.stream = new SEA3D.Stream( buffer );
-	this.state = this.readHead;
-
-	this.readState();
-
-};
-
-SEA3D.File.prototype.dispatchCompleteObject = function ( obj ) {
-
-	if ( ! this.onCompleteObject ) return;
-
-	this.onCompleteObject( {
-		file: this,
-		object: obj
-	} );
-
-};
-
-SEA3D.File.prototype.dispatchProgress = function () {
-
-	if ( ! this.onProgress ) return;
-
-	this.onProgress( {
-		file: this,
-		loaded: this.position,
-		total: this.length
-	} );
-
-};
-
-SEA3D.File.prototype.dispatchDownloadProgress = function ( position, length ) {
-
-	if ( ! this.onDownloadProgress ) return;
-
-	this.onDownloadProgress( {
-		file: this,
-		loaded: position,
-		total: length
-	} );
-
-};
-
-SEA3D.File.prototype.dispatchComplete = function () {
-
-	var elapsedTime = this.timer.elapsedTime;
-	var message = elapsedTime + "ms, " + this.objects.length + " objects";
-
-	if ( this.onComplete ) this.onComplete( {
-		file: this,
-		timeTotal: elapsedTime,
-		message: message
-	} );
-	else console.log( "SEA3D:", message );
-
-};
-
-SEA3D.File.prototype.dispatchError = function ( id, message ) {
-
-	if ( this.onError ) this.onError( { file: this, id: id, message: message } );
-	else console.error( "SEA3D: #" + id, message );
-
-};
-
-SEA3D.File.prototype.load = function ( url ) {
-
-	var self = this,
-		xhr = new XMLHttpRequest();
-
-	xhr.open( "GET", url, true );
-
-	if (!this.config.path) {
-
-//		this.config.path = THREE.Loader.prototype.extractUrlBase( url );
-		this.config.path = THREE.LoaderUtils.extractUrlBase( url );
-
-	}
-
-	if ( self.config.progressive ) {
-
-		var position = 0;
-
-		xhr.overrideMimeType( 'text/plain; charset=x-user-defined' );
-
-	} else {
-
-		xhr.responseType = 'arraybuffer';
-
-	}
-
-	xhr.onprogress = function ( e ) {
-
-		if ( self.config.progressive ) {
-
-			var binStr = xhr.responseText.substring( position ),
-				bytes = new Uint8Array( binStr.length );
-
-			for ( var i = 0; i < binStr.length; i ++ ) {
-
-				bytes[ i ] = binStr.charCodeAt( i ) & 0xFF;
-
-			}
-
-			position += binStr.length;
-
-			self.append( bytes.buffer );
-
-		}
-
-		self.dispatchDownloadProgress( e.loaded, e.total );
-
-	};
-
-	if ( ! self.config.progressive ) {
-
-		xhr.onreadystatechange = function () {
-
-			if ( xhr.readyState === 4 ) {
-
-				if ( xhr.status === 200 || xhr.status === 0 ) {
-
-					self.read( this.response );
-
-				} else {
-
-					this.dispatchError( 1001, "Couldn't load [" + url + "] [" + xhr.status + "]" );
-
-				}
-
-			}
-
-		};
-
-	}
-
-	xhr.send();
-
-};
+/**
+ * 	SEA3D SDK
+ * 	@author Sunag / http://www.sunag.com.br/
+ */
+
+'use strict';
+
+var SEA3D = { VERSION: 18110 };
+
+SEA3D.getVersion = function () {
+
+	// Max = 16777215 - VV.S.S.BB  | V = Version | S = Subversion | B = Buildversion
+	var v = SEA3D.VERSION.toString(), l = v.length;
+	return v.substring( 0, l - 4 ) + "." + v.substring( l - 4, l - 3 ) + "." + v.substring( l - 3, l - 2 ) + "." + parseFloat( v.substring( l - 2, l ) ).toString();
+
+};
+
+console.log( 'SEA3D ' + SEA3D.getVersion() );
+
+//
+//	STREAM : STANDARD DATA-IO ( LITTLE-ENDIAN )
+//
+
+SEA3D.Stream = function ( buffer ) {
+
+	this.position = 0;
+	this.buffer = buffer || new ArrayBuffer();
+
+};
+
+SEA3D.Stream.NONE = 0;
+
+// 1D = 0 at 31
+SEA3D.Stream.BOOLEAN = 1;
+
+SEA3D.Stream.BYTE = 2;
+SEA3D.Stream.UBYTE = 3;
+
+SEA3D.Stream.SHORT = 4;
+SEA3D.Stream.USHORT = 5;
+
+SEA3D.Stream.INT24 = 6;
+SEA3D.Stream.UINT24 = 7;
+
+SEA3D.Stream.INT = 8;
+SEA3D.Stream.UINT = 9;
+
+SEA3D.Stream.FLOAT = 10;
+SEA3D.Stream.DOUBLE = 11;
+SEA3D.Stream.DECIMAL = 12;
+
+// 2D = 32 at 63
+
+// 3D = 64 at 95
+SEA3D.Stream.VECTOR3D = 74;
+
+// 4D = 96 at 127
+SEA3D.Stream.VECTOR4D = 106;
+
+// Undefined Size = 128 at 255
+SEA3D.Stream.STRING_TINY = 128;
+SEA3D.Stream.STRING_SHORT = 129;
+SEA3D.Stream.STRING_LONG = 130;
+
+SEA3D.Stream.ASSET = 200;
+SEA3D.Stream.GROUP = 255;
+
+SEA3D.Stream.BLEND_MODE = [
+	"normal", "add", "subtract", "multiply", "dividing", "mix", "alpha", "screen", "darken",
+	"overlay", "colorburn", "linearburn", "lighten", "colordodge", "lineardodge",
+	"softlight", "hardlight", "pinlight", "spotlight", "spotlightblend", "hardmix",
+	"average", "difference", "exclusion", "hue", "saturation", "color", "value",
+	"linearlight", "grainextract", "reflect", "glow", "darkercolor", "lightercolor", "phoenix", "negation"
+];
+
+SEA3D.Stream.INTERPOLATION_TABLE =	[
+	"normal", "linear",
+	"sine.in", "sine.out", "sine.inout",
+	"cubic.in", "cubic.out", "cubic.inout",
+	"quint.in", "quint.out", "quint.inout",
+	"circ.in", "circ.out", "circ.inout",
+	"back.in", "back.out", "back.inout",
+	"quad.in", "quad.out", "quad.inout",
+	"quart.in", "quart.out", "quart.inout",
+	"expo.in", "expo.out", "expo.inout",
+	"elastic.in", "elastic.out", "elastic.inout",
+	"bounce.in", "bounce.out", "bounce.inout"
+];
+
+SEA3D.Stream.sizeOf = function ( kind ) {
+
+	if ( kind == 0 ) return 0;
+	else if ( kind >= 1 && kind <= 31 ) return 1;
+	else if ( kind >= 32 && kind <= 63 ) return 2;
+	else if ( kind >= 64 && kind <= 95 ) return 3;
+	else if ( kind >= 96 && kind <= 125 ) return 4;
+	return - 1;
+
+};
+
+SEA3D.Stream.prototype = {
+
+	constructor: SEA3D.Stream,
+
+	set buffer( val ) {
+
+		this.buf = val;
+		this.data = new DataView( val );
+
+	},
+
+	get buffer() {
+
+		return this.buf;
+
+	},
+
+	get bytesAvailable() {
+
+		return this.length - this.position;
+
+	},
+
+	get length() {
+
+		return this.buf.byteLength;
+
+	}
+
+};
+
+SEA3D.Stream.prototype.getByte = function ( pos ) {
+
+	return this.data.getInt8( pos );
+
+};
+
+SEA3D.Stream.prototype.readBytes = function ( len ) {
+
+	var buf = this.buf.slice( this.position, this.position + len );
+	this.position += len;
+	return buf;
+
+};
+
+SEA3D.Stream.prototype.readByte = function () {
+
+	return this.data.getInt8( this.position ++ );
+
+};
+
+SEA3D.Stream.prototype.readUByte = function () {
+
+	return this.data.getUint8( this.position ++ );
+
+};
+
+SEA3D.Stream.prototype.readBool = function () {
+
+	return this.data.getInt8( this.position ++ ) != 0;
+
+};
+
+SEA3D.Stream.prototype.readShort = function () {
+
+	var v = this.data.getInt16( this.position, true );
+	this.position += 2;
+	return v;
+
+};
+
+SEA3D.Stream.prototype.readUShort = function () {
+
+	var v = this.data.getUint16( this.position, true );
+	this.position += 2;
+	return v;
+
+};
+
+SEA3D.Stream.prototype.readUInt24 = function () {
+
+	var v = this.data.getUint32( this.position, true ) & 0xFFFFFF;
+	this.position += 3;
+	return v;
+
+};
+
+SEA3D.Stream.prototype.readUInt24F = function () {
+
+	return this.readUShort() | ( this.readUByte() << 16 );
+
+};
+
+SEA3D.Stream.prototype.readInt = function () {
+
+	var v = this.data.getInt32( this.position, true );
+	this.position += 4;
+	return v;
+
+};
+
+SEA3D.Stream.prototype.readUInt = function () {
+
+	var v = this.data.getUint32( this.position, true );
+	this.position += 4;
+	return v;
+
+};
+
+SEA3D.Stream.prototype.readFloat = function () {
+
+	var v = this.data.getFloat32( this.position, true );
+	this.position += 4;
+	return v;
+
+};
+
+SEA3D.Stream.prototype.readUInteger = function () {
+
+	var v = this.readUByte(),
+		r = v & 0x7F;
+
+	if ( ( v & 0x80 ) != 0 ) {
+
+		v = this.readUByte();
+		r |= ( v & 0x7F ) << 7;
+
+		if ( ( v & 0x80 ) != 0 ) {
+
+			v = this.readUByte();
+			r |= ( v & 0x7F ) << 13;
+
+		}
+
+	}
+
+	return r;
+
+};
+
+SEA3D.Stream.prototype.readVector2 = function () {
+
+	return { x: this.readFloat(), y: this.readFloat() };
+
+};
+
+SEA3D.Stream.prototype.readVector3 = function () {
+
+	return { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() };
+
+};
+
+SEA3D.Stream.prototype.readVector4 = function () {
+
+	return { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), w: this.readFloat() };
+
+};
+
+SEA3D.Stream.prototype.readMatrix = function () {
+
+	var mtx = new Float32Array( 16 );
+
+	mtx[ 0 ] = this.readFloat();
+	mtx[ 1 ] = this.readFloat();
+	mtx[ 2 ] = this.readFloat();
+	mtx[ 3 ] = 0.0;
+	mtx[ 4 ] = this.readFloat();
+	mtx[ 5 ] = this.readFloat();
+	mtx[ 6 ] = this.readFloat();
+	mtx[ 7 ] = 0.0;
+	mtx[ 8 ] = this.readFloat();
+	mtx[ 9 ] = this.readFloat();
+	mtx[ 10 ] = this.readFloat();
+	mtx[ 11 ] = 0.0;
+	mtx[ 12 ] = this.readFloat();
+	mtx[ 13 ] = this.readFloat();
+	mtx[ 14 ] = this.readFloat();
+	mtx[ 15 ] = 1.0;
+
+	return mtx;
+
+};
+
+SEA3D.Stream.prototype.readUTF8 = function ( len ) {
+
+	var buffer = this.readBytes( len );
+
+	return THREE.LoaderUtils.decodeText( new Uint8Array( buffer ) );
+
+};
+
+SEA3D.Stream.prototype.readExt = function () {
+
+	return this.readUTF8( 4 ).replace( /\0/g, "" );
+
+};
+
+SEA3D.Stream.prototype.readUTF8Tiny = function () {
+
+	return this.readUTF8( this.readUByte() );
+
+};
+
+SEA3D.Stream.prototype.readUTF8Short = function () {
+
+	return this.readUTF8( this.readUShort() );
+
+};
+
+SEA3D.Stream.prototype.readUTF8Long = function () {
+
+	return this.readUTF8( this.readUInt() );
+
+};
+
+SEA3D.Stream.prototype.readUByteArray = function ( length ) {
+
+	var v = new Uint8Array( length );
+
+	SEA3D.Stream.memcpy(
+		v.buffer,
+		0,
+		this.buffer,
+		this.position,
+		length
+	);
+
+	this.position += length;
+
+	return v;
+
+};
+
+SEA3D.Stream.prototype.readUShortArray = function ( length ) {
+
+	var v = new Uint16Array( length ),
+		len = length * 2;
+
+	SEA3D.Stream.memcpy(
+		v.buffer,
+		0,
+		this.buffer,
+		this.position,
+		len
+	);
+
+	this.position += len;
+
+	return v;
+
+};
+
+
+SEA3D.Stream.prototype.readUInt24Array = function ( length ) {
+
+	var v = new Uint32Array( length );
+
+	for ( var i = 0; i < length; i ++ ) {
+
+		v[ i ] = this.readUInt24();
+
+	}
+
+	return v;
+
+};
+
+
+SEA3D.Stream.prototype.readUIntArray = function ( length ) {
+
+	var v = new Uint32Array( length ),
+		len = length * 4;
+
+	SEA3D.Stream.memcpy(
+		v.buffer,
+		0,
+		this.buffer,
+		this.position,
+		len
+	);
+
+	this.position += len;
+
+	return v;
+
+};
+
+SEA3D.Stream.prototype.readFloatArray = function ( length ) {
+
+	var v = new Float32Array( length ),
+		len = length * 4;
+
+	SEA3D.Stream.memcpy(
+		v.buffer,
+		0,
+		this.buffer,
+		this.position,
+		len
+	);
+
+	this.position += len;
+
+	return v;
+
+};
+
+
+SEA3D.Stream.prototype.readBlendMode = function () {
+
+	return SEA3D.Stream.BLEND_MODE[ this.readUByte() ];
+
+};
+
+SEA3D.Stream.prototype.readInterpolation = function () {
+
+	return SEA3D.Stream.INTERPOLATION_TABLE[ this.readUByte() ];
+
+};
+
+SEA3D.Stream.prototype.readTags = function ( callback ) {
+
+	var numTag = this.readUByte();
+
+	for ( var i = 0; i < numTag; ++ i ) {
+
+		var kind = this.readUShort();
+		var size = this.readUInt();
+		var pos = this.position;
+
+		callback( kind, this, size );
+
+		this.position = pos += size;
+
+	}
+
+};
+
+SEA3D.Stream.prototype.readProperties = function ( sea3d ) {
+
+	var count = this.readUByte(),
+		props = {}, types = {};
+
+	props.__type = types;
+
+	for ( var i = 0; i < count; i ++ ) {
+
+		var name = this.readUTF8Tiny(),
+			type = this.readUByte();
+
+		types[ name ] = type;
+		props[ name ] = type == SEA3D.Stream.GROUP ? this.readProperties( sea3d ) : this.readToken( type, sea3d );
+
+	}
+
+	return props;
+
+};
+
+SEA3D.Stream.prototype.readAnimationList = function ( sea3d ) {
+
+	var list = [],
+		count = this.readUByte();
+
+	var i = 0;
+	while ( i < count ) {
+
+		var attrib = this.readUByte(),
+			anm = {};
+
+		anm.relative = ( attrib & 1 ) != 0;
+
+		if ( attrib & 2 ) anm.timeScale = this.readFloat();
+
+		anm.tag = sea3d.getObject( this.readUInt() );
+
+		list[ i ++ ] = anm;
+
+	}
+
+	return list;
+
+};
+
+SEA3D.Stream.prototype.readScriptList = function ( sea3d ) {
+
+	var list = [],
+		count = this.readUByte();
+
+	var i = 0;
+	while ( i < count ) {
+
+		var attrib = this.readUByte(),
+			script = {};
+
+		script.priority = ( attrib & 1 ) | ( attrib & 2 );
+
+		if ( attrib & 4 ) {
+
+			var numParams = this.readUByte();
+
+			script.params = {};
+
+			for ( var j = 0; j < numParams; j ++ ) {
+
+				var name = this.readUTF8Tiny();
+
+				script.params[ name ] = this.readObject( sea3d );
+
+			}
+
+		}
+
+		if ( attrib & 8 ) {
+
+			script.method = this.readUTF8Tiny();
+
+		}
+
+		script.tag = sea3d.getObject( this.readUInt() );
+
+		list[ i ++ ] = script;
+
+	}
+
+	return list;
+
+};
+
+SEA3D.Stream.prototype.readObject = function ( sea3d ) {
+
+	return this.readToken( this.readUByte(), sea3d );
+
+};
+
+SEA3D.Stream.prototype.readToken = function ( type, sea3d ) {
+
+	switch ( type )	{
+
+		// 1D
+		case SEA3D.Stream.BOOLEAN:
+			return this.readBool();
+			break;
+
+		case SEA3D.Stream.UBYTE:
+			return this.readUByte();
+			break;
+
+		case SEA3D.Stream.USHORT:
+			return this.readUShort();
+			break;
+
+		case SEA3D.Stream.UINT24:
+			return this.readUInt24();
+			break;
+
+		case SEA3D.Stream.INT:
+			return this.readInt();
+			break;
+
+		case SEA3D.Stream.UINT:
+			return this.readUInt();
+			break;
+
+		case SEA3D.Stream.FLOAT:
+			return this.readFloat();
+			break;
+
+		// 3D
+		case SEA3D.Stream.VECTOR3D:
+			return this.readVector3();
+			break;
+
+		// 4D
+		case SEA3D.Stream.VECTOR4D:
+			return this.readVector4();
+			break;
+
+		// Undefined Values
+		case SEA3D.Stream.STRING_TINY:
+			return this.readUTF8Tiny();
+			break;
+
+		case SEA3D.Stream.STRING_SHORT:
+			return this.readUTF8Short();
+			break;
+
+		case SEA3D.Stream.STRING_LONG:
+			return this.readUTF8Long();
+			break;
+
+		case SEA3D.Stream.ASSET:
+			var asset = this.readUInt();
+			return asset > 0 ? sea3d.getObject( asset - 1 ) : null;
+			break;
+
+		default:
+			console.error( "DataType not found!" );
+
+	}
+
+	return null;
+
+};
+
+SEA3D.Stream.prototype.readVector = function ( type, length, offset ) {
+
+	var size = SEA3D.Stream.sizeOf( type ),
+		i = offset * size,
+		count = i + ( length * size );
+
+	switch ( type )	{
+
+		// 1D
+		case SEA3D.Stream.BOOLEAN:
+
+			return this.readUByteArray( count );
+
+
+		case SEA3D.Stream.UBYTE:
+
+			return this.readUByteArray( count );
+
+
+		case SEA3D.Stream.USHORT:
+
+			return this.readUShortArray( count );
+
+
+		case SEA3D.Stream.UINT24:
+
+			return this.readUInt24Array( count );
+
+
+		case SEA3D.Stream.UINT:
+
+			return this.readUIntArray( count );
+
+
+		case SEA3D.Stream.FLOAT:
+
+			return this.readFloatArray( count );
+
+
+		// 3D
+		case SEA3D.Stream.VECTOR3D:
+
+			return this.readFloatArray( count );
+
+
+		// 4D
+		case SEA3D.Stream.VECTOR4D:
+
+			return this.readFloatArray( count );
+
+	}
+
+};
+
+SEA3D.Stream.prototype.append = function ( data ) {
+
+	var buffer = new ArrayBuffer( this.data.byteLength + data.byteLength );
+
+	SEA3D.Stream.memcpy( buffer, 0, this.data.buffer, 0, this.data.byteLength );
+	SEA3D.Stream.memcpy( buffer, this.data.byteLength, data, 0, data.byteLength );
+
+	this.buffer = buffer;
+
+};
+
+SEA3D.Stream.prototype.concat = function ( position, length ) {
+
+	return new SEA3D.Stream( this.buffer.slice( position, position + length ) );
+
+};
+
+/**
+ * @author DataStream.js
+ */
+
+SEA3D.Stream.memcpy = function ( dst, dstOffset, src, srcOffset, byteLength ) {
+
+	var dstU8 = new Uint8Array( dst, dstOffset, byteLength );
+	var srcU8 = new Uint8Array( src, srcOffset, byteLength );
+
+	dstU8.set( srcU8 );
+
+};
+
+//
+//	UByteArray
+//
+
+SEA3D.UByteArray = function () {
+
+	this.ubytes = [];
+	this.length = 0;
+
+};
+
+SEA3D.UByteArray.prototype = {
+
+	constructor: SEA3D.UByteArray,
+
+	add: function ( ubytes ) {
+
+		this.ubytes.push( ubytes );
+		this.length += ubytes.byteLength;
+
+	},
+
+	toBuffer: function () {
+
+		var memcpy = new Uint8Array( this.length );
+
+		for ( var i = 0, offset = 0; i < this.ubytes.length; i ++ ) {
+
+			memcpy.set( this.ubytes[ i ], offset );
+			offset += this.ubytes[ i ].byteLength;
+
+		}
+
+		return memcpy.buffer;
+
+	}
+};
+
+//
+//	Math
+//
+
+SEA3D.Math = {
+	RAD_TO_DEG: 180 / Math.PI,
+	DEG_TO_RAD: Math.PI / 180
+};
+
+SEA3D.Math.angle = function ( val ) {
+
+	var ang = 180,
+		inv = val < 0;
+
+	val = ( inv ? - val : val ) % 360;
+
+	if ( val > ang ) {
+
+		val = - ang + ( val - ang );
+
+	}
+
+	return ( inv ? - val : val );
+
+};
+
+SEA3D.Math.angleDiff = function ( a, b ) {
+
+	a *= this.DEG_TO_RAD;
+	b *= this.DEG_TO_RAD;
+
+	return Math.atan2( Math.sin( a - b ), Math.cos( a - b ) ) * this.RAD_TO_DEG;
+
+};
+
+SEA3D.Math.angleArea = function ( angle, target, area ) {
+
+	return Math.abs( this.angleDiff( angle, target ) ) <= area;
+
+};
+
+SEA3D.Math.direction = function ( x1, y1, x2, y2 ) {
+
+	return Math.atan2( y2 - y1, x2 - x1 );
+
+};
+
+SEA3D.Math.physicalLerp = function ( val, to, deltaTime, duration ) {
+
+	var t = deltaTime / duration;
+
+	if ( t > 1 ) t = 1;
+
+	return val + ( ( to - val ) * t );
+
+};
+
+SEA3D.Math.physicalAngle = function ( val, to, deltaTime, duration ) {
+
+	if ( Math.abs( val - to ) > 180 ) {
+
+		if ( val > to ) {
+
+			to += 360;
+
+		} else {
+
+			to -= 360;
+
+		}
+
+	}
+
+	var t = deltaTime / duration;
+
+	if ( t > 1 ) t = 1;
+
+	return this.angle( val + ( ( to - val ) * t ) );
+
+};
+
+SEA3D.Math.zero = function ( value, precision ) {
+
+	precision = precision || 1.0E-3;
+
+	var pValue = value < 0 ? - value : value;
+
+	if ( pValue - precision < 0 ) value = 0;
+
+	return value;
+
+};
+
+SEA3D.Math.round = function ( value, precision ) {
+
+	precision = Math.pow( 10, precision );
+
+	return Math.round( value * precision ) / precision;
+
+};
+
+SEA3D.Math.lerpAngle = function ( val, tar, t ) {
+
+	if ( Math.abs( val - tar ) > 180 ) {
+
+		if ( val > tar ) {
+
+			tar += 360;
+
+		} else {
+
+			tar -= 360;
+
+		}
+
+	}
+
+	val += ( tar - val ) * t;
+
+	return SEA3D.Math.angle( val );
+
+};
+
+SEA3D.Math.lerpColor = function ( val, tar, t ) {
+
+	var a0 = val >> 24 & 0xff,
+		r0 = val >> 16 & 0xff,
+		g0 = val >> 8 & 0xff,
+		b0 = val & 0xff;
+
+	var a1 = tar >> 24 & 0xff,
+		r1 = tar >> 16 & 0xff,
+		g1 = tar >> 8 & 0xff,
+		b1 = tar & 0xff;
+
+	a0 += ( a1 - a0 ) * t;
+	r0 += ( r1 - r0 ) * t;
+	g0 += ( g1 - g0 ) * t;
+	b0 += ( b1 - b0 ) * t;
+
+	return a0 << 24 | r0 << 16 | g0 << 8 | b0;
+
+};
+
+SEA3D.Math.lerp = function ( val, tar, t ) {
+
+	return val + ( ( tar - val ) * t );
+
+};
+
+//
+//	Timer
+//
+
+SEA3D.Timer = function () {
+
+	this.time = this.start = Date.now();
+
+};
+
+SEA3D.Timer.prototype = {
+
+	constructor: SEA3D.Timer,
+
+	get now() {
+
+		return Date.now();
+
+	},
+
+	get deltaTime() {
+
+		return Date.now() - this.time;
+
+	},
+
+	get elapsedTime() {
+
+		return Date.now() - this.start;
+
+	},
+
+	update: function () {
+
+		this.time = Date.now();
+
+	}
+};
+
+//
+//	Object
+//
+
+SEA3D.Object = function ( name, data, type, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.type = type;
+	this.sea3d = sea3d;
+
+};
+
+//
+//	Geometry Base
+//
+
+SEA3D.GeometryBase = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.attrib = data.readUShort();
+
+	this.isBig = ( this.attrib & 1 ) != 0;
+
+	// variable uint
+	data.readVInt = this.isBig ? data.readUInt : data.readUShort;
+
+	this.numVertex = data.readVInt();
+
+	this.length = this.numVertex * 3;
+
+};
+
+//
+//	Geometry
+//
+
+SEA3D.Geometry = function ( name, data, sea3d ) {
+
+	SEA3D.GeometryBase.call( this, name, data, sea3d );
+
+	var i, j, len;
+
+	// NORMAL
+	if ( this.attrib & 4 ) {
+
+		this.normal = data.readFloatArray( this.length );
+
+	}
+
+	// TANGENT
+	if ( this.attrib & 8 ) {
+
+		this.tangent = data.readFloatArray( this.length );
+
+	}
+
+	// UV
+	if ( this.attrib & 32 ) {
+
+		var uvCount = data.readUByte();
+
+		if ( uvCount ) {
+
+			this.uv = [];
+
+			len = this.numVertex * 2;
+
+			i = 0;
+			while ( i < uvCount ) {
+
+				// UV VERTEX DATA
+				this.uv[ i ++ ] = data.readFloatArray( len );
+
+			}
+
+		}
+
+	}
+
+	// JOINT-INDEXES / WEIGHTS
+	if ( this.attrib & 64 ) {
+
+		this.jointPerVertex = data.readUByte();
+
+		var jntLen = this.numVertex * this.jointPerVertex;
+
+		this.joint = data.readUShortArray( jntLen );
+		this.weight = data.readFloatArray( jntLen );
+
+	}
+
+	// VERTEX_COLOR
+	if ( this.attrib & 128 ) {
+
+		var colorAttrib = data.readUByte();
+
+		var colorCount = data.readUByte();
+
+		if ( colorCount ) {
+
+			this.numColor = ( ( ( colorAttrib & 64 ) >> 6 ) | ( ( colorAttrib & 128 ) >> 6 ) ) + 1;
+
+			this.color = [];
+
+			for ( i = 0 & 15; i < colorCount; i ++ ) {
+
+				this.color.push( data.readFloatArray( this.numVertex * this.numColor ) );
+
+			}
+
+		}
+
+	}
+
+	// VERTEX
+	this.vertex = data.readFloatArray( this.length );
+
+	// SUB-MESHES
+	var count = data.readUByte();
+
+	this.groups = [];
+
+	if ( this.attrib & 1024 ) {
+
+		// INDEXES
+		for ( i = 0, len = 0; i < count; i ++ ) {
+
+			j = data.readVInt() * 3;
+
+			this.groups.push( {
+				start: len,
+				count: j
+			} );
+
+			len += j;
+
+		}
+
+		if ( ! ( this.attrib & 2048 ) ) {
+
+			this.indexes = this.isBig ? data.readUIntArray( len ) : data.readUShortArray( len );
+
+		}
+
+	} else {
+
+		// INDEXES
+		var stride = this.isBig ? 4 : 2,
+			bytearray = new SEA3D.UByteArray();
+
+		for ( i = 0, j = 0; i < count; i ++ ) {
+
+			len = data.readVInt() * 3;
+
+			this.groups.push( {
+				start: j,
+				count: len
+			} );
+
+			j += len;
+
+			bytearray.add( data.readUByteArray( len * stride ) );
+
+		}
+
+		this.indexes = this.isBig ? new Uint32Array( bytearray.toBuffer() ) : new Uint16Array( bytearray.toBuffer() );
+
+	}
+
+};
+
+SEA3D.Geometry.prototype = Object.create( SEA3D.GeometryBase.prototype );
+SEA3D.Geometry.prototype.constructor = SEA3D.Geometry;
+
+SEA3D.Geometry.prototype.type = "geo";
+
+//
+//	Object3D
+//
+
+SEA3D.Object3D = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.isStatic = false;
+	this.visible = true;
+
+	this.attrib = data.readUShort();
+
+	if ( this.attrib & 1 ) this.parent = sea3d.getObject( data.readUInt() );
+
+	if ( this.attrib & 2 ) this.animations = data.readAnimationList( sea3d );
+
+	if ( this.attrib & 4 ) this.scripts = data.readScriptList( sea3d );
+
+	if ( this.attrib & 16 ) this.attributes = sea3d.getObject( data.readUInt() );
+
+	if ( this.attrib & 32 ) {
+
+		var objectType = data.readUByte();
+
+		this.isStatic = ( objectType & 1 ) != 0;
+		this.visible = ( objectType & 2 ) == 0;
+
+	}
+
+};
+
+SEA3D.Object3D.prototype.readTag = function ( kind, data, size ) {
+
+};
+
+//
+//	Entity3D
+//
+
+SEA3D.Entity3D = function ( name, data, sea3d ) {
+
+	SEA3D.Object3D.call( this, name, data, sea3d );
+
+	this.castShadows = true;
+
+	if ( this.attrib & 64 ) {
+
+		var lightType = data.readUByte();
+
+		this.castShadows = ( lightType & 1 ) == 0;
+
+	}
+
+};
+
+SEA3D.Entity3D.prototype = Object.create( SEA3D.Object3D.prototype );
+SEA3D.Entity3D.prototype.constructor = SEA3D.Entity3D;
+
+//
+//	Sound3D
+//
+
+SEA3D.Sound3D = function ( name, data, sea3d ) {
+
+	SEA3D.Object3D.call( this, name, data, sea3d );
+
+	this.autoPlay = ( this.attrib & 64 ) != 0;
+
+	if ( this.attrib & 128 ) this.mixer = sea3d.getObject( data.readUInt() );
+
+	this.sound = sea3d.getObject( data.readUInt() );
+	this.volume = data.readFloat();
+
+};
+
+SEA3D.Sound3D.prototype = Object.create( SEA3D.Object3D.prototype );
+SEA3D.Sound3D.prototype.constructor = SEA3D.Sound3D;
+
+//
+//	Sound Point
+//
+
+SEA3D.SoundPoint = function ( name, data, sea3d ) {
+
+	SEA3D.Sound3D.call( this, name, data, sea3d );
+
+	this.position = data.readVector3();
+	this.distance = data.readFloat();
+
+	data.readTags( this.readTag.bind( this ) );
+
+};
+
+SEA3D.SoundPoint.prototype = Object.create( SEA3D.Sound3D.prototype );
+SEA3D.SoundPoint.prototype.constructor = SEA3D.SoundPoint;
+
+SEA3D.SoundPoint.prototype.type = "sp";
+
+//
+//	Container3D
+//
+
+SEA3D.Container3D = function ( name, data, sea3d ) {
+
+	SEA3D.Object3D.call( this, name, data, sea3d );
+
+	this.transform = data.readMatrix();
+
+	data.readTags( this.readTag.bind( this ) );
+
+};
+
+SEA3D.Container3D.prototype = Object.create( SEA3D.Object3D.prototype );
+SEA3D.Container3D.prototype.constructor = SEA3D.Container3D;
+
+SEA3D.Container3D.prototype.type = "c3d";
+
+//
+//	Script URL
+//
+
+SEA3D.ScriptURL = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.url = data.readUTF8( data.length );
+
+};
+
+SEA3D.ScriptURL.prototype.type = "src";
+
+//
+//	Texture URL
+//
+
+SEA3D.TextureURL = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.url = sea3d.config.path + data.readUTF8( data.length );
+
+};
+
+SEA3D.TextureURL.prototype.type = "urlT";
+
+//
+//	CubeMap URL
+//
+
+SEA3D.CubeMapURL = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.faces = [];
+
+	for ( var i = 0; i < 6; i ++ ) {
+
+		this.faces[ i ] = data.readUTF8Tiny();
+
+	}
+
+};
+
+SEA3D.CubeMapURL.prototype.type = "cURL";
+
+//
+//	Actions
+//
+
+SEA3D.Actions = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.count = data.readUInt();
+	this.actions = [];
+
+	for ( var i = 0; i < this.count; i ++ ) {
+
+		var flag = data.readUByte();
+		var kind = data.readUShort();
+
+		var size = data.readUShort();
+
+		var position = data.position;
+		var act = this.actions[ i ] = { kind: kind };
+
+		// range of animation
+		if ( flag & 1 ) {
+
+			// start and count in frames
+			act.range = [ data.readUInt(), data.readUInt() ];
+
+		}
+
+		// time
+		if ( flag & 2 ) {
+
+			act.time = data.readUInt();
+
+		}
+
+		// easing
+		if ( flag & 4 ) {
+
+			act.intrpl = data.readInterpolation();
+
+			if ( act.intrpl.indexOf( 'back.' ) == 0 ) {
+
+				act.intrplParam0 = data.readFloat();
+
+			} else if ( act.intrpl.indexOf( 'elastic.' ) == 0 ) {
+
+				act.intrplParam0 = data.readFloat();
+				act.intrplParam1 = data.readFloat();
+
+			}
+
+		}
+
+		switch ( kind ) {
+
+			case SEA3D.Actions.RTT_TARGET:
+				act.source = sea3d.getObject( data.readUInt() );
+				act.target = sea3d.getObject( data.readUInt() );
+				break;
+
+			case SEA3D.Actions.LOOK_AT:
+				act.source = sea3d.getObject( data.readUInt() );
+				act.target = sea3d.getObject( data.readUInt() );
+				break;
+
+			case SEA3D.Actions.PLAY_SOUND:
+				act.sound = sea3d.getObject( data.readUInt() );
+				act.offset = data.readUInt();
+				break;
+
+			case SEA3D.Actions.PLAY_ANIMATION:
+				act.object = sea3d.getObject( data.readUInt() );
+				act.name = data.readUTF8Tiny();
+				break;
+
+			case SEA3D.Actions.FOG:
+				act.color = data.readUInt24();
+				act.min = data.readFloat();
+				act.max = data.readFloat();
+				break;
+
+			case SEA3D.Actions.ENVIRONMENT:
+				act.texture = sea3d.getObject( data.readUInt() );
+				break;
+
+			case SEA3D.Actions.ENVIRONMENT_COLOR:
+				act.color = data.readUInt24F();
+				break;
+
+			case SEA3D.Actions.CAMERA:
+				act.camera = sea3d.getObject( data.readUInt() );
+				break;
+
+			case SEA3D.Actions.SCRIPTS:
+				act.scripts = data.readScriptList( sea3d );
+				break;
+
+			case SEA3D.Actions.CLASS_OF:
+				act.classof = sea3d.getObject( data.readUInt() );
+				break;
+
+			case SEA3D.Actions.ATTRIBUTES:
+				act.attributes = sea3d.getObject( data.readUInt() );
+				break;
+
+			default:
+				console.log( "Action \"" + kind + "\" not found." );
+				break;
+
+		}
+
+		data.position = position + size;
+
+	}
+
+};
+
+SEA3D.Actions.SCENE = 0;
+SEA3D.Actions.ENVIRONMENT_COLOR = 1;
+SEA3D.Actions.ENVIRONMENT = 2;
+SEA3D.Actions.FOG = 3;
+SEA3D.Actions.PLAY_ANIMATION = 4;
+SEA3D.Actions.PLAY_SOUND = 5;
+SEA3D.Actions.ANIMATION_AUDIO_SYNC = 6;
+SEA3D.Actions.LOOK_AT = 7;
+SEA3D.Actions.RTT_TARGET = 8;
+SEA3D.Actions.CAMERA = 9;
+SEA3D.Actions.SCRIPTS = 10;
+SEA3D.Actions.CLASS_OF = 11;
+SEA3D.Actions.ATTRIBUTES = 12;
+
+SEA3D.Actions.prototype.type = "act";
+
+//
+//	Properties
+//
+
+SEA3D.Properties = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.props = data.readProperties( sea3d );
+	this.props.__name = name;
+
+};
+
+SEA3D.Properties.prototype.type = "prop";
+
+//
+//	File Info
+//
+
+SEA3D.FileInfo = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.info = data.readProperties( sea3d );
+	this.info.__name = name;
+
+	sea3d.info = this.info;
+
+};
+
+SEA3D.FileInfo.prototype.type = "info";
+
+//
+//	Java Script
+//
+
+SEA3D.JavaScript = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.src = data.readUTF8( data.length );
+
+};
+
+SEA3D.JavaScript.prototype.type = "js";
+
+//
+//	Java Script Method
+//
+
+SEA3D.JavaScriptMethod = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	var count = data.readUShort();
+
+	this.methods = {};
+
+	for ( var i = 0; i < count; i ++ ) {
+
+		var flag = data.readUByte();
+		var method = data.readUTF8Tiny();
+
+		this.methods[ method ] = {
+			src: data.readUTF8Long()
+		};
+
+	}
+
+};
+
+SEA3D.JavaScriptMethod.prototype.type = "jsm";
+
+//
+//	GLSL
+//
+
+SEA3D.GLSL = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.src = data.readUTF8( data.length );
+
+};
+
+SEA3D.GLSL.prototype.type = "glsl";
+
+//
+//	Dummy
+//
+
+SEA3D.Dummy = function ( name, data, sea3d ) {
+
+	SEA3D.Object3D.call( this, name, data, sea3d );
+
+	this.transform = data.readMatrix();
+
+	this.width = data.readFloat();
+	this.height = data.readFloat();
+	this.depth = data.readFloat();
+
+	data.readTags( this.readTag.bind( this ) );
+
+};
+
+SEA3D.Dummy.prototype = Object.create( SEA3D.Object3D.prototype );
+SEA3D.Dummy.prototype.constructor = SEA3D.Dummy;
+
+SEA3D.Dummy.prototype.type = "dmy";
+
+//
+//	Line
+//
+
+SEA3D.Line = function ( name, data, sea3d ) {
+
+	SEA3D.Object3D.call( this, name, data, sea3d );
+
+	this.count = ( this.attrib & 64 ? data.readUInt() : data.readUShort() ) * 3;
+	this.closed = ( this.attrib & 128 ) != 0;
+	this.transform = data.readMatrix();
+
+	this.vertex = [];
+
+	var i = 0;
+	while ( i < this.count ) {
+
+		this.vertex[ i ++ ] = data.readFloat();
+
+	}
+
+	data.readTags( this.readTag.bind( this ) );
+
+};
+
+SEA3D.Line.prototype = Object.create( SEA3D.Object3D.prototype );
+SEA3D.Line.prototype.constructor = SEA3D.Line;
+
+SEA3D.Line.prototype.type = "line";
+
+//
+//	Sprite
+//
+
+SEA3D.Sprite = function ( name, data, sea3d ) {
+
+	SEA3D.Object3D.call( this, name, data, sea3d );
+
+	if ( this.attrib & 256 ) {
+
+		this.material = sea3d.getObject( data.readUInt() );
+
+	}
+
+	this.position = data.readVector3();
+
+	this.width = data.readFloat();
+	this.height = data.readFloat();
+
+	data.readTags( this.readTag.bind( this ) );
+
+};
+
+SEA3D.Sprite.prototype = Object.create( SEA3D.Object3D.prototype );
+SEA3D.Sprite.prototype.constructor = SEA3D.Sprite;
+
+SEA3D.Sprite.prototype.type = "m2d";
+
+//
+//	Mesh
+//
+
+SEA3D.Mesh = function ( name, data, sea3d ) {
+
+	SEA3D.Entity3D.call( this, name, data, sea3d );
+
+	// MATERIAL
+	if ( this.attrib & 256 ) {
+
+		this.material = [];
+
+		var len = data.readUByte();
+
+		if ( len == 1 ) this.material[ 0 ] = sea3d.getObject( data.readUInt() );
+		else {
+
+			var i = 0;
+			while ( i < len ) {
+
+				var matIndex = data.readUInt();
+
+				if ( matIndex > 0 ) this.material[ i ++ ] = sea3d.getObject( matIndex - 1 );
+				else this.material[ i ++ ] = undefined;
+
+			}
+
+		}
+
+	}
+
+	if ( this.attrib & 512 ) {
+
+		this.modifiers = [];
+
+		var len = data.readUByte();
+
+		for ( var i = 0; i < len; i ++ ) {
+
+			this.modifiers[ i ] = sea3d.getObject( data.readUInt() );
+
+		}
+
+	}
+
+	if ( this.attrib & 1024 ) {
+
+		this.reference = {
+			type: data.readUByte(),
+			ref: sea3d.getObject( data.readUInt() )
+		};
+
+	}
+
+	this.transform = data.readMatrix();
+
+	this.geometry = sea3d.getObject( data.readUInt() );
+
+	data.readTags( this.readTag.bind( this ) );
+
+};
+
+SEA3D.Mesh.prototype = Object.create( SEA3D.Entity3D.prototype );
+SEA3D.Mesh.prototype.constructor = SEA3D.Mesh;
+
+SEA3D.Mesh.prototype.type = "m3d";
+
+//
+//	Skeleton
+//
+
+SEA3D.Skeleton = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	var length = data.readUShort();
+
+	this.joint = [];
+
+	for ( var i = 0; i < length; i ++ ) {
+
+		this.joint[ i ] = {
+			name: data.readUTF8Tiny(),
+			parentIndex: data.readUShort() - 1,
+			inverseBindMatrix: data.readMatrix()
+		};
+
+	}
+
+};
+
+SEA3D.Skeleton.prototype.type = "skl";
+
+//
+//	Skeleton Local
+//
+
+SEA3D.SkeletonLocal = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	var length = data.readUShort();
+
+	this.joint = [];
+
+	for ( var i = 0; i < length; i ++ ) {
+
+		this.joint[ i ] = {
+			name: data.readUTF8Tiny(),
+			parentIndex: data.readUShort() - 1,
+			// POSITION XYZ
+			x: data.readFloat(),
+			y: data.readFloat(),
+			z: data.readFloat(),
+			// QUATERNION XYZW
+			qx: data.readFloat(),
+			qy: data.readFloat(),
+			qz: data.readFloat(),
+			qw: data.readFloat()
+		};
+
+	}
+
+};
+
+SEA3D.SkeletonLocal.prototype.type = "sklq";
+
+//
+//	Animation Base
+//
+
+SEA3D.AnimationBase = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	var flag = data.readUByte();
+
+	this.sequence = [];
+
+	if ( flag & 1 ) {
+
+		var count = data.readUShort();
+
+		for ( var i = 0; i < count; i ++ ) {
+
+			var seqFlag = data.readUByte();
+
+			this.sequence[ i ] = {
+				name: data.readUTF8Tiny(),
+				start: data.readUInt(),
+				count: data.readUInt(),
+				repeat: ( seqFlag & 1 ) != 0,
+				intrpl: ( seqFlag & 2 ) == 0
+			};
+
+		}
+
+	}
+
+	this.frameRate = data.readUByte();
+	this.numFrames = data.readUInt();
+
+	// no contains sequence
+	if ( this.sequence.length == 0 ) {
+
+		this.sequence[ 0 ] = { name: "root", start: 0, count: this.numFrames, repeat: true, intrpl: true };
+
+	}
+
+};
+
+//
+//	Animation
+//
+
+SEA3D.Animation = function ( name, data, sea3d ) {
+
+	SEA3D.AnimationBase.call( this, name, data, sea3d );
+
+	this.dataList = [];
+
+	for ( var i = 0, l = data.readUByte(); i < l; i ++ ) {
+
+		var kind = data.readUShort(),
+			type = data.readUByte();
+
+		var anmRaw = data.readVector( type, this.numFrames, 0 );
+
+		this.dataList.push( {
+			kind: kind,
+			type: type,
+			blockSize: SEA3D.Stream.sizeOf( type ),
+			data: anmRaw
+		} );
+
+	}
+
+};
+
+SEA3D.Animation.POSITION = 0;
+SEA3D.Animation.ROTATION = 1;
+SEA3D.Animation.SCALE = 2;
+SEA3D.Animation.COLOR = 3;
+SEA3D.Animation.MULTIPLIER = 4;
+SEA3D.Animation.ATTENUATION_START = 5;
+SEA3D.Animation.ATTENUATION_END = 6;
+SEA3D.Animation.FOV = 7;
+SEA3D.Animation.OFFSET_U = 8;
+SEA3D.Animation.OFFSET_V = 9;
+SEA3D.Animation.SCALE_U = 10;
+SEA3D.Animation.SCALE_V = 11;
+SEA3D.Animation.ANGLE = 12;
+SEA3D.Animation.ALPHA = 13;
+SEA3D.Animation.VOLUME = 14;
+
+SEA3D.Animation.MORPH = 250;
+
+SEA3D.Animation.prototype = Object.create( SEA3D.AnimationBase.prototype );
+SEA3D.Animation.prototype.constructor = SEA3D.Animation;
+
+SEA3D.Animation.prototype.type = "anm";
+
+//
+//	Skeleton Animation
+//
+
+SEA3D.SkeletonAnimation = function ( name, data, sea3d ) {
+
+	SEA3D.AnimationBase.call( this, name, data, sea3d );
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.numJoints = data.readUShort();
+
+	this.raw = data.readFloatArray( this.numFrames * this.numJoints * 7 );
+
+};
+
+SEA3D.SkeletonAnimation.prototype.type = "skla";
+
+//
+//	UVW Animation
+//
+
+SEA3D.UVWAnimation = function ( name, data, sea3d ) {
+
+	SEA3D.Animation.call( this, name, data, sea3d );
+
+};
+
+SEA3D.UVWAnimation.prototype.type = "auvw";
+
+//
+//	Morph
+//
+
+SEA3D.Morph = function ( name, data, sea3d ) {
+
+	SEA3D.GeometryBase.call( this, name, data, sea3d );
+
+	var useVertex = ( this.attrib & 2 ) != 0;
+	var useNormal = ( this.attrib & 4 ) != 0;
+
+	var nodeCount = data.readUShort();
+
+	this.node = [];
+
+	for ( var i = 0; i < nodeCount; i ++ ) {
+
+		var nodeName = data.readUTF8Tiny(),
+			verts, norms;
+
+		if ( useVertex ) verts = data.readFloatArray( this.length );
+		if ( useNormal ) norms = data.readFloatArray( this.length );
+
+		this.node[ i ] = { vertex: verts, normal: norms, name: nodeName };
+
+	}
+
+};
+
+SEA3D.Morph.prototype = Object.create( SEA3D.GeometryBase.prototype );
+SEA3D.Morph.prototype.constructor = SEA3D.Morph;
+
+SEA3D.Morph.prototype.type = "mph";
+
+//
+//	Morph Animation
+//
+
+SEA3D.MorphAnimation = function ( name, data, sea3d ) {
+
+	SEA3D.AnimationBase.call( this, name, data, sea3d );
+
+	this.dataList = [];
+
+	for ( var i = 0, l = data.readUByte(); i < l; i ++ ) {
+
+		this.dataList.push( {
+			kind: SEA3D.Animation.MORPH,
+			type: SEA3D.Stream.FLOAT,
+			name: data.readUTF8Tiny(),
+			blockSize: 1,
+			data: data.readVector( SEA3D.Stream.FLOAT, this.numFrames, 0 )
+		} );
+
+	}
+
+};
+
+SEA3D.MorphAnimation.prototype.type = "mpha";
+
+//
+//	Vertex Animation
+//
+
+SEA3D.VertexAnimation = function ( name, data, sea3d ) {
+
+	SEA3D.AnimationBase.call( this, name, data, sea3d );
+
+	var flags = data.readUByte();
+
+	this.isBig = ( flags & 1 ) != 0;
+
+	data.readVInt = this.isBig ? data.readUInt : data.readUShort;
+
+	this.numVertex = data.readVInt();
+
+	this.length = this.numVertex * 3;
+
+	var useVertex = ( flags & 2 ) != 0;
+	var useNormal = ( flags & 4 ) != 0;
+
+	this.frame = [];
+
+	var i, verts, norms;
+
+	for ( i = 0; i < this.numFrames; i ++ ) {
+
+		if ( useVertex ) verts = data.readFloatArray( this.length );
+		if ( useNormal ) norms = data.readFloatArray( this.length );
+
+		this.frame[ i ] = { vertex: verts, normal: norms };
+
+	}
+
+};
+
+SEA3D.VertexAnimation.prototype = Object.create( SEA3D.AnimationBase.prototype );
+SEA3D.VertexAnimation.prototype.constructor = SEA3D.VertexAnimation;
+
+SEA3D.VertexAnimation.prototype.type = "vtxa";
+
+//
+//	Camera
+//
+
+SEA3D.Camera = function ( name, data, sea3d ) {
+
+	SEA3D.Object3D.call( this, name, data, sea3d );
+
+	if ( this.attrib & 64 ) {
+
+		this.dof = {
+			distance: data.readFloat(),
+			range: data.readFloat()
+		};
+
+	}
+
+	this.transform = data.readMatrix();
+
+	this.fov = data.readFloat();
+
+	data.readTags( this.readTag.bind( this ) );
+
+};
+
+SEA3D.Camera.prototype = Object.create( SEA3D.Object3D.prototype );
+SEA3D.Camera.prototype.constructor = SEA3D.Camera;
+
+SEA3D.Camera.prototype.type = "cam";
+
+//
+//	Orthographic Camera
+//
+
+SEA3D.OrthographicCamera = function ( name, data, sea3d ) {
+
+	SEA3D.Object3D.call( this, name, data, sea3d );
+
+	this.transform = data.readMatrix();
+
+	this.height = data.readFloat();
+
+	data.readTags( this.readTag.bind( this ) );
+
+};
+
+SEA3D.OrthographicCamera.prototype = Object.create( SEA3D.Object3D.prototype );
+SEA3D.OrthographicCamera.prototype.constructor = SEA3D.OrthographicCamera;
+
+SEA3D.OrthographicCamera.prototype.type = "camo";
+
+//
+//	Joint Object
+//
+
+SEA3D.JointObject = function ( name, data, sea3d ) {
+
+	SEA3D.Object3D.call( this, name, data, sea3d );
+
+	this.target = sea3d.getObject( data.readUInt() );
+	this.joint = data.readUShort();
+
+	data.readTags( this.readTag.bind( this ) );
+
+};
+
+SEA3D.JointObject.prototype = Object.create( SEA3D.Object3D.prototype );
+SEA3D.JointObject.prototype.constructor = SEA3D.JointObject;
+
+SEA3D.JointObject.prototype.type = "jnt";
+
+//
+//	Light
+//
+
+SEA3D.Light = function ( name, data, sea3d ) {
+
+	SEA3D.Object3D.call( this, name, data, sea3d );
+
+	this.attenStart = Number.MAX_VALUE;
+	this.attenEnd = Number.MAX_VALUE;
+
+	if ( this.attrib & 64 ) {
+
+		var shadowHeader = data.readUByte();
+
+		this.shadow = {};
+
+		this.shadow.opacity = shadowHeader & 1 ? data.readFloat() : 1;
+		this.shadow.color = shadowHeader & 2 ? data.readUInt24() : 0x000000;
+
+	}
+
+	if ( this.attrib & 512 ) {
+
+		this.attenStart = data.readFloat();
+		this.attenEnd = data.readFloat();
+
+	}
+
+	this.color = data.readUInt24();
+	this.multiplier = data.readFloat();
+
+};
+
+SEA3D.Light.prototype = Object.create( SEA3D.Object3D.prototype );
+SEA3D.Light.prototype.constructor = SEA3D.Light;
+
+//
+//	Point Light
+//
+
+SEA3D.PointLight = function ( name, data, sea3d ) {
+
+	SEA3D.Light.call( this, name, data, sea3d );
+
+	if ( this.attrib & 128 ) {
+
+		this.attenuation = {
+			start: data.readFloat(),
+			end: data.readFloat()
+		};
+
+	}
+
+	this.position = data.readVector3();
+
+	data.readTags( this.readTag.bind( this ) );
+
+};
+
+SEA3D.PointLight.prototype = Object.create( SEA3D.Light.prototype );
+SEA3D.PointLight.prototype.constructor = SEA3D.PointLight;
+
+SEA3D.PointLight.prototype.type = "plht";
+
+//
+//	Hemisphere Light
+//
+
+SEA3D.HemisphereLight = function ( name, data, sea3d ) {
+
+	SEA3D.Light.call( this, name, data, sea3d );
+
+	if ( this.attrib & 128 ) {
+
+		this.attenuation = {
+			start: data.readFloat(),
+			end: data.readFloat()
+		};
+
+	}
+
+	this.secondColor = data.readUInt24();
+
+	data.readTags( this.readTag.bind( this ) );
+
+};
+
+SEA3D.HemisphereLight.prototype = Object.create( SEA3D.Light.prototype );
+SEA3D.HemisphereLight.prototype.constructor = SEA3D.HemisphereLight;
+
+SEA3D.HemisphereLight.prototype.type = "hlht";
+
+//
+//	Ambient Light
+//
+
+SEA3D.AmbientLight = function ( name, data, sea3d ) {
+
+	SEA3D.Light.call( this, name, data, sea3d );
+
+	data.readTags( this.readTag.bind( this ) );
+
+};
+
+SEA3D.AmbientLight.prototype = Object.create( SEA3D.Light.prototype );
+SEA3D.AmbientLight.prototype.constructor = SEA3D.AmbientLight;
+
+SEA3D.AmbientLight.prototype.type = "alht";
+
+//
+//	Directional Light
+//
+
+SEA3D.DirectionalLight = function ( name, data, sea3d ) {
+
+	SEA3D.Light.call( this, name, data, sea3d );
+
+	this.transform = data.readMatrix();
+
+	data.readTags( this.readTag.bind( this ) );
+
+};
+
+SEA3D.DirectionalLight.prototype = Object.create( SEA3D.Light.prototype );
+SEA3D.DirectionalLight.prototype.constructor = SEA3D.DirectionalLight;
+
+SEA3D.DirectionalLight.prototype.type = "dlht";
+
+//
+//	Material
+//
+
+SEA3D.Material = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.technique = [];
+	this.tecniquesDict = {};
+
+	this.attrib = data.readUShort();
+
+	this.alpha = 1;
+	this.blendMode = "normal";
+
+	this.doubleSided = ( this.attrib & 1 ) != 0;
+
+	this.receiveLights = ( this.attrib & 2 ) == 0;
+	this.receiveShadows = ( this.attrib & 4 ) == 0;
+	this.receiveFog = ( this.attrib & 8 ) == 0;
+
+	this.repeat = ( this.attrib & 16 ) == 0;
+
+	if ( this.attrib & 32 )
+		this.alpha = data.readFloat();
+
+	if ( this.attrib & 64 )
+		this.blendMode = data.readBlendMode();
+
+	if ( this.attrib & 128 )
+		this.animations = data.readAnimationList( sea3d );
+
+	this.depthWrite = ( this.attrib & 256 ) == 0;
+	this.depthTest = ( this.attrib & 512 ) == 0;
+
+	this.premultipliedAlpha = ( this.attrib & 1024 ) != 0;
+
+	var count = data.readUByte();
+
+	for ( var i = 0; i < count; ++ i ) {
+
+		var kind = data.readUShort();
+		var size = data.readUShort();
+		var pos = data.position;
+		var tech, methodAttrib;
+
+		switch ( kind ) {
+
+			case SEA3D.Material.PHONG:
+			
+				tech = {
+					ambientColor: data.readUInt24(),
+					diffuseColor: data.readUInt24(),
+					specularColor: data.readUInt24(),
+
+					specular: data.readFloat(),
+					gloss: data.readFloat()
+				};
+				
+				break;
+
+			case SEA3D.Material.PHYSICAL:
+			
+				tech = {
+					color: data.readUInt24(),
+					roughness: data.readFloat(),
+					metalness: data.readFloat()
+				};
+				
+				break;
+
+			case SEA3D.Material.ANISOTROPIC:
+				break;
+
+			case SEA3D.Material.COMPOSITE_TEXTURE:
+			
+				tech = {
+					composite: sea3d.getObject( data.readUInt() )
+				};
+				
+				break;
+
+			case SEA3D.Material.DIFFUSE_MAP:
+			case SEA3D.Material.SPECULAR_MAP:
+			case SEA3D.Material.NORMAL_MAP:
+			case SEA3D.Material.AMBIENT_MAP:
+			case SEA3D.Material.ALPHA_MAP:
+			case SEA3D.Material.EMISSIVE_MAP:
+			case SEA3D.Material.ROUGHNESS_MAP:
+			case SEA3D.Material.METALNESS_MAP:
+			
+				tech = {
+					texture: sea3d.getObject( data.readUInt() )
+				};
+				
+				break;
+
+			case SEA3D.Material.REFLECTION:
+			case SEA3D.Material.FRESNEL_REFLECTION:
+			
+				tech = {
+					texture: sea3d.getObject( data.readUInt() ),
+					alpha: data.readFloat()
+				};
+
+				if ( kind == SEA3D.Material.FRESNEL_REFLECTION ) {
+
+					tech.power = data.readFloat();
+					tech.normal = data.readFloat();
+
+				}
+				
+				break;
+
+			case SEA3D.Material.REFRACTION:
+			
+				tech = {
+					texture: sea3d.getObject( data.readUInt() ),
+					alpha: data.readFloat(),
+					ior: data.readFloat()
+				};
+				
+				break;
+
+			case SEA3D.Material.RIM:
+			
+				tech = {
+					color: data.readUInt24(),
+					strength: data.readFloat(),
+					power: data.readFloat(),
+					blendMode: data.readBlendMode()
+				};
+				
+				break;
+
+			case SEA3D.Material.LIGHT_MAP:
+			
+				tech = {
+					texture: sea3d.getObject( data.readUInt() ),
+					channel: data.readUByte(),
+					blendMode: data.readBlendMode()
+				};
+				
+				break;
+
+			case SEA3D.Material.DETAIL_MAP:
+			
+				tech = {
+					texture: sea3d.getObject( data.readUInt() ),
+					scale: data.readFloat(),
+					blendMode: data.readBlendMode()
+				};
+				
+				break;
+
+			case SEA3D.Material.CEL:
+			
+				tech = {
+					color: data.readUInt24(),
+					levels: data.readUByte(),
+					size: data.readFloat(),
+					specularCutOff: data.readFloat(),
+					smoothness: data.readFloat()
+				};
+				
+				break;
+
+			case SEA3D.Material.TRANSLUCENT:
+			
+				tech = {
+					translucency: data.readFloat(),
+					scattering: data.readFloat()
+				};
+				
+				break;
+
+			case SEA3D.Material.BLEND_NORMAL_MAP:
+			
+				methodAttrib = data.readUByte();
+
+				tech = {
+					texture: sea3d.getObject( data.readUInt() ),
+					secondaryTexture: sea3d.getObject( data.readUInt() )
+				};
+
+				if ( methodAttrib & 1 ) {
+
+					tech.offsetX0 = data.readFloat();
+					tech.offsetY0 = data.readFloat();
+
+					tech.offsetX1 = data.readFloat();
+					tech.offsetY1 = data.readFloat();
+
+				} else {
+
+					tech.offsetX0 = tech.offsetY0 =
+					tech.offsetX1 = tech.offsetY1 = 0;
+
+				}
+
+				tech.animate = methodAttrib & 2;
+				
+				break;
+
+			case SEA3D.Material.MIRROR_REFLECTION:
+			
+				tech = {
+					texture: sea3d.getObject( data.readUInt() ),
+					alpha: data.readFloat()
+				};
+				break;
+
+			case SEA3D.Material.EMISSIVE:
+			
+				tech = {
+					color: data.readUInt24F()
+				};
+				
+				break;
+
+			case SEA3D.Material.VERTEX_COLOR:
+			
+				tech = {
+					blendMode: data.readBlendMode()
+				};
+				
+				break;
+
+			case SEA3D.Material.WRAP_LIGHTING:
+			
+				tech = {
+					color: data.readUInt24(),
+					strength: data.readFloat()
+				};
+				
+				break;
+
+			case SEA3D.Material.COLOR_REPLACE:
+			
+				methodAttrib = data.readUByte();
+
+				tech = {
+					red: data.readUInt24(),
+					green: data.readUInt24(),
+					blue: data.readUInt24F()
+				};
+
+				if ( methodAttrib & 1 ) tech.mask = sea3d.getObject( data.readUInt() );
+
+				if ( methodAttrib & 2 ) tech.alpha = data.readFloat();
+
+				break;
+
+			case SEA3D.Material.REFLECTION_SPHERICAL:
+			
+				tech = {
+					texture: sea3d.getObject( data.readUInt() ),
+					alpha: data.readFloat()
+				};
+				
+				break;
+
+			case SEA3D.Material.REFLECTIVITY:
+			
+				methodAttrib = data.readUByte();
+
+				tech = {
+					strength: data.readFloat()
+				};
+
+				if ( methodAttrib & 1 ) tech.mask = sea3d.getObject( data.readUInt() );
+
+				break;
+
+			case SEA3D.Material.CLEAR_COAT:
+			
+				tech = {
+					strength: data.readFloat(),
+					roughness: data.readFloat()
+				};
+				
+				break;
+
+			case SEA3D.Material.FLACCIDITY:
+			
+				methodAttrib = data.readUByte();
+
+				tech = {
+					target: sea3d.getObject( data.readUInt() ),
+					scale: data.readFloat(),
+					spring: data.readFloat(),
+					damping: data.readFloat()
+				};
+
+				if ( methodAttrib & 1 ) tech.mask = sea3d.getObject( data.readUInt() );
+
+				break;
+				
+			default:
+			
+				console.warn( "SEA3D: MaterialTechnique not found:", kind.toString( 16 ) );
+
+				data.position = pos += size;
+				
+				continue;
+
+		}
+
+		tech.kind = kind;
+
+		this.technique.push( tech );
+		this.tecniquesDict[ kind ] = tech;
+
+		data.position = pos += size;
+
+	}
+
+};
+
+SEA3D.Material.PHONG = 0;
+SEA3D.Material.COMPOSITE_TEXTURE = 1;
+SEA3D.Material.DIFFUSE_MAP = 2;
+SEA3D.Material.SPECULAR_MAP = 3;
+SEA3D.Material.REFLECTION = 4;
+SEA3D.Material.REFRACTION = 5;
+SEA3D.Material.NORMAL_MAP = 6;
+SEA3D.Material.FRESNEL_REFLECTION = 7;
+SEA3D.Material.RIM = 8;
+SEA3D.Material.LIGHT_MAP = 9;
+SEA3D.Material.DETAIL_MAP = 10;
+SEA3D.Material.CEL = 11;
+SEA3D.Material.TRANSLUCENT = 12;
+SEA3D.Material.BLEND_NORMAL_MAP = 13;
+SEA3D.Material.MIRROR_REFLECTION = 14;
+SEA3D.Material.AMBIENT_MAP = 15;
+SEA3D.Material.ALPHA_MAP = 16;
+SEA3D.Material.EMISSIVE_MAP = 17;
+SEA3D.Material.VERTEX_COLOR = 18;
+SEA3D.Material.WRAP_LIGHTING = 19;
+SEA3D.Material.COLOR_REPLACE = 20;
+SEA3D.Material.REFLECTION_SPHERICAL = 21;
+SEA3D.Material.ANISOTROPIC = 22;
+SEA3D.Material.EMISSIVE = 23;
+SEA3D.Material.PHYSICAL = 24;
+SEA3D.Material.ROUGHNESS_MAP = 25;
+SEA3D.Material.METALNESS_MAP = 26;
+SEA3D.Material.REFLECTIVITY = 27;
+SEA3D.Material.CLEAR_COAT = 28;
+SEA3D.Material.FLACCIDITY = 29;
+
+SEA3D.Material.prototype.type = "mat";
+
+//
+//	Composite
+//
+
+SEA3D.Composite = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	var layerCount = data.readUByte();
+
+	this.layer = [];
+
+	for ( var i = 0; i < layerCount; i ++ ) {
+
+		this.layer[ i ] = new SEA3D.Composite.prototype.Layer( data, sea3d );
+
+	}
+
+};
+
+SEA3D.Composite.prototype.getLayerByName = function ( name ) {
+
+	for ( var i = 0; i < this.layer.length; i ++ ) {
+
+		if ( this.layer[ i ].name == name ) {
+
+			return this.layer[ i ];
+
+		}
+
+	}
+
+};
+
+SEA3D.Composite.prototype.Layer = function ( data, sea3d ) {
+
+	var attrib = data.readUShort();
+
+	if ( attrib & 1 ) this.texture = new SEA3D.Composite.LayerBitmap( data, sea3d );
+	else this.color = data.readUInt24();
+
+	if ( attrib & 2 ) {
+
+		this.mask = new SEA3D.Composite.LayerBitmap( data, sea3d );
+
+	}
+
+	if ( attrib & 4 ) {
+
+		this.name = data.readUTF8Tiny();
+
+	}
+
+	this.blendMode = attrib & 8 ? data.readBlendMode() : "normal";
+
+	this.opacity = attrib & 16 ? data.readFloat() : 1;
+
+};
+
+SEA3D.Composite.LayerBitmap = function ( data, sea3d ) {
+
+	this.map = sea3d.getObject( data.readUInt() );
+
+	var attrib = data.readUShort();
+
+	this.channel = attrib & 1 ? data.readUByte() : 0;
+	this.repeat = attrib & 2 == 0;
+	this.offsetU = attrib & 4 ? data.readFloat() : 0;
+	this.offsetV = attrib & 8 ? data.readFloat() : 0;
+	this.scaleU = attrib & 16 ? data.readFloat() : 1;
+	this.scaleV = attrib & 32 ? data.readFloat() : 1;
+	this.rotation = attrib & 64 ? data.readFloat() : 0;
+
+	if ( attrib & 128 ) this.animation = data.readAnimationList( sea3d );
+
+};
+
+SEA3D.Composite.prototype.type = "ctex";
+
+//
+//	Planar Render
+//
+
+SEA3D.PlanarRender = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.attrib = data.readUByte();
+
+	this.quality = ( this.attrib & 1 ) | ( this.attrib & 2 );
+	this.transform = data.readMatrix();
+
+};
+
+SEA3D.PlanarRender.prototype.type = "rttp";
+
+//
+//	Cube Render
+//
+
+SEA3D.CubeRender = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.attrib = data.readUByte();
+
+	this.quality = ( this.attrib & 1 ) | ( this.attrib & 2 );
+	this.position = data.readVector3();
+
+};
+
+SEA3D.CubeRender.prototype.type = "rttc";
+
+//
+//	Cube Maps
+//
+
+SEA3D.CubeMap = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.transparent = false;
+
+	this.extension = data.readExt();
+
+	this.faces = [];
+
+	for ( var i = 0; i < 6; i ++ ) {
+
+		var size = data.readUInt();
+
+		this.faces[ i ] = data.concat( data.position, size );
+
+		data.position += size;
+
+	}
+
+};
+
+SEA3D.CubeMap.prototype.type = "cmap";
+
+//
+//	JPEG
+//
+
+SEA3D.JPEG = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.transparent = false;
+
+};
+
+SEA3D.JPEG.prototype.type = "jpg";
+
+//
+//	JPEG_XR
+//
+
+SEA3D.JPEG_XR = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.transparent = true;
+
+};
+
+SEA3D.JPEG_XR.prototype.type = "wdp";
+
+//
+//	PNG
+//
+
+SEA3D.PNG = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.transparent = data.getByte( 25 ) == 0x06;
+
+};
+
+SEA3D.PNG.prototype.type = "png";
+
+//
+//	GIF
+//
+
+SEA3D.GIF = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.transparent = data.getByte( 11 ) > 0;
+
+};
+
+SEA3D.GIF.prototype.type = "gif";
+
+//
+//	OGG
+//
+
+SEA3D.OGG = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+};
+
+SEA3D.OGG.prototype.type = "ogg";
+
+//
+//	MP3
+//
+
+SEA3D.MP3 = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+};
+
+SEA3D.MP3.prototype.type = "mp3";
+
+//
+//	Texture Update
+//
+
+SEA3D.TextureUpdate = function ( name, data, sea3d ) {
+
+	this.name = name;
+	this.data = data;
+	this.sea3d = sea3d;
+
+	this.index = data.readUInt();
+	this.bytes = data.concat( data.position, data.length - data.position );
+
+};
+
+SEA3D.TextureUpdate.prototype.type = "uTex";
+
+//
+//	FILE FORMAT
+//
+
+SEA3D.File = function ( config ) {
+
+	this.config = {
+		streaming: true,
+		timeLimit: 60,
+		progressive: false
+	};
+
+	if ( config ) {
+
+		if ( config.streaming !== undefined ) this.config.streaming = config.streaming;
+		if ( config.timeLimit !== undefined ) this.config.timeLimit = config.timeLimit;
+		if ( config.progressive !== undefined ) this.config.progressive = config.progressive;
+		if ( config.path !== undefined ) this.config.path = config.path;
+
+	}
+
+	this.version = SEA3D.VERSION;
+	this.objects = [];
+	this.typeClass = {};
+	this.typeRead = {};
+	this.typeUnique = {};
+	this.position =
+	this.dataPosition = 0;
+	this.scope = this;
+
+	// SEA3D
+	this.addClass( SEA3D.FileInfo, true );
+	this.addClass( SEA3D.Geometry, true );
+	this.addClass( SEA3D.Mesh );
+	this.addClass( SEA3D.Sprite );
+	this.addClass( SEA3D.Material );
+	this.addClass( SEA3D.Composite );
+	this.addClass( SEA3D.PointLight );
+	this.addClass( SEA3D.DirectionalLight );
+	this.addClass( SEA3D.HemisphereLight );
+	this.addClass( SEA3D.AmbientLight );
+	this.addClass( SEA3D.Animation, true );
+	this.addClass( SEA3D.Skeleton, true );
+	this.addClass( SEA3D.SkeletonLocal, true );
+	this.addClass( SEA3D.SkeletonAnimation, true );
+	this.addClass( SEA3D.UVWAnimation, true );
+	this.addClass( SEA3D.JointObject );
+	this.addClass( SEA3D.Camera );
+	this.addClass( SEA3D.OrthographicCamera );
+	this.addClass( SEA3D.Morph, true );
+	this.addClass( SEA3D.MorphAnimation, true );
+	this.addClass( SEA3D.VertexAnimation, true );
+	this.addClass( SEA3D.CubeMap, true );
+	this.addClass( SEA3D.Dummy );
+	this.addClass( SEA3D.Line );
+	this.addClass( SEA3D.SoundPoint );
+	this.addClass( SEA3D.PlanarRender );
+	this.addClass( SEA3D.CubeRender );
+	this.addClass( SEA3D.Actions );
+	this.addClass( SEA3D.Container3D );
+	this.addClass( SEA3D.Properties );
+
+	// URL BASED
+	this.addClass( SEA3D.ScriptURL, true );
+	this.addClass( SEA3D.TextureURL, true );
+	this.addClass( SEA3D.CubeMapURL, true );
+
+	// UNIVERSAL
+	this.addClass( SEA3D.JPEG, true );
+	this.addClass( SEA3D.JPEG_XR, true );
+	this.addClass( SEA3D.PNG, true );
+	this.addClass( SEA3D.GIF, true );
+	this.addClass( SEA3D.OGG, true );
+	this.addClass( SEA3D.MP3, true );
+	this.addClass( SEA3D.JavaScript, true );
+	this.addClass( SEA3D.JavaScriptMethod, true );
+	this.addClass( SEA3D.GLSL, true );
+
+	// Updaters
+	this.addClass( SEA3D.TextureUpdate, true );
+
+	// Extensions
+	var i = SEA3D.File.Extensions.length;
+
+	while ( i -- ) {
+
+		SEA3D.File.Extensions[ i ].call( this );
+
+	}
+
+};
+
+SEA3D.File.Extensions = [];
+SEA3D.File.CompressionLibs = {};
+SEA3D.File.DecompressionMethod = {};
+
+SEA3D.File.setExtension = function ( callback ) {
+
+	SEA3D.File.Extensions.push( callback );
+
+};
+
+SEA3D.File.setDecompressionEngine = function ( id, name, method ) {
+
+	SEA3D.File.CompressionLibs[ id ] = name;
+	SEA3D.File.DecompressionMethod[ id ] = method;
+
+};
+
+SEA3D.File.prototype.addClass = function ( clazz, unique ) {
+
+	this.typeClass[ clazz.prototype.type ] = clazz;
+	this.typeUnique[ clazz.prototype.type ] = unique === true;
+
+};
+
+SEA3D.File.prototype.readHead = function () {
+
+	if ( this.stream.bytesAvailable < 16 )
+		return false;
+
+	if ( this.stream.readUTF8( 3 ) != "SEA" )
+		throw new Error( "Invalid SEA3D format." );
+
+	this.sign = this.stream.readUTF8( 3 );
+
+	this.version = this.stream.readUInt24();
+
+	if ( this.stream.readUByte() != 0 ) {
+
+		throw new Error( "Protection algorithm not compatible." );
+
+	}
+
+	this.compressionID = this.stream.readUByte();
+
+	this.compressionAlgorithm = SEA3D.File.CompressionLibs[ this.compressionID ];
+	this.decompressionMethod = SEA3D.File.DecompressionMethod[ this.compressionID ];
+
+	if ( this.compressionID > 0 && ! this.decompressionMethod ) {
+
+		throw new Error( "Compression algorithm not compatible." );
+
+	}
+
+	this.length = this.stream.readUInt();
+
+	this.dataPosition = this.stream.position;
+
+	this.objects.length = 0;
+
+	this.state = this.readBody;
+
+	if ( this.onHead ) {
+
+		this.onHead( {
+			file: this,
+			sign: this.sign
+		} );
+
+	}
+
+	return true;
+
+};
+
+SEA3D.File.prototype.getObject = function ( index ) {
+
+	return this.objects[ index ];
+
+};
+
+SEA3D.File.prototype.getObjectByName = function ( name ) {
+
+	return this.objects[ name ];
+
+};
+
+SEA3D.File.prototype.readSEAObject = function () {
+
+	if ( this.stream.bytesAvailable < 4 )
+		return null;
+
+	var size = this.stream.readUInt(),
+		position = this.stream.position;
+
+	if ( this.stream.bytesAvailable < size )
+		return null;
+
+	var flag = this.stream.readUByte(),
+		type = this.stream.readExt(),
+		meta = null;
+
+	var name = flag & 1 ? this.stream.readUTF8Tiny() : "",
+		compressed = ( flag & 2 ) != 0,
+		streaming = ( flag & 4 ) != 0;
+
+	if ( flag & 8 ) {
+
+		var metalen = this.stream.readUShort();
+		var metabytes = this.stream.concat( this.stream.position, metalen );
+
+		this.stream.position += metalen;
+
+		if ( compressed && this.decompressionMethod ) {
+
+			metabytes.buffer = this.decompressionMethod( metabytes.buffer );
+
+		}
+
+		meta = metabytes.readProperties( this );
+
+	}
+
+	size -= this.stream.position - position;
+	position = this.stream.position;
+
+	var data = this.stream.concat( position, size ),
+		obj;
+
+	if ( this.typeClass[ type ] ) {
+
+		if ( compressed && this.decompressionMethod ) {
+
+			data.buffer = this.decompressionMethod( data.buffer );
+
+		}
+
+		obj = new this.typeClass[ type ]( name, data, this );
+
+		if ( ( this.config.streaming && streaming || this.config.forceStreaming ) && this.typeRead[ type ] ) {
+
+			this.typeRead[ type ].call( this.scope, obj );
+
+		}
+
+	} else {
+
+		obj = new SEA3D.Object( name, data, type, this );
+
+		console.warn( "SEA3D: Unknown format \"" + type + "\" of file \"" + name + "\". Add a module referring for this format." );
+
+	}
+
+	obj.streaming = streaming;
+	obj.metadata = meta;
+
+	this.objects.push( this.objects[ obj.name + "." + obj.type ] = obj );
+
+	this.dataPosition = position + size;
+
+	++ this.position;
+
+	return obj;
+
+};
+
+SEA3D.File.prototype.isDone = function () {
+
+	return this.position == this.length;
+
+};
+
+SEA3D.File.prototype.readBody = function () {
+
+	this.timer.update();
+
+	if ( ! this.resume ) return false;
+
+	while ( this.position < this.length ) {
+
+		if ( this.timer.deltaTime < this.config.timeLimit ) {
+
+			this.stream.position = this.dataPosition;
+
+			var sea = this.readSEAObject();
+
+			if ( sea ) this.dispatchCompleteObject( sea );
+			else return false;
+
+		} else return false;
+
+	}
+
+	this.state = this.readComplete;
+
+	return true;
+
+};
+
+SEA3D.File.prototype.initParse = function () {
+
+	this.timer = new SEA3D.Timer();
+	this.position = 0;
+	this.resume = true;
+
+};
+
+SEA3D.File.prototype.parse = function () {
+
+	this.initParse();
+
+	if ( isFinite( this.config.timeLimit ) ) requestAnimationFrame( this.parseObject.bind( this ) );
+	else this.parseObject();
+
+};
+
+SEA3D.File.prototype.parseObject = function () {
+
+	this.timer.update();
+
+	while ( this.position < this.length && this.timer.deltaTime < this.config.timeLimit ) {
+
+		var obj = this.objects[ this.position ++ ],
+			type = obj.type;
+
+		if ( ! this.typeUnique[ type ] ) delete obj.tag;
+
+		if ( ( obj.streaming || this.config.forceStreaming ) && this.typeRead[ type ] ) {
+
+			if ( obj.tag == undefined ) {
+
+				this.typeRead[ type ].call( this.scope, obj );
+
+			}
+
+		}
+
+	}
+
+	if ( this.position == this.length ) {
+
+		var elapsedTime = this.timer.elapsedTime;
+		var message = elapsedTime + "ms, " + this.objects.length + " objects";
+
+		if ( this.onParseComplete ) {
+
+			this.onParseComplete( {
+				file: this,
+				timeTotal: elapsedTime,
+				message: message
+			} );
+
+		} else console.log( "SEA3D Parse Complete:", message );
+
+	} else {
+
+		if ( this.onParseProgress ) {
+
+			this.onParseProgress( {
+				file: this,
+				loaded: this.position,
+				total: this.length
+			} );
+
+		}
+
+		setTimeout( this.parseObject.bind( this ), 10 );
+
+	}
+
+};
+
+SEA3D.File.prototype.readComplete = function () {
+
+	this.stream.position = this.dataPosition;
+
+	if ( this.stream.readUInt24F() != 0x5EA3D1 )
+		console.warn( "SEA3D file is corrupted." );
+
+	delete this.state;
+
+	return false;
+
+};
+
+SEA3D.File.prototype.readState = function () {
+
+	while ( this.state() ) continue;
+
+	if ( this.state ) {
+
+		requestAnimationFrame( this.readState.bind( this ) );
+
+		this.dispatchProgress();
+
+	} else {
+
+		this.dispatchComplete();
+
+	}
+
+};
+
+SEA3D.File.prototype.append = function( buffer ) {
+
+	if (this.state) {
+
+		this.stream.append( buffer );
+
+	} else {
+
+		this.read( buffer );
+
+	}
+
+};
+
+SEA3D.File.prototype.read = function ( buffer ) {
+
+	if ( ! buffer ) throw new Error( "No data found." );
+
+	this.initParse();
+
+	this.stream = new SEA3D.Stream( buffer );
+	this.state = this.readHead;
+
+	this.readState();
+
+};
+
+SEA3D.File.prototype.dispatchCompleteObject = function ( obj ) {
+
+	if ( ! this.onCompleteObject ) return;
+
+	this.onCompleteObject( {
+		file: this,
+		object: obj
+	} );
+
+};
+
+SEA3D.File.prototype.dispatchProgress = function () {
+
+	if ( ! this.onProgress ) return;
+
+	this.onProgress( {
+		file: this,
+		loaded: this.position,
+		total: this.length
+	} );
+
+};
+
+SEA3D.File.prototype.dispatchDownloadProgress = function ( position, length ) {
+
+	if ( ! this.onDownloadProgress ) return;
+
+	this.onDownloadProgress( {
+		file: this,
+		loaded: position,
+		total: length
+	} );
+
+};
+
+SEA3D.File.prototype.dispatchComplete = function () {
+
+	var elapsedTime = this.timer.elapsedTime;
+	var message = elapsedTime + "ms, " + this.objects.length + " objects";
+
+	if ( this.onComplete ) this.onComplete( {
+		file: this,
+		timeTotal: elapsedTime,
+		message: message
+	} );
+	else console.log( "SEA3D:", message );
+
+};
+
+SEA3D.File.prototype.dispatchError = function ( id, message ) {
+
+	if ( this.onError ) this.onError( { file: this, id: id, message: message } );
+	else console.error( "SEA3D: #" + id, message );
+
+};
+
+SEA3D.File.prototype.load = function ( url ) {
+
+	var self = this,
+		xhr = new XMLHttpRequest();
+
+	xhr.open( "GET", url, true );
+
+	if (!this.config.path) {
+
+//		this.config.path = THREE.Loader.prototype.extractUrlBase( url );
+		this.config.path = THREE.LoaderUtils.extractUrlBase( url );
+
+	}
+
+	if ( self.config.progressive ) {
+
+		var position = 0;
+
+		xhr.overrideMimeType( 'text/plain; charset=x-user-defined' );
+
+	} else {
+
+		xhr.responseType = 'arraybuffer';
+
+	}
+
+	xhr.onprogress = function ( e ) {
+
+		if ( self.config.progressive ) {
+
+			var binStr = xhr.responseText.substring( position ),
+				bytes = new Uint8Array( binStr.length );
+
+			for ( var i = 0; i < binStr.length; i ++ ) {
+
+				bytes[ i ] = binStr.charCodeAt( i ) & 0xFF;
+
+			}
+
+			position += binStr.length;
+
+			self.append( bytes.buffer );
+
+		}
+
+		self.dispatchDownloadProgress( e.loaded, e.total );
+
+	};
+
+	if ( ! self.config.progressive ) {
+
+		xhr.onreadystatechange = function () {
+
+			if ( xhr.readyState === 4 ) {
+
+				if ( xhr.status === 200 || xhr.status === 0 ) {
+
+					self.read( this.response );
+
+				} else {
+
+					this.dispatchError( 1001, "Couldn't load [" + url + "] [" + xhr.status + "]" );
+
+				}
+
+			}
+
+		};
+
+	}
+
+	xhr.send();
+
+};

+ 774 - 774
examples/js/loaders/sea3d/SEA3DLegacy.js

@@ -1,774 +1,774 @@
-/**
- * 	SEA3D Legacy for Three.JS
- * 	@author Sunag / http://www.sunag.com.br/
- */
-
-'use strict';
-
-//
-//	Header
-//
-
-Object.assign( THREE.SEA3D.prototype, {
-
-	_onHead: THREE.SEA3D.prototype.onHead,
-	_updateTransform: THREE.SEA3D.prototype.updateTransform,
-	_readMorph: THREE.SEA3D.prototype.readMorph,
-	_readVertexAnimation: THREE.SEA3D.prototype.readVertexAnimation,
-	_readGeometryBuffer: THREE.SEA3D.prototype.readGeometryBuffer,
-	_readLine: THREE.SEA3D.prototype.readLine,
-	_getModifier: THREE.SEA3D.prototype.getModifier,
-	_readAnimation: THREE.SEA3D.prototype.readAnimation
-
-} );
-
-//
-//	Utils
-//
-
-THREE.SEA3D.prototype.isLegacy = function ( sea ) {
-
-	var sea3d = sea.sea3d;
-
-	if ( sea3d.sign === "S3D" ) {
-
-		return sea3d.config.legacy;
-
-	}
-
-	return false;
-
-};
-
-THREE.SEA3D.prototype.flipVec3 = function ( v ) {
-
-	if ( ! v ) return;
-
-	var i = 2;
-
-	while ( i < v.length ) {
-
-		v[ i ] = - v[ i ];
-
-		i += 3;
-
-	}
-
-	return v;
-
-};
-
-THREE.SEA3D.prototype.addVector = function ( v, t ) {
-
-	if ( ! v ) return;
-
-	for ( var i = 0; i < v.length; i ++ ) {
-
-		v[ i ] += t[ i ];
-
-	}
-
-	return v;
-
-};
-
-THREE.SEA3D.prototype.expandJoints = function ( sea ) {
-
-	var numJoints = sea.numVertex * 4;
-
-	var joint = sea.isBig ? new Uint32Array( numJoints ) : new Uint16Array( numJoints );
-	var weight = new Float32Array( numJoints );
-
-	var w = 0, jpv = sea.jointPerVertex;
-
-	for ( var i = 0; i < sea.numVertex; i ++ ) {
-
-		var tjsIndex = i * 4;
-		var seaIndex = i * jpv;
-
-		joint[ tjsIndex ] = sea.joint[ seaIndex ];
-		if ( jpv > 1 ) joint[ tjsIndex + 1 ] = sea.joint[ seaIndex + 1 ];
-		if ( jpv > 2 ) joint[ tjsIndex + 2 ] = sea.joint[ seaIndex + 2 ];
-		if ( jpv > 3 ) joint[ tjsIndex + 3 ] = sea.joint[ seaIndex + 3 ];
-
-		weight[ tjsIndex ] = sea.weight[ seaIndex ];
-		if ( jpv > 1 ) weight[ tjsIndex + 1 ] = sea.weight[ seaIndex + 1 ];
-		if ( jpv > 2 ) weight[ tjsIndex + 2 ] = sea.weight[ seaIndex + 2 ];
-		if ( jpv > 3 ) weight[ tjsIndex + 3 ] = sea.weight[ seaIndex + 3 ];
-
-		w = weight[ tjsIndex ] + weight[ tjsIndex + 1 ] + weight[ tjsIndex + 2 ] + weight[ tjsIndex + 3 ];
-
-		weight[ tjsIndex ] += 1 - w;
-
-	}
-
-	sea.joint = joint;
-	sea.weight = weight;
-
-	sea.jointPerVertex = 4;
-
-};
-
-THREE.SEA3D.prototype.compressJoints = function ( sea ) {
-
-	var numJoints = sea.numVertex * 4;
-
-	var joint = sea.isBig ? new Uint32Array( numJoints ) : new Uint16Array( numJoints );
-	var weight = new Float32Array( numJoints );
-
-	var w = 0, jpv = sea.jointPerVertex;
-
-	for ( var i = 0; i < sea.numVertex; i ++ ) {
-
-		var tjsIndex = i * 4;
-		var seaIndex = i * jpv;
-
-		joint[ tjsIndex ] = sea.joint[ seaIndex ];
-		joint[ tjsIndex + 1 ] = sea.joint[ seaIndex + 1 ];
-		joint[ tjsIndex + 2 ] = sea.joint[ seaIndex + 2 ];
-		joint[ tjsIndex + 3 ] = sea.joint[ seaIndex + 3 ];
-
-		weight[ tjsIndex ] = sea.weight[ seaIndex ];
-		weight[ tjsIndex + 1 ] = sea.weight[ seaIndex + 1 ];
-		weight[ tjsIndex + 2 ] = sea.weight[ seaIndex + 2 ];
-		weight[ tjsIndex + 3 ] = sea.weight[ seaIndex + 3 ];
-
-		w = weight[ tjsIndex ] + weight[ tjsIndex + 1 ] + weight[ tjsIndex + 2 ] + weight[ tjsIndex + 3 ];
-
-		weight[ tjsIndex ] += 1 - w;
-
-	}
-
-	sea.joint = joint;
-	sea.weight = weight;
-
-	sea.jointPerVertex = 4;
-
-};
-
-THREE.SEA3D.prototype.flipIndexes = function ( v ) {
-
-	var i = 1; // y >-< z
-
-	while ( i < v.length ) {
-
-		var idx = v[ i + 1 ];
-		v[ i + 1 ] = v[ i ];
-		v[ i ] = idx;
-
-		i += 3;
-
-	}
-
-	return v;
-
-};
-
-THREE.SEA3D.prototype.flipBoneMatrix = function () {
-
-	var zero = new THREE.Vector3();
-
-	return function ( mtx ) {
-
-		var pos = THREE.SEA3D.VECBUF.setFromMatrixPosition( mtx );
-		pos.z = - pos.z;
-
-		mtx.setPosition( zero );
-		mtx.multiplyMatrices( THREE.SEA3D.MTXBUF.makeRotationZ( THREE.Math.degToRad( 180 ) ), mtx );
-		mtx.setPosition( pos );
-
-		return mtx;
-
-	};
-
-}();
-
-THREE.SEA3D.prototype.flipScaleMatrix = function () {
-
-	var pos = new THREE.Vector3();
-	var qua = new THREE.Quaternion();
-	var slc = new THREE.Vector3();
-
-	return function ( local, rotate, parent, parentRotate ) {
-
-		if ( parent ) local.multiplyMatrices( parent, local );
-
-		local.decompose( pos, qua, slc );
-
-		slc.z = - slc.z;
-
-		local.compose( pos, qua, slc );
-
-		if ( rotate ) {
-
-			local.multiplyMatrices( local, THREE.SEA3D.MTXBUF.makeRotationZ( THREE.Math.degToRad( 180 ) ) );
-
-		}
-
-		if ( parent ) {
-
-			parent = parent.clone();
-
-			this.flipScaleMatrix( parent, parentRotate );
-
-			local.multiplyMatrices( parent.getInverse( parent ), local );
-
-		}
-
-		return local;
-
-	};
-
-}();
-
-//
-//	Legacy
-//
-
-THREE.SEA3D.prototype.flipDefaultAnimation = function () {
-
-	var buf1 = new THREE.Matrix4();
-	var buf2 = new THREE.Matrix4();
-
-	var pos = new THREE.Vector3();
-	var qua = new THREE.Quaternion();
-	var slc = new THREE.Vector3();
-
-	var to_pos = new THREE.Vector3();
-	var to_qua = new THREE.Quaternion();
-	var to_slc = new THREE.Vector3();
-
-	return function ( animation, obj3d, relative ) {
-
-		if ( animation.isFliped ) return;
-
-		var dataList = animation.dataList,
-			t_anm = [];
-
-		for ( var i = 0; i < dataList.length; i ++ ) {
-
-			var data = dataList[ i ],
-				raw = data.data,
-				kind = data.kind,
-				numFrames = raw.length / data.blockSize;
-
-			switch ( kind ) {
-
-				case SEA3D.Animation.POSITION:
-				case SEA3D.Animation.ROTATION:
-				case SEA3D.Animation.SCALE:
-
-					t_anm.push( {
-						kind: kind,
-						numFrames: numFrames,
-						raw: raw
-					} );
-
-					break;
-
-			}
-
-		}
-
-		if ( t_anm.length > 0 ) {
-
-			var numFrames = t_anm[ 0 ].numFrames,
-				parent = undefined;
-
-			if ( relative ) {
-
-				buf1.identity();
-				parent = this.flipScaleMatrix( buf2.copy( obj3d.matrixWorld ) );
-
-			} else {
-
-				if ( obj3d.parent ) {
-
-					parent = this.flipScaleMatrix( buf2.copy( obj3d.parent.matrixWorld ) );
-
-				}
-
-				this.flipScaleMatrix( buf1.copy( obj3d.matrix ), false, parent );
-
-			}
-
-			buf1.decompose( pos, qua, slc );
-
-			for ( var f = 0, t, c; f < numFrames; f ++ ) {
-
-				for ( t = 0; t < t_anm.length; t ++ ) {
-
-					var raw = t_anm[ t ].raw,
-						kind = t_anm[ t ].kind;
-
-					switch ( kind ) {
-
-						case SEA3D.Animation.POSITION:
-
-							c = f * 3;
-
-							pos.set(
-								raw[ c ],
-								raw[ c + 1 ],
-								raw[ c + 2 ]
-							);
-
-							break;
-
-						case SEA3D.Animation.ROTATION:
-
-							c = f * 4;
-
-							qua.set(
-								raw[ c ],
-								raw[ c + 1 ],
-								raw[ c + 2 ],
-								raw[ c + 3 ]
-							);
-
-							break;
-
-						case SEA3D.Animation.SCALE:
-
-							c = f * 4;
-
-							slc.set(
-								raw[ c ],
-								raw[ c + 1 ],
-								raw[ c + 2 ]
-							);
-
-							break;
-
-					}
-
-				}
-
-				buf1.compose( pos, qua, slc );
-
-				this.flipScaleMatrix( buf1, false, buf2 );
-
-				buf1.decompose( to_pos, to_qua, to_slc );
-
-				for ( t = 0; t < t_anm.length; t ++ ) {
-
-					var raw = t_anm[ t ].raw,
-						kind = t_anm[ t ].kind;
-
-					switch ( kind ) {
-
-						case SEA3D.Animation.POSITION:
-
-							c = f * 3;
-
-							raw[ c ] = to_pos.x;
-							raw[ c + 1 ] = to_pos.y;
-							raw[ c + 2 ] = to_pos.z;
-
-							break;
-
-						case SEA3D.Animation.ROTATION:
-
-							c = f * 4;
-
-							raw[ c ] = to_qua.x;
-							raw[ c + 1 ] = to_qua.y;
-							raw[ c + 2 ] = to_qua.z;
-							raw[ c + 3 ] = to_qua.w;
-
-							break;
-
-						case SEA3D.Animation.SCALE:
-
-							c = f * 3;
-
-							raw[ c ] = to_slc.x;
-							raw[ c + 1 ] = to_slc.y;
-							raw[ c + 2 ] = to_slc.z;
-
-							break;
-
-					}
-
-				}
-
-			}
-
-		}
-
-		animation.isFliped = true;
-
-	};
-
-}();
-
-THREE.SEA3D.prototype.readAnimation = function ( sea ) {
-
-	if ( ! this.isLegacy( sea ) ) {
-
-		this._readAnimation( sea );
-
-	}
-
-};
-
-THREE.SEA3D.prototype.getModifier = function ( req ) {
-
-	var sea = req.sea;
-
-	if ( this.isLegacy( sea ) && ! sea.done ) {
-
-		sea.done = true;
-
-		switch ( sea.type ) {
-
-			case SEA3D.SkeletonAnimation.prototype.type:
-
-				this.readSkeletonAnimationLegacy( sea, req.skeleton );
-
-				return sea.tag;
-
-				break;
-
-			case SEA3D.Animation.prototype.type:
-			case SEA3D.MorphAnimation.prototype.type:
-			case SEA3D.UVWAnimation.prototype.type:
-
-				if ( req.scope instanceof THREE.Object3D ) {
-
-					this.flipDefaultAnimation( sea, req.scope, req.relative );
-
-				}
-
-				this._readAnimation( sea );
-
-				return sea.tag;
-
-				break;
-
-			case SEA3D.Morph.prototype.type:
-
-				this.readMorphLegacy( sea, req.geometry );
-
-				break;
-
-		}
-
-	}
-
-	return this._getModifier( req );
-
-};
-
-THREE.SEA3D.prototype.updateTransform = function () {
-
-	var buf1 = new THREE.Matrix4();
-	var identity = new THREE.Matrix4();
-
-	return function ( obj3d, sea ) {
-
-		if ( this.isLegacy( sea ) ) {
-
-			if ( sea.transform ) buf1.fromArray( sea.transform );
-			else buf1.makeTranslation( sea.position.x, sea.position.y, sea.position.z );
-
-			this.flipScaleMatrix(
-				buf1, false,
-				obj3d.parent ? obj3d.parent.matrixWorld : identity,
-				obj3d.parent instanceof THREE.Bone
-			);
-
-			obj3d.position.setFromMatrixPosition( buf1 );
-			obj3d.scale.setFromMatrixScale( buf1 );
-
-			// ignore rotation scale
-
-			buf1.scale( THREE.SEA3D.VECBUF.set( 1 / obj3d.scale.x, 1 / obj3d.scale.y, 1 / obj3d.scale.z ) );
-			obj3d.rotation.setFromRotationMatrix( buf1 );
-
-			obj3d.updateMatrixWorld();
-
-		} else {
-
-			this._updateTransform( obj3d, sea );
-
-		}
-
-	};
-
-}();
-
-THREE.SEA3D.prototype.readSkeleton = function () {
-
-	var mtx_tmp_inv = new THREE.Matrix4(),
-		mtx_local = new THREE.Matrix4(),
-		mtx_parent = new THREE.Matrix4(),
-		pos = new THREE.Vector3(),
-		qua = new THREE.Quaternion();
-
-	return function ( sea ) {
-
-		var bones = [],
-			isLegacy = sea.sea3d.config.legacy;
-
-		for ( var i = 0; i < sea.joint.length; i ++ ) {
-
-			var bone = sea.joint[ i ];
-
-			// get world inverse matrix
-
-			mtx_tmp_inv.fromArray( bone.inverseBindMatrix );
-
-			// convert to world matrix
-
-			mtx_local.getInverse( mtx_tmp_inv );
-
-			// convert to three.js order
-
-			if ( isLegacy ) this.flipBoneMatrix( mtx_local );
-
-			if ( bone.parentIndex > - 1 ) {
-
-				// to world
-
-				mtx_tmp_inv.fromArray( sea.joint[ bone.parentIndex ].inverseBindMatrix );
-				mtx_parent.getInverse( mtx_tmp_inv );
-
-				// convert parent to three.js order
-
-				if ( isLegacy ) this.flipBoneMatrix( mtx_parent );
-
-				// to local
-
-				mtx_parent.getInverse( mtx_parent );
-
-				mtx_local.multiplyMatrices( mtx_parent, mtx_local );
-
-			}
-
-			// apply matrix
-
-			pos.setFromMatrixPosition( mtx_local );
-			qua.setFromRotationMatrix( mtx_local );
-
-			bones[ i ] = {
-				name: bone.name,
-				pos: [ pos.x, pos.y, pos.z ],
-				rotq: [ qua.x, qua.y, qua.z, qua.w ],
-				parent: bone.parentIndex
-			};
-
-		}
-
-		this.domain.bones = this.bones = this.bones || [];
-		this.bones.push( this.objects[ sea.name + '.sklq' ] = sea.tag = bones );
-
-		return bones;
-
-	};
-
-}();
-
-THREE.SEA3D.prototype.readSkeletonAnimationLegacy = function () {
-
-	var mtx_tmp_inv = new THREE.Matrix4(),
-		mtx_local = new THREE.Matrix4(),
-		mtx_global = new THREE.Matrix4(),
-		mtx_parent = new THREE.Matrix4();
-
-	return function ( sea, skl ) {
-
-		if ( sea.tag ) return sea.tag;
-
-		var animations = [],
-			delta = ( 1000 / sea.frameRate ) / 1000,
-			scale = [ 1, 1, 1 ];
-
-		for ( var i = 0; i < sea.sequence.length; i ++ ) {
-
-			var seq = sea.sequence[ i ];
-
-			var start = seq.start;
-			var end = start + seq.count;
-
-			var animation = {
-				name: seq.name,
-				repeat: seq.repeat,
-				fps: sea.frameRate,
-				JIT: 0,
-				length: delta * seq.count,
-				hierarchy: []
-			};
-
-			var numJoints = sea.numJoints,
-				raw = sea.raw;
-
-			for ( var j = 0; j < numJoints; j ++ ) {
-
-				var bone = skl.joint[ j ],
-					node = { parent: bone.parentIndex, keys: [] },
-					keys = node.keys,
-					time = 0;
-
-				for ( var frame = start; frame < end; frame ++ ) {
-
-					var idx = ( frame * numJoints * 7 ) + ( j * 7 );
-
-					mtx_local.makeRotationFromQuaternion( THREE.SEA3D.QUABUF.set( raw[ idx + 3 ], raw[ idx + 4 ], raw[ idx + 5 ], raw[ idx + 6 ] ) );
-					mtx_local.setPosition( THREE.SEA3D.VECBUF.set( raw[ idx ], raw[ idx + 1 ], raw[ idx + 2 ] ) );
-
-					if ( bone.parentIndex > - 1 ) {
-
-						// to global
-
-						mtx_tmp_inv.fromArray( skl.joint[ bone.parentIndex ].inverseBindMatrix );
-
-						mtx_parent.getInverse( mtx_tmp_inv );
-
-						mtx_global.multiplyMatrices( mtx_parent, mtx_local );
-
-						// convert to three.js matrix
-
-						this.flipBoneMatrix( mtx_global );
-
-						// flip parent inverse
-
-						this.flipBoneMatrix( mtx_parent );
-
-						// to local
-
-						mtx_parent.getInverse( mtx_parent );
-
-						mtx_local.multiplyMatrices( mtx_parent, mtx_global );
-
-					} else {
-
-						this.flipBoneMatrix( mtx_local );
-
-					}
-
-					var posQ = THREE.SEA3D.VECBUF.setFromMatrixPosition( mtx_local );
-					var newQ = THREE.SEA3D.QUABUF.setFromRotationMatrix( mtx_local );
-
-					keys.push( {
-						time: time,
-						pos: [ posQ.x, posQ.y, posQ.z ],
-						rot: [ newQ.x, newQ.y, newQ.z, newQ.w ],
-						scl: scale
-					} );
-
-					time += delta;
-
-				}
-
-				animation.hierarchy[ j ] = node;
-
-			}
-
-			animations.push( THREE.SEA3D.AnimationClip.fromClip( THREE.AnimationClip.parseAnimation( animation, skl.tag ), seq.repeat ) );
-
-		}
-
-		this.domain.clips = this.clips = this.clips || [];
-		this.clips.push( this.objects[ sea.name + '.anm' ] = sea.tag = animations );
-
-	};
-
-}();
-
-THREE.SEA3D.prototype.readMorphLegacy = function ( sea, geo ) {
-
-	for ( var i = 0; i < sea.node.length; i ++ ) {
-
-		var node = sea.node[ i ];
-
-		this.flipVec3( node.vertex );
-		this.flipVec3( node.normal );
-
-		this.addVector( node.vertex, geo.vertex );
-		this.addVector( node.normal, geo.normal );
-
-	}
-
-	this._readMorph( sea );
-
-};
-
-THREE.SEA3D.prototype.readMorph = function ( sea ) {
-
-	if ( ! this.isLegacy( sea ) ) {
-
-		this._readMorph( sea );
-
-	}
-
-};
-
-THREE.SEA3D.prototype.readVertexAnimation = function ( sea ) {
-
-	if ( this.isLegacy( sea ) ) {
-
-		for ( var i = 0, l = sea.frame.length; i < l; i ++ ) {
-
-			var frame = sea.frame[ i ];
-
-			this.flipVec3( frame.vertex );
-			this.flipVec3( frame.normal );
-
-		}
-
-	}
-
-	this._readVertexAnimation( sea );
-
-};
-
-THREE.SEA3D.prototype.readGeometryBuffer = function ( sea ) {
-
-	if ( this.isLegacy( sea ) ) {
-
-		this.flipVec3( sea.vertex );
-		this.flipVec3( sea.normal );
-
-		this.flipIndexes( sea.indexes );
-
-		if ( sea.jointPerVertex > 4 ) this.compressJoints( sea );
-		else if ( sea.jointPerVertex < 4 ) this.expandJoints( sea );
-
-	}
-
-	this._readGeometryBuffer( sea );
-
-};
-
-THREE.SEA3D.prototype.readLines = function ( sea ) {
-
-	if ( this.isLegacy( sea ) ) {
-
-		this.flipVec3( sea.vertex );
-
-	}
-
-	this._readLines( sea );
-
-};
-
-THREE.SEA3D.prototype.onHead = function ( args ) {
-
-	if ( args.sign != "S3D" && args.sign != "TJS" ) {
-
-		throw new Error( "Sign '" + args.sign + "' unknown." );
-
-	}
-
-};
-
-THREE.SEA3D.EXTENSIONS_LOADER.push( { setTypeRead: function () {
-
-	// CONFIG
-
-	this.config.legacy = this.config.legacy == undefined ? true : this.config.legacy;
-
-	this.file.typeRead[ SEA3D.Skeleton.prototype.type ] = this.readSkeleton;
-
-} } );
+/**
+ * 	SEA3D Legacy for Three.JS
+ * 	@author Sunag / http://www.sunag.com.br/
+ */
+
+'use strict';
+
+//
+//	Header
+//
+
+Object.assign( THREE.SEA3D.prototype, {
+
+	_onHead: THREE.SEA3D.prototype.onHead,
+	_updateTransform: THREE.SEA3D.prototype.updateTransform,
+	_readMorph: THREE.SEA3D.prototype.readMorph,
+	_readVertexAnimation: THREE.SEA3D.prototype.readVertexAnimation,
+	_readGeometryBuffer: THREE.SEA3D.prototype.readGeometryBuffer,
+	_readLine: THREE.SEA3D.prototype.readLine,
+	_getModifier: THREE.SEA3D.prototype.getModifier,
+	_readAnimation: THREE.SEA3D.prototype.readAnimation
+
+} );
+
+//
+//	Utils
+//
+
+THREE.SEA3D.prototype.isLegacy = function ( sea ) {
+
+	var sea3d = sea.sea3d;
+
+	if ( sea3d.sign === "S3D" ) {
+
+		return sea3d.config.legacy;
+
+	}
+
+	return false;
+
+};
+
+THREE.SEA3D.prototype.flipVec3 = function ( v ) {
+
+	if ( ! v ) return;
+
+	var i = 2;
+
+	while ( i < v.length ) {
+
+		v[ i ] = - v[ i ];
+
+		i += 3;
+
+	}
+
+	return v;
+
+};
+
+THREE.SEA3D.prototype.addVector = function ( v, t ) {
+
+	if ( ! v ) return;
+
+	for ( var i = 0; i < v.length; i ++ ) {
+
+		v[ i ] += t[ i ];
+
+	}
+
+	return v;
+
+};
+
+THREE.SEA3D.prototype.expandJoints = function ( sea ) {
+
+	var numJoints = sea.numVertex * 4;
+
+	var joint = sea.isBig ? new Uint32Array( numJoints ) : new Uint16Array( numJoints );
+	var weight = new Float32Array( numJoints );
+
+	var w = 0, jpv = sea.jointPerVertex;
+
+	for ( var i = 0; i < sea.numVertex; i ++ ) {
+
+		var tjsIndex = i * 4;
+		var seaIndex = i * jpv;
+
+		joint[ tjsIndex ] = sea.joint[ seaIndex ];
+		if ( jpv > 1 ) joint[ tjsIndex + 1 ] = sea.joint[ seaIndex + 1 ];
+		if ( jpv > 2 ) joint[ tjsIndex + 2 ] = sea.joint[ seaIndex + 2 ];
+		if ( jpv > 3 ) joint[ tjsIndex + 3 ] = sea.joint[ seaIndex + 3 ];
+
+		weight[ tjsIndex ] = sea.weight[ seaIndex ];
+		if ( jpv > 1 ) weight[ tjsIndex + 1 ] = sea.weight[ seaIndex + 1 ];
+		if ( jpv > 2 ) weight[ tjsIndex + 2 ] = sea.weight[ seaIndex + 2 ];
+		if ( jpv > 3 ) weight[ tjsIndex + 3 ] = sea.weight[ seaIndex + 3 ];
+
+		w = weight[ tjsIndex ] + weight[ tjsIndex + 1 ] + weight[ tjsIndex + 2 ] + weight[ tjsIndex + 3 ];
+
+		weight[ tjsIndex ] += 1 - w;
+
+	}
+
+	sea.joint = joint;
+	sea.weight = weight;
+
+	sea.jointPerVertex = 4;
+
+};
+
+THREE.SEA3D.prototype.compressJoints = function ( sea ) {
+
+	var numJoints = sea.numVertex * 4;
+
+	var joint = sea.isBig ? new Uint32Array( numJoints ) : new Uint16Array( numJoints );
+	var weight = new Float32Array( numJoints );
+
+	var w = 0, jpv = sea.jointPerVertex;
+
+	for ( var i = 0; i < sea.numVertex; i ++ ) {
+
+		var tjsIndex = i * 4;
+		var seaIndex = i * jpv;
+
+		joint[ tjsIndex ] = sea.joint[ seaIndex ];
+		joint[ tjsIndex + 1 ] = sea.joint[ seaIndex + 1 ];
+		joint[ tjsIndex + 2 ] = sea.joint[ seaIndex + 2 ];
+		joint[ tjsIndex + 3 ] = sea.joint[ seaIndex + 3 ];
+
+		weight[ tjsIndex ] = sea.weight[ seaIndex ];
+		weight[ tjsIndex + 1 ] = sea.weight[ seaIndex + 1 ];
+		weight[ tjsIndex + 2 ] = sea.weight[ seaIndex + 2 ];
+		weight[ tjsIndex + 3 ] = sea.weight[ seaIndex + 3 ];
+
+		w = weight[ tjsIndex ] + weight[ tjsIndex + 1 ] + weight[ tjsIndex + 2 ] + weight[ tjsIndex + 3 ];
+
+		weight[ tjsIndex ] += 1 - w;
+
+	}
+
+	sea.joint = joint;
+	sea.weight = weight;
+
+	sea.jointPerVertex = 4;
+
+};
+
+THREE.SEA3D.prototype.flipIndexes = function ( v ) {
+
+	var i = 1; // y >-< z
+
+	while ( i < v.length ) {
+
+		var idx = v[ i + 1 ];
+		v[ i + 1 ] = v[ i ];
+		v[ i ] = idx;
+
+		i += 3;
+
+	}
+
+	return v;
+
+};
+
+THREE.SEA3D.prototype.flipBoneMatrix = function () {
+
+	var zero = new THREE.Vector3();
+
+	return function ( mtx ) {
+
+		var pos = THREE.SEA3D.VECBUF.setFromMatrixPosition( mtx );
+		pos.z = - pos.z;
+
+		mtx.setPosition( zero );
+		mtx.multiplyMatrices( THREE.SEA3D.MTXBUF.makeRotationZ( THREE.Math.degToRad( 180 ) ), mtx );
+		mtx.setPosition( pos );
+
+		return mtx;
+
+	};
+
+}();
+
+THREE.SEA3D.prototype.flipScaleMatrix = function () {
+
+	var pos = new THREE.Vector3();
+	var qua = new THREE.Quaternion();
+	var slc = new THREE.Vector3();
+
+	return function ( local, rotate, parent, parentRotate ) {
+
+		if ( parent ) local.multiplyMatrices( parent, local );
+
+		local.decompose( pos, qua, slc );
+
+		slc.z = - slc.z;
+
+		local.compose( pos, qua, slc );
+
+		if ( rotate ) {
+
+			local.multiplyMatrices( local, THREE.SEA3D.MTXBUF.makeRotationZ( THREE.Math.degToRad( 180 ) ) );
+
+		}
+
+		if ( parent ) {
+
+			parent = parent.clone();
+
+			this.flipScaleMatrix( parent, parentRotate );
+
+			local.multiplyMatrices( parent.getInverse( parent ), local );
+
+		}
+
+		return local;
+
+	};
+
+}();
+
+//
+//	Legacy
+//
+
+THREE.SEA3D.prototype.flipDefaultAnimation = function () {
+
+	var buf1 = new THREE.Matrix4();
+	var buf2 = new THREE.Matrix4();
+
+	var pos = new THREE.Vector3();
+	var qua = new THREE.Quaternion();
+	var slc = new THREE.Vector3();
+
+	var to_pos = new THREE.Vector3();
+	var to_qua = new THREE.Quaternion();
+	var to_slc = new THREE.Vector3();
+
+	return function ( animation, obj3d, relative ) {
+
+		if ( animation.isFliped ) return;
+
+		var dataList = animation.dataList,
+			t_anm = [];
+
+		for ( var i = 0; i < dataList.length; i ++ ) {
+
+			var data = dataList[ i ],
+				raw = data.data,
+				kind = data.kind,
+				numFrames = raw.length / data.blockSize;
+
+			switch ( kind ) {
+
+				case SEA3D.Animation.POSITION:
+				case SEA3D.Animation.ROTATION:
+				case SEA3D.Animation.SCALE:
+
+					t_anm.push( {
+						kind: kind,
+						numFrames: numFrames,
+						raw: raw
+					} );
+
+					break;
+
+			}
+
+		}
+
+		if ( t_anm.length > 0 ) {
+
+			var numFrames = t_anm[ 0 ].numFrames,
+				parent = undefined;
+
+			if ( relative ) {
+
+				buf1.identity();
+				parent = this.flipScaleMatrix( buf2.copy( obj3d.matrixWorld ) );
+
+			} else {
+
+				if ( obj3d.parent ) {
+
+					parent = this.flipScaleMatrix( buf2.copy( obj3d.parent.matrixWorld ) );
+
+				}
+
+				this.flipScaleMatrix( buf1.copy( obj3d.matrix ), false, parent );
+
+			}
+
+			buf1.decompose( pos, qua, slc );
+
+			for ( var f = 0, t, c; f < numFrames; f ++ ) {
+
+				for ( t = 0; t < t_anm.length; t ++ ) {
+
+					var raw = t_anm[ t ].raw,
+						kind = t_anm[ t ].kind;
+
+					switch ( kind ) {
+
+						case SEA3D.Animation.POSITION:
+
+							c = f * 3;
+
+							pos.set(
+								raw[ c ],
+								raw[ c + 1 ],
+								raw[ c + 2 ]
+							);
+
+							break;
+
+						case SEA3D.Animation.ROTATION:
+
+							c = f * 4;
+
+							qua.set(
+								raw[ c ],
+								raw[ c + 1 ],
+								raw[ c + 2 ],
+								raw[ c + 3 ]
+							);
+
+							break;
+
+						case SEA3D.Animation.SCALE:
+
+							c = f * 4;
+
+							slc.set(
+								raw[ c ],
+								raw[ c + 1 ],
+								raw[ c + 2 ]
+							);
+
+							break;
+
+					}
+
+				}
+
+				buf1.compose( pos, qua, slc );
+
+				this.flipScaleMatrix( buf1, false, buf2 );
+
+				buf1.decompose( to_pos, to_qua, to_slc );
+
+				for ( t = 0; t < t_anm.length; t ++ ) {
+
+					var raw = t_anm[ t ].raw,
+						kind = t_anm[ t ].kind;
+
+					switch ( kind ) {
+
+						case SEA3D.Animation.POSITION:
+
+							c = f * 3;
+
+							raw[ c ] = to_pos.x;
+							raw[ c + 1 ] = to_pos.y;
+							raw[ c + 2 ] = to_pos.z;
+
+							break;
+
+						case SEA3D.Animation.ROTATION:
+
+							c = f * 4;
+
+							raw[ c ] = to_qua.x;
+							raw[ c + 1 ] = to_qua.y;
+							raw[ c + 2 ] = to_qua.z;
+							raw[ c + 3 ] = to_qua.w;
+
+							break;
+
+						case SEA3D.Animation.SCALE:
+
+							c = f * 3;
+
+							raw[ c ] = to_slc.x;
+							raw[ c + 1 ] = to_slc.y;
+							raw[ c + 2 ] = to_slc.z;
+
+							break;
+
+					}
+
+				}
+
+			}
+
+		}
+
+		animation.isFliped = true;
+
+	};
+
+}();
+
+THREE.SEA3D.prototype.readAnimation = function ( sea ) {
+
+	if ( ! this.isLegacy( sea ) ) {
+
+		this._readAnimation( sea );
+
+	}
+
+};
+
+THREE.SEA3D.prototype.getModifier = function ( req ) {
+
+	var sea = req.sea;
+
+	if ( this.isLegacy( sea ) && ! sea.done ) {
+
+		sea.done = true;
+
+		switch ( sea.type ) {
+
+			case SEA3D.SkeletonAnimation.prototype.type:
+
+				this.readSkeletonAnimationLegacy( sea, req.skeleton );
+
+				return sea.tag;
+
+				break;
+
+			case SEA3D.Animation.prototype.type:
+			case SEA3D.MorphAnimation.prototype.type:
+			case SEA3D.UVWAnimation.prototype.type:
+
+				if ( req.scope instanceof THREE.Object3D ) {
+
+					this.flipDefaultAnimation( sea, req.scope, req.relative );
+
+				}
+
+				this._readAnimation( sea );
+
+				return sea.tag;
+
+				break;
+
+			case SEA3D.Morph.prototype.type:
+
+				this.readMorphLegacy( sea, req.geometry );
+
+				break;
+
+		}
+
+	}
+
+	return this._getModifier( req );
+
+};
+
+THREE.SEA3D.prototype.updateTransform = function () {
+
+	var buf1 = new THREE.Matrix4();
+	var identity = new THREE.Matrix4();
+
+	return function ( obj3d, sea ) {
+
+		if ( this.isLegacy( sea ) ) {
+
+			if ( sea.transform ) buf1.fromArray( sea.transform );
+			else buf1.makeTranslation( sea.position.x, sea.position.y, sea.position.z );
+
+			this.flipScaleMatrix(
+				buf1, false,
+				obj3d.parent ? obj3d.parent.matrixWorld : identity,
+				obj3d.parent instanceof THREE.Bone
+			);
+
+			obj3d.position.setFromMatrixPosition( buf1 );
+			obj3d.scale.setFromMatrixScale( buf1 );
+
+			// ignore rotation scale
+
+			buf1.scale( THREE.SEA3D.VECBUF.set( 1 / obj3d.scale.x, 1 / obj3d.scale.y, 1 / obj3d.scale.z ) );
+			obj3d.rotation.setFromRotationMatrix( buf1 );
+
+			obj3d.updateMatrixWorld();
+
+		} else {
+
+			this._updateTransform( obj3d, sea );
+
+		}
+
+	};
+
+}();
+
+THREE.SEA3D.prototype.readSkeleton = function () {
+
+	var mtx_tmp_inv = new THREE.Matrix4(),
+		mtx_local = new THREE.Matrix4(),
+		mtx_parent = new THREE.Matrix4(),
+		pos = new THREE.Vector3(),
+		qua = new THREE.Quaternion();
+
+	return function ( sea ) {
+
+		var bones = [],
+			isLegacy = sea.sea3d.config.legacy;
+
+		for ( var i = 0; i < sea.joint.length; i ++ ) {
+
+			var bone = sea.joint[ i ];
+
+			// get world inverse matrix
+
+			mtx_tmp_inv.fromArray( bone.inverseBindMatrix );
+
+			// convert to world matrix
+
+			mtx_local.getInverse( mtx_tmp_inv );
+
+			// convert to three.js order
+
+			if ( isLegacy ) this.flipBoneMatrix( mtx_local );
+
+			if ( bone.parentIndex > - 1 ) {
+
+				// to world
+
+				mtx_tmp_inv.fromArray( sea.joint[ bone.parentIndex ].inverseBindMatrix );
+				mtx_parent.getInverse( mtx_tmp_inv );
+
+				// convert parent to three.js order
+
+				if ( isLegacy ) this.flipBoneMatrix( mtx_parent );
+
+				// to local
+
+				mtx_parent.getInverse( mtx_parent );
+
+				mtx_local.multiplyMatrices( mtx_parent, mtx_local );
+
+			}
+
+			// apply matrix
+
+			pos.setFromMatrixPosition( mtx_local );
+			qua.setFromRotationMatrix( mtx_local );
+
+			bones[ i ] = {
+				name: bone.name,
+				pos: [ pos.x, pos.y, pos.z ],
+				rotq: [ qua.x, qua.y, qua.z, qua.w ],
+				parent: bone.parentIndex
+			};
+
+		}
+
+		this.domain.bones = this.bones = this.bones || [];
+		this.bones.push( this.objects[ sea.name + '.sklq' ] = sea.tag = bones );
+
+		return bones;
+
+	};
+
+}();
+
+THREE.SEA3D.prototype.readSkeletonAnimationLegacy = function () {
+
+	var mtx_tmp_inv = new THREE.Matrix4(),
+		mtx_local = new THREE.Matrix4(),
+		mtx_global = new THREE.Matrix4(),
+		mtx_parent = new THREE.Matrix4();
+
+	return function ( sea, skl ) {
+
+		if ( sea.tag ) return sea.tag;
+
+		var animations = [],
+			delta = ( 1000 / sea.frameRate ) / 1000,
+			scale = [ 1, 1, 1 ];
+
+		for ( var i = 0; i < sea.sequence.length; i ++ ) {
+
+			var seq = sea.sequence[ i ];
+
+			var start = seq.start;
+			var end = start + seq.count;
+
+			var animation = {
+				name: seq.name,
+				repeat: seq.repeat,
+				fps: sea.frameRate,
+				JIT: 0,
+				length: delta * seq.count,
+				hierarchy: []
+			};
+
+			var numJoints = sea.numJoints,
+				raw = sea.raw;
+
+			for ( var j = 0; j < numJoints; j ++ ) {
+
+				var bone = skl.joint[ j ],
+					node = { parent: bone.parentIndex, keys: [] },
+					keys = node.keys,
+					time = 0;
+
+				for ( var frame = start; frame < end; frame ++ ) {
+
+					var idx = ( frame * numJoints * 7 ) + ( j * 7 );
+
+					mtx_local.makeRotationFromQuaternion( THREE.SEA3D.QUABUF.set( raw[ idx + 3 ], raw[ idx + 4 ], raw[ idx + 5 ], raw[ idx + 6 ] ) );
+					mtx_local.setPosition( THREE.SEA3D.VECBUF.set( raw[ idx ], raw[ idx + 1 ], raw[ idx + 2 ] ) );
+
+					if ( bone.parentIndex > - 1 ) {
+
+						// to global
+
+						mtx_tmp_inv.fromArray( skl.joint[ bone.parentIndex ].inverseBindMatrix );
+
+						mtx_parent.getInverse( mtx_tmp_inv );
+
+						mtx_global.multiplyMatrices( mtx_parent, mtx_local );
+
+						// convert to three.js matrix
+
+						this.flipBoneMatrix( mtx_global );
+
+						// flip parent inverse
+
+						this.flipBoneMatrix( mtx_parent );
+
+						// to local
+
+						mtx_parent.getInverse( mtx_parent );
+
+						mtx_local.multiplyMatrices( mtx_parent, mtx_global );
+
+					} else {
+
+						this.flipBoneMatrix( mtx_local );
+
+					}
+
+					var posQ = THREE.SEA3D.VECBUF.setFromMatrixPosition( mtx_local );
+					var newQ = THREE.SEA3D.QUABUF.setFromRotationMatrix( mtx_local );
+
+					keys.push( {
+						time: time,
+						pos: [ posQ.x, posQ.y, posQ.z ],
+						rot: [ newQ.x, newQ.y, newQ.z, newQ.w ],
+						scl: scale
+					} );
+
+					time += delta;
+
+				}
+
+				animation.hierarchy[ j ] = node;
+
+			}
+
+			animations.push( THREE.SEA3D.AnimationClip.fromClip( THREE.AnimationClip.parseAnimation( animation, skl.tag ), seq.repeat ) );
+
+		}
+
+		this.domain.clips = this.clips = this.clips || [];
+		this.clips.push( this.objects[ sea.name + '.anm' ] = sea.tag = animations );
+
+	};
+
+}();
+
+THREE.SEA3D.prototype.readMorphLegacy = function ( sea, geo ) {
+
+	for ( var i = 0; i < sea.node.length; i ++ ) {
+
+		var node = sea.node[ i ];
+
+		this.flipVec3( node.vertex );
+		this.flipVec3( node.normal );
+
+		this.addVector( node.vertex, geo.vertex );
+		this.addVector( node.normal, geo.normal );
+
+	}
+
+	this._readMorph( sea );
+
+};
+
+THREE.SEA3D.prototype.readMorph = function ( sea ) {
+
+	if ( ! this.isLegacy( sea ) ) {
+
+		this._readMorph( sea );
+
+	}
+
+};
+
+THREE.SEA3D.prototype.readVertexAnimation = function ( sea ) {
+
+	if ( this.isLegacy( sea ) ) {
+
+		for ( var i = 0, l = sea.frame.length; i < l; i ++ ) {
+
+			var frame = sea.frame[ i ];
+
+			this.flipVec3( frame.vertex );
+			this.flipVec3( frame.normal );
+
+		}
+
+	}
+
+	this._readVertexAnimation( sea );
+
+};
+
+THREE.SEA3D.prototype.readGeometryBuffer = function ( sea ) {
+
+	if ( this.isLegacy( sea ) ) {
+
+		this.flipVec3( sea.vertex );
+		this.flipVec3( sea.normal );
+
+		this.flipIndexes( sea.indexes );
+
+		if ( sea.jointPerVertex > 4 ) this.compressJoints( sea );
+		else if ( sea.jointPerVertex < 4 ) this.expandJoints( sea );
+
+	}
+
+	this._readGeometryBuffer( sea );
+
+};
+
+THREE.SEA3D.prototype.readLines = function ( sea ) {
+
+	if ( this.isLegacy( sea ) ) {
+
+		this.flipVec3( sea.vertex );
+
+	}
+
+	this._readLines( sea );
+
+};
+
+THREE.SEA3D.prototype.onHead = function ( args ) {
+
+	if ( args.sign != "S3D" && args.sign != "TJS" ) {
+
+		throw new Error( "Sign '" + args.sign + "' unknown." );
+
+	}
+
+};
+
+THREE.SEA3D.EXTENSIONS_LOADER.push( { setTypeRead: function () {
+
+	// CONFIG
+
+	this.config.legacy = this.config.legacy == undefined ? true : this.config.legacy;
+
+	this.file.typeRead[ SEA3D.Skeleton.prototype.type ] = this.readSkeleton;
+
+} } );

+ 3843 - 3843
examples/js/loaders/sea3d/SEA3DLoader.js

@@ -1,3843 +1,3843 @@
-/**
- * 	SEA3D for Three.JS
- * 	@author Sunag / http://www.sunag.com.br/
- */
-
-'use strict';
-
-//
-//
-//	SEA3D
-//
-
-THREE.SEA3D = function ( config ) {
-
-	this.config = {
-		id: "",
-		scripts: true,
-		runScripts: true,
-		autoPlay: false,
-		dummys: true,
-		multiplier: 1,
-		bounding: true,
-		audioRolloffFactor: 10,
-		lights: true,
-		useEnvironment: true,
-		useVertexTexture: true,
-		forceStatic: false,
-		streaming: true,
-		async: true,
-		paths: {},
-		timeLimit: 10
-	};
-
-	if ( config ) this.loadConfig( config );
-
-};
-
-//
-//	Polyfills
-//
-
-if ( THREE.Float32BufferAttribute === undefined ) {
-
-	THREE.Float32BufferAttribute = THREE.Float32Attribute;
-
-}
-
-THREE.SEA3D.useMultiMaterial = THREE.MultiMaterial.prototype.isMultiMaterial;
-
-//
-//	Config
-//
-
-THREE.SEA3D.MTXBUF = new THREE.Matrix4();
-THREE.SEA3D.VECBUF = new THREE.Vector3();
-THREE.SEA3D.QUABUF = new THREE.Quaternion();
-
-THREE.SEA3D.BACKGROUND_COLOR = 0x333333;
-THREE.SEA3D.HELPER_COLOR = 0x9AB9E5;
-THREE.SEA3D.RTT_SIZE = 512;
-
-THREE.SEA3D.identityMatrixScale = function () {
-
-	var scl = new THREE.Vector3();
-
-	return function identityMatrixScale( matrix ) {
-
-		scl.setFromMatrixScale( matrix );
-
-		return matrix.scale( scl.set( 1 / scl.x, 1 / scl.y, 1 / scl.z ) );
-
-	};
-
-}();
-
-THREE.SEA3D.prototype = Object.assign( Object.create( THREE.EventDispatcher.prototype ), {
-
-	constructor: THREE.SEA3D,
-
-	setShadowMap: function ( light ) {
-
-		light.shadow.mapSize.width = 2048;
-		light.shadow.mapSize.height = 1024;
-
-		light.castShadow = true;
-
-		light.shadow.camera.left = - 200;
-		light.shadow.camera.right = 200;
-		light.shadow.camera.top = 200;
-		light.shadow.camera.bottom = - 200;
-
-		light.shadow.camera.near = 1;
-		light.shadow.camera.far = 3000;
-		light.shadow.camera.fov = 45;
-
-		light.shadow.bias = - 0.001;
-
-	}
-
-} );
-
-Object.defineProperties( THREE.SEA3D.prototype, {
-
-	container: {
-
-		set: function ( val ) {
-
-			this.config.container = val;
-
-		},
-
-		get: function () {
-
-			return this.config.container;
-
-		}
-
-	},
-
-	elapsedTime: {
-
-		get: function () {
-
-			return this.file.timer.elapsedTime;
-
-		}
-
-	}
-
-} );
-
-//
-//	Domain
-//
-
-THREE.SEA3D.Domain = function ( id, objects, container ) {
-
-	this.id = id;
-	this.objects = objects;
-	this.container = container;
-
-	this.sources = [];
-	this.local = {};
-
-	this.scriptTargets = [];
-
-	this.events = new THREE.EventDispatcher();
-
-};
-
-THREE.SEA3D.Domain.global = {};
-
-THREE.SEA3D.Domain.prototype = Object.assign( Object.create( THREE.EventDispatcher.prototype ), {
-
-	constructor: THREE.SEA3D.Domain,
-
-	add: function ( src ) {
-
-		this.sources.push( src );
-
-	},
-
-	remove: function ( src ) {
-
-		this.sources.splice( this.sources.indexOf( src ), 1 );
-
-	},
-
-	contains: function ( src ) {
-
-		return this.sources.indexOf( src ) != - 1;
-
-	},
-
-	addEventListener: function ( type, listener ) {
-
-		this.events.addEventListener( type, listener );
-
-	},
-
-	hasEventListener: function ( type, listener ) {
-
-		return this.events.hasEventListener( type, listener );
-
-	},
-
-	removeEventListener: function ( type, listener ) {
-
-		this.events.removeEventListener( type, listener );
-
-	},
-
-	print: function () {
-
-		console.log.apply( console, arguments );
-
-	},
-
-	watch: function () {
-
-		console.log.apply( console, 'watch:', arguments );
-
-	},
-
-	runScripts: function () {
-
-		for ( var i = 0; i < this.scriptTargets.length; i ++ ) {
-
-			this.runJSMList( this.scriptTargets[ i ] );
-
-		}
-
-	},
-
-	runJSMList: function ( target ) {
-
-		var scripts = target.scripts;
-
-		for ( var i = 0; i < scripts.length; i ++ ) {
-
-			this.runJSM( target, scripts[ i ] );
-
-		}
-
-		return scripts;
-
-	},
-
-	runJSM: function ( target, script ) {
-
-		var include = {
-			print: this.print,
-			watch: this.watch,
-			sea3d: this,
-			scene: this.container,
-			source: new THREE.SEA3D.ScriptDomain( this, target instanceof THREE.SEA3D.Domain )
-		};
-
-		Object.freeze( include.source );
-
-		THREE.SEA3D.ScriptHandler.add( include.source );
-
-		try {
-
-			this.methods[ script.method ](
-				include,
-				this.getReference,
-				THREE.SEA3D.Domain.global,
-				this.local,
-				target,
-				script.params
-			);
-
-		} catch ( e ) {
-
-			console.error( 'SEA3D JavaScript: Error running method "' + script.method + '".' );
-			console.error( e );
-
-		}
-
-	},
-
-	getReference: function ( ns ) {
-
-		return eval( ns );
-
-	},
-
-	disposeList: function ( list ) {
-
-		if ( ! list || ! list.length ) return;
-
-		list = list.concat();
-
-		var i = list.length;
-
-		while ( i -- ) {
-
-			list[ i ].dispose();
-
-		}
-
-	},
-
-	dispatchEvent: function ( event ) {
-
-		event.domain = this;
-
-		var sources = this.sources.concat(),
-			i = sources.length;
-
-		while ( i -- ) {
-
-			sources[ i ].dispatchEvent( event );
-
-		}
-
-		this.events.dispatchEvent( event );
-
-	},
-
-	dispose: function () {
-
-		this.disposeList( this.sources );
-
-		while ( this.container.children.length ) {
-
-			this.container.remove( this.container.children[ 0 ] );
-
-		}
-
-		var i = THREE.SEA3D.EXTENSIONS_DOMAIN.length;
-
-		while ( i -- ) {
-
-			var domain = THREE.SEA3D.EXTENSIONS_DOMAIN[ i ];
-
-			if ( domain.dispose ) domain.dispose.call( this );
-
-		}
-
-		this.disposeList( this.materials );
-		this.disposeList( this.dummys );
-
-		this.dispatchEvent( { type: "dispose" } );
-
-	}
-} );
-
-//
-//	Domain Manager
-//
-
-THREE.SEA3D.DomainManager = function ( autoDisposeRootDomain ) {
-
-	this.domains = [];
-	this.autoDisposeRootDomain = autoDisposeRootDomain !== undefined ? autoDisposeRootDomain : true;
-
-};
-
-Object.assign( THREE.SEA3D.DomainManager.prototype, {
-
-	onDisposeDomain: function ( e ) {
-
-		this.remove( e.domain );
-
-		if ( this.autoDisposeRootDomain && this.domains.length == 1 ) {
-
-			this.dispose();
-
-		}
-
-	},
-
-	add: function ( domain ) {
-
-		this._onDisposeDomain = this._onDisposeDomain || this.onDisposeDomain.bind( this );
-
-		domain.on( "dispose", this._onDisposeDomain );
-
-		this.domains.push( domain );
-
-		this.textures = this.textures || domain.textures;
-		this.cubemaps = this.cubemaps || domain.cubemaps;
-		this.geometries = this.geometries || domain.geometries;
-
-	},
-
-	remove: function ( domain ) {
-
-		domain.removeEvent( "dispose", this._onDisposeDomain );
-
-		this.domains.splice( this.domains.indexOf( domain ), 1 );
-
-	},
-
-	contains: function ( domain ) {
-
-		return this.domains.indexOf( domain ) != - 1;
-
-	},
-
-	disposeList: function ( list ) {
-
-		if ( ! list || ! list.length ) return;
-
-		list = list.concat();
-
-		var i = list.length;
-
-		while ( i -- ) {
-
-			list[ i ].dispose();
-
-		}
-
-	},
-
-	dispose: function () {
-
-		this.disposeList( this.domains );
-		this.disposeList( this.textures );
-		this.disposeList( this.cubemaps );
-		this.disposeList( this.geometries );
-
-	}
-
-} );
-
-//
-//	Script ( closure for private functions )
-//
-
-THREE.SEA3D.ScriptDomain = function ( domain, root ) {
-
-	domain = domain || new THREE.SEA3D.Domain();
-	domain.add( this );
-
-	var events = new THREE.EventDispatcher();
-
-	this.getId = function () {
-
-		return domain.id;
-
-	};
-
-	this.isRoot = function () {
-
-		return root;
-
-	};
-
-	this.addEventListener = function ( type, listener ) {
-
-		events.addEventListener( type, listener );
-
-	};
-
-	this.hasEventListener = function ( type, listener ) {
-
-		return events.hasEventListener( type, listener );
-
-	};
-
-	this.removeEventListener = function ( type, listener ) {
-
-		events.removeEventListener( type, listener );
-
-	};
-
-	this.dispatchEvent = function ( event ) {
-
-		event.script = this;
-
-		events.dispatchEvent( event );
-
-	};
-
-	this.dispose = function () {
-
-		domain.remove( this );
-
-		if ( root ) domain.dispose();
-
-		this.dispatchEvent( { type: "dispose" } );
-
-	};
-
-};
-
-//
-//	Script Manager ( closure for private functions )
-//
-
-THREE.SEA3D.ScriptManager = function () {
-
-	this.scripts = [];
-
-	var onDisposeScript = ( function ( e ) {
-
-		this.remove( e.script );
-
-	} ).bind( this );
-
-	this.add = function ( src ) {
-
-		src.addEventListener( "dispose", onDisposeScript );
-
-		this.scripts.push( src );
-
-	};
-
-	this.remove = function ( src ) {
-
-		src.removeEventListener( "dispose", onDisposeScript );
-
-		this.scripts.splice( this.scripts.indexOf( src ), 1 );
-
-	};
-
-	this.contains = function ( src ) {
-
-		return this.scripts.indexOf( src ) > - 1;
-
-	};
-
-	this.dispatchEvent = function ( event ) {
-
-		var scripts = this.scripts.concat(),
-			i = scripts.length;
-
-		while ( i -- ) {
-
-			scripts[ i ].dispatchEvent( event );
-
-		}
-
-	};
-
-};
-
-//
-//	Script Handler
-//
-
-THREE.SEA3D.ScriptHandler = new THREE.SEA3D.ScriptManager();
-
-THREE.SEA3D.ScriptHandler.dispatchUpdate = function ( delta ) {
-
-	this.dispatchEvent( {
-		type: "update",
-		delta: delta
-	} );
-
-};
-
-//
-//	Animation Clip
-//
-
-THREE.SEA3D.AnimationClip = function ( name, duration, tracks, repeat ) {
-
-	THREE.AnimationClip.call( this, name, duration, tracks );
-
-	this.repeat = repeat !== undefined ? repeat : true;
-
-};
-
-THREE.SEA3D.AnimationClip.fromClip = function ( clip, repeat ) {
-
-	return new THREE.SEA3D.AnimationClip( clip.name, clip.duration, clip.tracks, repeat );
-
-};
-
-THREE.SEA3D.AnimationClip.prototype = Object.assign( Object.create( THREE.AnimationClip.prototype ), {
-
-	constructor: THREE.SEA3D.AnimationClip
-
-} );
-
-//
-//	Animation
-//
-
-THREE.SEA3D.Animation = function ( clip, timeScale ) {
-
-	this.clip = clip;
-	this.timeScale = timeScale !== undefined ? timeScale : 1;
-
-};
-
-THREE.SEA3D.Animation.COMPLETE = "animationComplete";
-
-THREE.SEA3D.Animation.prototype = Object.assign( Object.create( THREE.EventDispatcher.prototype ), {
-
-	constructor: THREE.SEA3D.Animation,
-
-	onComplete: function ( scope ) {
-
-		this.dispatchEvent( { type: THREE.SEA3D.Animation.COMPLETE, target: this } );
-
-
-	}
-
-} );
-
-Object.defineProperties( THREE.SEA3D.Animation.prototype, {
-
-	name: {
-
-		get: function () {
-
-			return this.clip.name;
-
-		}
-
-	},
-
-	repeat: {
-
-		get: function () {
-
-			return this.clip.repeat;
-
-		}
-
-	},
-
-	duration: {
-
-		get: function () {
-
-			return this.clip.duration;
-
-		}
-
-	},
-
-	mixer: {
-
-		set: function ( val ) {
-
-			if ( this.mx ) {
-
-				this.mx.uncacheClip( this.clip );
-				delete this.mx;
-
-			}
-
-			if ( val ) {
-
-				this.mx = val;
-				this.mx.clipAction( this.clip );
-
-			}
-
-		},
-
-		get: function () {
-
-			return this.mx;
-
-		}
-
-	}
-
-} );
-
-//
-//	Animator
-//
-
-THREE.SEA3D.Animator = function ( clips, mixer ) {
-
-	this.updateAnimations( clips, mixer );
-
-	this.clone = function ( scope ) {
-
-		return new this.constructor( this.clips, new THREE.AnimationMixer( scope ) ).copyFrom( this );
-
-	}.bind( this );
-
-};
-
-Object.assign( THREE.SEA3D.Animator.prototype, {
-
-	update: function ( dt ) {
-
-		this.mixer.update( dt || 0 );
-
-		if ( this.currentAnimationAction && this.currentAnimationAction.paused ) {
-
-			this.pause();
-
-			if ( this.currentAnimation ) {
-
-				this.currentAnimation.onComplete( this );
-
-			}
-
-		}
-
-		return this;
-
-	},
-
-	updateAnimations: function ( clips, mixer ) {
-
-		if ( this.playing ) this.stop();
-
-		if ( this.mixer ) THREE.SEA3D.AnimationHandler.remove( this );
-
-		this.mixer = mixer;
-
-		this.relative = false;
-		this.playing = false;
-		this.paused = false;
-
-		this.timeScale = 1;
-
-		this.animations = [];
-		this.animation = {};
-
-		this.clips = [];
-
-		if ( clips ) {
-
-			for ( var i = 0; i < clips.length; i ++ ) {
-
-				this.addAnimation( clips[ i ] );
-
-			}
-
-		}
-
-		return this;
-
-	},
-
-	addAnimation: function ( animation ) {
-
-		if ( animation instanceof THREE.AnimationClip ) {
-
-			this.clips.push( animation );
-
-			animation = new THREE.SEA3D.Animation( animation );
-
-		}
-
-		this.animations.push( animation );
-		this.animation[ animation.name ] = animation;
-
-		animation.mixer = this.mixer;
-
-		return animation;
-
-	},
-
-	removeAnimation: function ( animation ) {
-
-		if ( animation instanceof THREE.AnimationClip ) {
-
-			animation = this.getAnimationByClip( animation );
-
-		}
-
-		this.clips.splice( this.clips.indexOf( animation.clip ), 1 );
-
-		delete this.animation[ animation.name ];
-		this.animations.splice( this.animations.indexOf( animation ), 1 );
-
-		animation.mixer = null;
-
-		return animation;
-
-	},
-
-	getAnimationByClip: function ( clip ) {
-
-		for ( var i = 0; i < this.animations.length; i ++ ) {
-
-			if ( this.animations[ i ].clip === clip ) return clip;
-
-		}
-
-	},
-
-	getAnimationByName: function ( name ) {
-
-		return typeof name === "number" ? this.animations[ name ] : this.animation[ name ];
-
-	},
-
-	setAnimationWeight: function ( name, val ) {
-
-		this.mixer.clipAction( this.getAnimationByName( name ).clip ).setEffectiveWeight( val );
-
-	},
-
-	getAnimationWeight: function ( name ) {
-
-		return this.mixer.clipAction( this.getAnimationByName( name ).clip ).getEffectiveWeight();
-
-	},
-
-	pause: function () {
-
-		if ( this.playing && this.currentAnimation ) {
-
-			THREE.SEA3D.AnimationHandler.remove( this );
-
-			this.playing = false;
-
-		}
-
-		return this;
-
-	},
-
-	resume: function () {
-
-		if ( ! this.playing && this.currentAnimation ) {
-
-			THREE.SEA3D.AnimationHandler.add( this );
-
-			this.playing = true;
-
-		}
-
-		return this;
-
-	},
-
-	setTimeScale: function ( val ) {
-
-		this.timeScale = val;
-
-		if ( this.currentAnimationAction ) this.updateTimeScale();
-
-		return this;
-
-	},
-
-	getTimeScale: function () {
-
-		return this.timeScale;
-
-	},
-
-	updateTimeScale: function () {
-
-		this.currentAnimationAction.setEffectiveTimeScale( this.timeScale * ( this.currentAnimation ? this.currentAnimation.timeScale : 1 ) );
-
-		return this;
-
-	},
-
-	play: function ( name, crossfade, offset, weight ) {
-
-		var animation = this.getAnimationByName( name );
-
-		if ( ! animation ) throw new Error( 'Animation "' + name + '" not found.' );
-
-		if ( animation == this.currentAnimation ) {
-
-			if ( offset !== undefined || ! animation.repeat ) this.currentAnimationAction.time = offset !== undefined ? offset :
-				( this.currentAnimationAction.timeScale >= 0 ? 0 : this.currentAnimation.duration );
-
-			this.currentAnimationAction.setEffectiveWeight( weight !== undefined ? weight : 1 );
-			this.currentAnimationAction.paused = false;
-
-			return this.resume();
-
-		} else {
-
-			this.previousAnimation = this.currentAnimation;
-			this.currentAnimation = animation;
-
-			this.previousAnimationAction = this.currentAnimationAction;
-			this.currentAnimationAction = this.mixer.clipAction( animation.clip ).setLoop( animation.repeat ? THREE.LoopRepeat : THREE.LoopOnce, Infinity ).reset();
-			this.currentAnimationAction.clampWhenFinished = ! animation.repeat;
-			this.currentAnimationAction.paused = false;
-
-			this.updateTimeScale();
-
-			if ( offset !== undefined || ! animation.repeat ) this.currentAnimationAction.time = offset !== undefined ? offset :
-				( this.currentAnimationAction.timeScale >= 0 ? 0 : this.currentAnimation.duration );
-
-			this.currentAnimationAction.setEffectiveWeight( weight !== undefined ? weight : 1 );
-
-			this.currentAnimationAction.play();
-
-			if ( ! this.playing ) this.mixer.update( 0 );
-
-			this.playing = true;
-
-			if ( this.previousAnimation ) this.previousAnimationAction.crossFadeTo( this.currentAnimationAction, crossfade || 0, false );
-
-			THREE.SEA3D.AnimationHandler.add( this );
-
-		}
-
-		return this;
-
-	},
-
-	stop: function () {
-
-		if ( this.playing ) THREE.SEA3D.AnimationHandler.remove( this );
-
-		if ( this.currentAnimation ) {
-
-			this.currentAnimationAction.stop();
-
-			this.previousAnimation = this.currentAnimation;
-			this.previousAnimationAction = this.currentAnimationAction;
-
-			delete this.currentAnimationAction;
-			delete this.currentAnimation;
-
-			this.playing = false;
-
-		}
-
-		return this;
-
-	},
-
-	playw: function ( name, weight ) {
-
-		if ( ! this.playing && ! this.paused ) THREE.SEA3D.AnimationHandler.add( this );
-
-		var animation = this.getAnimationByName( name );
-
-		this.playing = true;
-
-		var clip = this.mixer.clipAction( animation.clip );
-		clip.setLoop( animation.repeat ? THREE.LoopRepeat : THREE.LoopOnce, Infinity ).reset();
-		clip.clampWhenFinished = ! animation.repeat;
-		clip.paused = false;
-
-		clip.setEffectiveWeight( weight ).play();
-
-		return clip;
-
-	},
-
-	crossFade: function ( fromAnimName, toAnimName, duration, wrap ) {
-
-		this.mixer.stopAllAction();
-
-		var fromAction = this.playw( fromAnimName, 1 );
-		var toAction = this.playw( toAnimName, 1 );
-
-		fromAction.crossFadeTo( toAction, duration, wrap !== undefined ? wrap : false );
-
-		return this;
-
-	},
-
-	stopAll: function () {
-
-		this.stop().mixer.stopAllAction();
-
-		this.playing = false;
-
-		return this;
-
-	},
-
-	unPauseAll: function () {
-
-		this.mixer.timeScale = 1;
-
-		this.playing = true;
-		this.paused = false;
-
-		return this;
-
-	},
-
-	pauseAll: function () {
-
-		this.mixer.timeScale = 0;
-
-		this.playing = false;
-		this.paused = true;
-
-		return this;
-
-	},
-
-	setRelative: function ( val ) {
-
-		if ( this.relative == val ) return;
-
-		this.stop();
-
-		this.relative = val;
-
-		return this;
-
-	},
-
-	getRelative: function () {
-
-		return this.relative;
-
-	},
-
-	copyFrom: function ( scope ) {
-
-		for ( var i = 0; i < this.animations.length; i ++ ) {
-
-			this.animations[ i ].timeScale = scope.animations[ i ].timeScale;
-
-		}
-
-		return this;
-
-	}
-
-} );
-
-//
-//	Object3D Animator
-//
-
-THREE.SEA3D.Object3DAnimator = function ( clips, object3d ) {
-
-	this.object3d = object3d;
-
-	THREE.SEA3D.Animator.call( this, clips, new THREE.AnimationMixer( object3d ) );
-
-	this.clone = function ( scope ) {
-
-		return new this.constructor( this.clips, scope ).copyFrom( this );
-
-	}.bind( this );
-
-};
-
-THREE.SEA3D.Object3DAnimator.prototype = Object.assign( Object.create( THREE.SEA3D.Animator.prototype ), {
-
-	constructor: THREE.SEA3D.Object3DAnimator,
-
-	stop: function () {
-
-		if ( this.currentAnimation ) {
-
-			var animate = this.object3d.animate;
-
-			if ( animate && this instanceof THREE.SEA3D.Object3DAnimator ) {
-
-				animate.position.set( 0, 0, 0 );
-				animate.quaternion.set( 0, 0, 0, 1 );
-				animate.scale.set( 1, 1, 1 );
-
-			}
-
-		}
-
-		THREE.SEA3D.Animator.prototype.stop.call( this );
-
-	},
-
-	setRelative: function ( val ) {
-
-		THREE.SEA3D.Animator.prototype.setRelative.call( this, val );
-
-		this.object3d.setAnimator( this.relative );
-
-		this.updateAnimations( this.clips, new THREE.AnimationMixer( this.relative ? this.object3d.animate : this.object3d ) );
-
-	}
-
-} );
-
-//
-//	Camera Animator
-//
-
-THREE.SEA3D.CameraAnimator = function ( clips, object3d ) {
-
-	THREE.SEA3D.Object3DAnimator.call( this, clips, object3d );
-
-};
-
-THREE.SEA3D.CameraAnimator.prototype = Object.assign( Object.create( THREE.SEA3D.Object3DAnimator.prototype ), {
-
-	constructor: THREE.SEA3D.CameraAnimator
-
-} );
-
-//
-//	Sound Animator
-//
-
-THREE.SEA3D.SoundAnimator = function ( clips, object3d ) {
-
-	THREE.SEA3D.Object3DAnimator.call( this, clips, object3d );
-
-};
-
-THREE.SEA3D.SoundAnimator.prototype = Object.assign( Object.create( THREE.SEA3D.Object3DAnimator.prototype ), {
-
-	constructor: THREE.SEA3D.SoundAnimator
-
-} );
-
-//
-//	Light Animator
-//
-
-THREE.SEA3D.LightAnimator = function ( clips, object3d ) {
-
-	THREE.SEA3D.Object3DAnimator.call( this, clips, object3d );
-
-};
-
-THREE.SEA3D.LightAnimator.prototype = Object.assign( Object.create( THREE.SEA3D.Object3DAnimator.prototype ), {
-
-	constructor: THREE.SEA3D.LightAnimator
-
-} );
-
-//
-//	Container
-//
-
-THREE.SEA3D.Object3D = function ( ) {
-
-	THREE.Object3D.call( this );
-
-};
-
-THREE.SEA3D.Object3D.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), {
-
-	constructor: THREE.SEA3D.Object3D,
-
-	// Relative Animation Extension ( Only used if relative animation is enabled )
-	// TODO: It can be done with shader
-
-	updateAnimateMatrix: function ( force ) {
-
-		if ( this.matrixAutoUpdate === true ) this.updateMatrix();
-
-		if ( this.matrixWorldNeedsUpdate === true || force === true ) {
-
-			if ( this.parent === null ) {
-
-				this.matrixWorld.copy( this.matrix );
-
-			} else {
-
-				this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
-
-			}
-
-			this.animate.updateMatrix();
-
-			this.matrixWorld.multiplyMatrices( this.matrixWorld, this.animate.matrix );
-
-			this.matrixWorldNeedsUpdate = false;
-
-			force = true;
-
-		}
-
-		// update children
-
-		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
-
-			this.children[ i ].updateMatrixWorld( force );
-
-		}
-
-	},
-
-	setAnimator: function ( val ) {
-
-		if ( this.getAnimator() == val )
-			return;
-
-		if ( val ) {
-
-			this.animate = new THREE.Object3D();
-
-			this.updateMatrixWorld = THREE.SEA3D.Object3D.prototype.updateAnimateMatrix;
-
-		} else {
-
-			delete this.animate;
-
-			this.updateMatrixWorld = THREE.Object3D.prototype.updateMatrixWorld;
-
-		}
-
-		this.matrixWorldNeedsUpdate = true;
-
-	},
-
-	getAnimator: function () {
-
-		return this.animate != undefined;
-
-	},
-
-	copy: function ( source ) {
-
-		THREE.Object3D.prototype.copy.call( this, source );
-
-		this.attribs = source.attribs;
-		this.scripts = source.scripts;
-
-		if ( source.animator ) this.animator = source.animator.clone( this );
-
-		return this;
-
-	}
-
-} );
-
-//
-//	Dummy
-//
-
-THREE.SEA3D.Dummy = function ( width, height, depth ) {
-
-	this.width = width != undefined ? width : 100;
-	this.height = height != undefined ? height : 100;
-	this.depth = depth != undefined ? depth : 100;
-
-	var geo = new THREE.BoxGeometry( this.width, this.height, this.depth, 1, 1, 1 );
-
-	geo.computeBoundingBox();
-	geo.computeBoundingSphere();
-
-	THREE.Mesh.call( this, geo, THREE.SEA3D.Dummy.MATERIAL );
-
-};
-
-THREE.SEA3D.Dummy.MATERIAL = new THREE.MeshBasicMaterial( { wireframe: true, color: THREE.SEA3D.HELPER_COLOR } );
-
-THREE.SEA3D.Dummy.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), THREE.SEA3D.Object3D.prototype, {
-
-	constructor: THREE.SEA3D.Dummy,
-
-	copy: function ( source ) {
-
-		THREE.Mesh.prototype.copy.call( this, source );
-
-		this.attribs = source.attribs;
-		this.scripts = source.scripts;
-
-		if ( source.animator ) this.animator = source.animator.clone( this );
-
-		return this;
-
-	},
-
-	dispose: function () {
-
-		this.geometry.dispose();
-
-	}
-
-} );
-
-//
-//	Mesh
-//
-
-THREE.SEA3D.Mesh = function ( geometry, material ) {
-
-	THREE.Mesh.call( this, geometry, material );
-
-};
-
-THREE.SEA3D.Mesh.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), THREE.SEA3D.Object3D.prototype, {
-
-	constructor: THREE.SEA3D.Mesh,
-
-	setWeight: function ( name, val ) {
-
-		var index = typeof name === "number" ? name : this.morphTargetDictionary[ name ];
-
-		this.morphTargetInfluences[ index ] = val;
-
-	},
-
-	getWeight: function ( name ) {
-
-		var index = typeof name === "number" ? name : this.morphTargetDictionary[ name ];
-
-		return this.morphTargetInfluences[ index ];
-
-	},
-
-	copy: function ( source ) {
-
-		THREE.Mesh.prototype.copy.call( this, source );
-
-		this.attribs = source.attribs;
-		this.scripts = source.scripts;
-
-		if ( source.animator ) this.animator = source.animator.clone( this );
-
-		return this;
-
-	}
-
-} );
-
-//
-//	Skinning
-//
-
-THREE.SEA3D.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
-
-	THREE.SkinnedMesh.call( this, geometry, material, useVertexTexture );
-
-	this.updateAnimations( geometry.animations, new THREE.AnimationMixer( this ) );
-
-};
-
-THREE.SEA3D.SkinnedMesh.prototype = Object.assign( Object.create( THREE.SkinnedMesh.prototype ), THREE.SEA3D.Mesh.prototype, THREE.SEA3D.Animator.prototype, {
-
-	constructor: THREE.SEA3D.SkinnedMesh,
-
-	boneByName: function ( name ) {
-
-		var bones = this.skeleton.bones;
-
-		for ( var i = 0, bl = bones.length; i < bl; i ++ ) {
-
-			if ( name == bones[ i ].name )
-				return bones[ i ];
-
-		}
-
-	},
-
-	copy: function ( source ) {
-
-		THREE.SkinnedMesh.prototype.copy.call( this, source );
-
-		this.attribs = source.attribs;
-		this.scripts = source.scripts;
-
-		if ( source.animator ) this.animator = source.animator.clone( this );
-
-		return this;
-
-	}
-
-} );
-
-//
-//	Vertex Animation
-//
-
-THREE.SEA3D.VertexAnimationMesh = function ( geometry, material ) {
-
-	THREE.Mesh.call( this, geometry, material );
-
-	this.type = 'MorphAnimMesh';
-
-	this.updateAnimations( geometry.animations, new THREE.AnimationMixer( this ) );
-
-};
-
-THREE.SEA3D.VertexAnimationMesh.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), THREE.SEA3D.Mesh.prototype, THREE.SEA3D.Animator.prototype, {
-
-	constructor: THREE.SEA3D.VertexAnimationMesh,
-
-	copy: function ( source ) {
-
-		THREE.Mesh.prototype.copy.call( this, source );
-
-		this.attribs = source.attribs;
-		this.scripts = source.scripts;
-
-		if ( source.animator ) this.animator = source.animator.clone( this );
-
-		return this;
-
-	}
-
-} );
-
-//
-//	Camera
-//
-
-THREE.SEA3D.Camera = function ( fov, aspect, near, far ) {
-
-	THREE.PerspectiveCamera.call( this, fov, aspect, near, far );
-
-};
-
-THREE.SEA3D.Camera.prototype = Object.assign( Object.create( THREE.PerspectiveCamera.prototype ), THREE.SEA3D.Object3D.prototype, {
-
-	constructor: THREE.SEA3D.Camera,
-
-	copy: function ( source ) {
-
-		THREE.PerspectiveCamera.prototype.copy.call( this, source );
-
-		this.attribs = source.attribs;
-		this.scripts = source.scripts;
-
-		if ( source.animator ) this.animator = source.animator.clone( this );
-
-		return this;
-
-	}
-
-} );
-
-//
-//	Orthographic Camera
-//
-
-THREE.SEA3D.OrthographicCamera = function ( left, right, top, bottom, near, far ) {
-
-	THREE.OrthographicCamera.call( this, left, right, top, bottom, near, far );
-
-};
-
-THREE.SEA3D.OrthographicCamera.prototype = Object.assign( Object.create( THREE.OrthographicCamera.prototype ), THREE.SEA3D.Object3D.prototype, {
-
-	constructor: THREE.SEA3D.OrthographicCamera,
-
-	copy: function ( source ) {
-
-		THREE.OrthographicCamera.prototype.copy.call( this, source );
-
-		this.attribs = source.attribs;
-		this.scripts = source.scripts;
-
-		if ( source.animator ) this.animator = source.animator.clone( this );
-
-		return this;
-
-	}
-
-} );
-
-//
-//	PointLight
-//
-
-THREE.SEA3D.PointLight = function ( hex, intensity, distance, decay ) {
-
-	THREE.PointLight.call( this, hex, intensity, distance, decay );
-
-};
-
-THREE.SEA3D.PointLight.prototype = Object.assign( Object.create( THREE.PointLight.prototype ), THREE.SEA3D.Object3D.prototype, {
-
-	constructor: THREE.SEA3D.PointLight,
-
-	copy: function ( source ) {
-
-		THREE.PointLight.prototype.copy.call( this, source );
-
-		this.attribs = source.attribs;
-		this.scripts = source.scripts;
-
-		if ( source.animator ) this.animator = source.animator.clone( this );
-
-		return this;
-
-	}
-
-} );
-
-//
-//	Point Sound
-//
-
-THREE.SEA3D.PointSound = function ( listener, sound ) {
-
-	THREE.PositionalAudio.call( this, listener );
-
-	this.setSound( sound );
-
-};
-
-THREE.SEA3D.PointSound.prototype = Object.assign( Object.create( THREE.PositionalAudio.prototype ), THREE.SEA3D.Object3D.prototype, {
-
-	constructor: THREE.SEA3D.PointSound,
-
-	setSound: function ( sound ) {
-
-		this.sound = sound;
-
-		if ( sound ) {
-
-			if ( sound.buffer ) {
-
-				this.setBuffer( sound.buffer );
-
-			} else {
-
-				sound.addEventListener( "complete", function ( e ) {
-
-					this.setBuffer( sound.buffer );
-
-				}.bind( this ) );
-
-			}
-
-		}
-
-		return this;
-
-	},
-
-	copy: function ( source ) {
-
-		THREE.PositionalAudio.prototype.copy.call( this, source );
-
-		this.attribs = source.attribs;
-		this.scripts = source.scripts;
-
-		if ( source.animator ) this.animator = source.animator.clone( this );
-
-		return this;
-
-	}
-
-} );
-
-//
-//	Animation Handler
-//
-
-THREE.SEA3D.AnimationHandler = {
-
-	animators: [],
-
-	update: function ( dt ) {
-
-		var i = 0;
-
-		while ( i < this.animators.length ) {
-
-			this.animators[ i ++ ].update( dt );
-
-		}
-
-	},
-
-	add: function ( animator ) {
-
-		var index = this.animators.indexOf( animator );
-
-		if ( index === - 1 ) this.animators.push( animator );
-
-	},
-
-	remove: function ( animator ) {
-
-		var index = this.animators.indexOf( animator );
-
-		if ( index !== - 1 ) this.animators.splice( index, 1 );
-
-	}
-
-};
-
-//
-//	Sound
-//
-
-THREE.SEA3D.Sound = function ( src ) {
-
-	this.uuid = THREE.Math.generateUUID();
-
-	this.src = src;
-
-	new THREE.AudioLoader().load( src, function ( buffer ) {
-
-		this.buffer = buffer;
-
-		this.dispatchEvent( { type: "complete" } );
-
-	}.bind( this ) );
-
-};
-
-THREE.SEA3D.Sound.prototype = Object.assign( Object.create( THREE.EventDispatcher.prototype ), {
-
-	constructor: THREE.SEA3D.Sound
-
-} );
-
-//
-//	Output
-//
-
-THREE.SEA3D.Domain.prototype.getMesh = THREE.SEA3D.prototype.getMesh = function ( name ) {
-
-	return this.objects[ "m3d/" + name ];
-
-};
-
-THREE.SEA3D.Domain.prototype.getDummy = THREE.SEA3D.prototype.getDummy = function ( name ) {
-
-	return this.objects[ "dmy/" + name ];
-
-};
-
-THREE.SEA3D.Domain.prototype.getLine = THREE.SEA3D.prototype.getLine = function ( name ) {
-
-	return this.objects[ "line/" + name ];
-
-};
-
-THREE.SEA3D.Domain.prototype.getSound3D = THREE.SEA3D.prototype.getSound3D = function ( name ) {
-
-	return this.objects[ "sn3d/" + name ];
-
-};
-
-THREE.SEA3D.Domain.prototype.getMaterial = THREE.SEA3D.prototype.getMaterial = function ( name ) {
-
-	return this.objects[ "mat/" + name ];
-
-};
-
-THREE.SEA3D.Domain.prototype.getLight = THREE.SEA3D.prototype.getLight = function ( name ) {
-
-	return this.objects[ "lht/" + name ];
-
-};
-
-THREE.SEA3D.Domain.prototype.getGLSL = THREE.SEA3D.prototype.getGLSL = function ( name ) {
-
-	return this.objects[ "glsl/" + name ];
-
-};
-
-THREE.SEA3D.Domain.prototype.getCamera = THREE.SEA3D.prototype.getCamera = function ( name ) {
-
-	return this.objects[ "cam/" + name ];
-
-};
-
-THREE.SEA3D.Domain.prototype.getTexture = THREE.SEA3D.prototype.getTexture = function ( name ) {
-
-	return this.objects[ "tex/" + name ];
-
-};
-
-THREE.SEA3D.Domain.prototype.getCubeMap = THREE.SEA3D.prototype.getCubeMap = function ( name ) {
-
-	return this.objects[ "cmap/" + name ];
-
-};
-
-THREE.SEA3D.Domain.prototype.getJointObject = THREE.SEA3D.prototype.getJointObject = function ( name ) {
-
-	return this.objects[ "jnt/" + name ];
-
-};
-
-THREE.SEA3D.Domain.prototype.getContainer3D = THREE.SEA3D.prototype.getContainer3D = function ( name ) {
-
-	return this.objects[ "c3d/" + name ];
-
-};
-
-THREE.SEA3D.Domain.prototype.getSprite = THREE.SEA3D.prototype.getSprite = function ( name ) {
-
-	return this.objects[ "m2d/" + name ];
-
-};
-
-THREE.SEA3D.Domain.prototype.getProperties = THREE.SEA3D.prototype.getProperties = function ( name ) {
-
-	return this.objects[ "prop/" + name ];
-
-};
-
-//
-//	Utils
-//
-
-THREE.SEA3D.prototype.isPowerOfTwo = function ( num ) {
-
-	return num ? ( ( num & - num ) == num ) : false;
-
-};
-
-THREE.SEA3D.prototype.nearestPowerOfTwo = function ( num ) {
-
-	return Math.pow( 2, Math.round( Math.log( num ) / Math.LN2 ) );
-
-};
-
-THREE.SEA3D.prototype.updateTransform = function ( obj3d, sea ) {
-
-	var mtx = THREE.SEA3D.MTXBUF, vec = THREE.SEA3D.VECBUF;
-
-	if ( sea.transform ) mtx.fromArray( sea.transform );
-	else mtx.makeTranslation( sea.position.x, sea.position.y, sea.position.z );
-
-	// matrix
-
-	obj3d.position.setFromMatrixPosition( mtx );
-	obj3d.scale.setFromMatrixScale( mtx );
-
-	// ignore rotation scale
-
-	obj3d.rotation.setFromRotationMatrix( THREE.SEA3D.identityMatrixScale( mtx ) );
-
-	// optimize if is static
-
-	if ( this.config.forceStatic || sea.isStatic ) {
-
-		obj3d.updateMatrix();
-		obj3d.matrixAutoUpdate = false;
-
-	}
-
-};
-
-THREE.SEA3D.prototype.toVector3 = function ( data ) {
-
-	return new THREE.Vector3( data.x, data.y, data.z );
-
-};
-
-THREE.SEA3D.prototype.toFaces = function ( faces ) {
-
-	// xyz(- / +) to xyz(+ / -) sequence
-	var f = [];
-
-	f[ 0 ] = faces[ 1 ];
-	f[ 1 ] = faces[ 0 ];
-	f[ 2 ] = faces[ 3 ];
-	f[ 3 ] = faces[ 2 ];
-	f[ 4 ] = faces[ 5 ];
-	f[ 5 ] = faces[ 4 ];
-
-	return f;
-
-};
-
-THREE.SEA3D.prototype.updateScene = function () {
-
-	if ( this.materials != undefined ) {
-
-		for ( var i = 0, l = this.materials.length; i < l; ++ i ) {
-
-			this.materials[ i ].needsUpdate = true;
-
-		}
-
-	}
-
-};
-
-THREE.SEA3D.prototype.addSceneObject = function ( sea, obj3d ) {
-
-	obj3d = obj3d || sea.tag;
-
-	obj3d.visible = sea.visible;
-
-	if ( sea.parent ) sea.parent.tag.add( obj3d );
-	else if ( this.config.container ) this.config.container.add( obj3d );
-
-	if ( sea.attributes ) obj3d.attribs = sea.attributes.tag;
-
-	if ( sea.scripts ) {
-
-		obj3d.scripts = this.getJSMList( obj3d, sea.scripts );
-
-		if ( this.config.scripts && this.config.runScripts ) this.domain.runJSMList( obj3d );
-
-	}
-
-};
-
-THREE.SEA3D.prototype.createObjectURL = function ( raw, mime ) {
-
-	return ( window.URL || window.webkitURL ).createObjectURL( new Blob( [ raw ], { type: mime } ) );
-
-};
-
-THREE.SEA3D.prototype.parsePath = function ( url ) {
-
-	var paths = this.config.paths;
-
-	for ( var name in paths ) {
-
-		url = url.replace( new RegExp( "%" + name + "%", "g" ), paths[ name ] );
-
-	}
-
-	return url;
-
-};
-
-THREE.SEA3D.prototype.addDefaultAnimation = function ( sea, animatorClass ) {
-
-	var scope = sea.tag;
-
-	for ( var i = 0, count = sea.animations ? sea.animations.length : 0; i < count; i ++ ) {
-
-		var anm = sea.animations[ i ];
-
-		switch ( anm.tag.type ) {
-
-			case SEA3D.Animation.prototype.type:
-
-				var animation = anm.tag.tag || this.getModifier( {
-					sea: anm.tag,
-					scope: scope,
-					relative: anm.relative
-				} );
-
-				scope.animator = new animatorClass( animation, scope );
-				scope.animator.setRelative( anm.relative );
-
-				if ( this.config.autoPlay ) {
-
-					scope.animator.play( 0 );
-
-				}
-
-				return scope.animator;
-
-				break;
-
-		}
-
-	}
-
-};
-
-//
-//	Geometry
-//
-
-THREE.SEA3D.prototype.readGeometryBuffer = function ( sea ) {
-
-	var geo = sea.tag || new THREE.BufferGeometry();
-
-	for ( var i = 0; i < sea.groups.length; i ++ ) {
-
-		var g = sea.groups[ i ];
-
-		geo.addGroup( g.start, g.count, i );
-
-	}
-
-	// not indexes? use polygon soup
-	if ( sea.indexes ) geo.setIndex( new THREE.BufferAttribute( sea.indexes, 1 ) );
-
-	geo.addAttribute( 'position', new THREE.BufferAttribute( sea.vertex, 3 ) );
-
-	if ( sea.uv ) {
-
-		geo.addAttribute( 'uv', new THREE.BufferAttribute( sea.uv[ 0 ], 2 ) );
-		if ( sea.uv.length > 1 ) geo.addAttribute( 'uv2', new THREE.BufferAttribute( sea.uv[ 1 ], 2 ) );
-
-	}
-
-	if ( sea.normal ) geo.addAttribute( 'normal', new THREE.BufferAttribute( sea.normal, 3 ) );
-	else geo.computeVertexNormals();
-
-	if ( sea.tangent4 ) geo.addAttribute( 'tangent', new THREE.BufferAttribute( sea.tangent4, 4 ) );
-
-	if ( sea.color ) geo.addAttribute( 'color', new THREE.BufferAttribute( sea.color[ 0 ], sea.numColor ) );
-
-	if ( sea.joint ) {
-
-		geo.addAttribute( 'skinIndex', new THREE.Float32BufferAttribute( sea.joint, sea.jointPerVertex ) );
-		geo.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( sea.weight, sea.jointPerVertex ) );
-
-	}
-
-	if ( this.config.bounding ) {
-
-		geo.computeBoundingBox();
-		geo.computeBoundingSphere();
-
-	}
-
-	geo.name = sea.name;
-
-	this.domain.geometries = this.geometries = this.geometries || [];
-	this.geometries.push( this.objects[ "geo/" + sea.name ] = sea.tag = geo );
-
-};
-
-//
-//	Dummy
-//
-
-THREE.SEA3D.prototype.readDummy = function ( sea ) {
-
-	var dummy = new THREE.SEA3D.Dummy( sea.width, sea.height, sea.depth );
-	dummy.name = sea.name;
-
-	this.domain.dummys = this.dummys = this.dummys || [];
-	this.dummys.push( this.objects[ "dmy/" + sea.name ] = sea.tag = dummy );
-
-	this.addSceneObject( sea );
-	this.updateTransform( dummy, sea );
-
-	this.addDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator );
-
-};
-
-//
-//	Line
-//
-
-THREE.SEA3D.prototype.readLine = function ( sea ) {
-
-	var	geo = new THREE.BufferGeometry();
-
-	if ( sea.closed )
-		sea.vertex.push( sea.vertex[ 0 ], sea.vertex[ 1 ], sea.vertex[ 2 ] );
-
-	geo.addAttribute( 'position', new THREE.Float32BufferAttribute( sea.vertex, 3 ) );
-
-	var line = new THREE.Line( geo, new THREE.LineBasicMaterial( { color: THREE.SEA3D.HELPER_COLOR, linewidth: 3 } ) );
-	line.name = sea.name;
-
-	this.lines = this.lines || [];
-	this.lines.push( this.objects[ "line/" + sea.name ] = sea.tag = line );
-
-	this.addSceneObject( sea );
-	this.updateTransform( line, sea );
-
-	this.addDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator );
-
-};
-
-//
-//	Container3D
-//
-
-THREE.SEA3D.prototype.readContainer3D = function ( sea ) {
-
-	var container = new THREE.SEA3D.Object3D();
-
-	this.domain.containers = this.containers = this.containers || [];
-	this.containers.push( this.objects[ "c3d/" + sea.name ] = sea.tag = container );
-
-	this.addSceneObject( sea );
-	this.updateTransform( container, sea );
-
-	this.addDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator );
-
-};
-
-//
-//	Sprite
-//
-
-THREE.SEA3D.prototype.readSprite = function ( sea ) {
-
-	var mat;
-
-	if ( sea.material ) {
-
-		if ( ! sea.material.tag.sprite ) {
-
-			mat = sea.material.tag.sprite = new THREE.SpriteMaterial();
-
-			this.setBlending( mat, sea.blendMode );
-
-			var map = sea.material.tag.map;
-
-			if ( map ) {
-
-				map.flipY = true;
-				mat.map = map;
-
-			}
-
-			mat.color.set( sea.material.tag.color );
-			mat.opacity = sea.material.tag.opacity;
-			mat.fog = sea.material.receiveFog;
-
-		} else {
-
-			mat = sea.material.tag.sprite;
-
-		}
-
-	}
-
-	var sprite = new THREE.Sprite( mat );
-	sprite.name = sea.name;
-
-	this.domain.sprites = this.sprites = this.sprites || [];
-	this.sprites.push( this.objects[ "m2d/" + sea.name ] = sea.tag = sprite );
-
-	this.addSceneObject( sea );
-	this.updateTransform( sprite, sea );
-
-	sprite.scale.set( sea.width, sea.height, 1 );
-
-};
-
-//
-//	Mesh
-//
-
-THREE.SEA3D.prototype.readMesh = function ( sea ) {
-
-	var i, count, geo = sea.geometry.tag, mesh, mat, skeleton, morpher, skeletonAnimation, vertexAnimation, uvwAnimationClips, morphAnimation;
-
-	for ( i = 0, count = sea.modifiers ? sea.modifiers.length : 0; i < count; i ++ ) {
-
-		var mod = sea.modifiers[ i ];
-
-		switch ( mod.type ) {
-
-			case SEA3D.Skeleton.prototype.type:
-			case SEA3D.SkeletonLocal.prototype.type:
-
-				skeleton = mod;
-
-				geo.bones = skeleton.tag;
-
-				break;
-
-			case SEA3D.Morph.prototype.type:
-
-				morpher = mod.tag || this.getModifier( {
-					sea: mod,
-					geometry: sea.geometry
-				} );
-
-				geo.morphAttributes = morpher.attribs;
-				geo.morphTargets = morpher.targets;
-
-				break;
-
-		}
-
-	}
-
-	for ( i = 0, count = sea.animations ? sea.animations.length : 0; i < count; i ++ ) {
-
-		var anm = sea.animations[ i ],
-			anmTag = anm.tag;
-
-		switch ( anmTag.type ) {
-
-			case SEA3D.SkeletonAnimation.prototype.type:
-
-				skeletonAnimation = anmTag;
-
-				geo.animations = skeletonAnimation.tag || this.getModifier( {
-					sea: skeletonAnimation,
-					skeleton: skeleton,
-					relative: true
-				} );
-
-				break;
-
-			case SEA3D.VertexAnimation.prototype.type:
-
-				vertexAnimation = anmTag;
-
-				geo.morphAttributes = vertexAnimation.tag.attribs;
-				geo.morphTargets = vertexAnimation.tag.targets;
-				geo.animations = vertexAnimation.tag.animations;
-
-				break;
-
-			case SEA3D.UVWAnimation.prototype.type:
-
-				uvwAnimationClips = anmTag.tag || this.getModifier( {
-					sea: anmTag
-				} );
-
-				break;
-
-			case SEA3D.MorphAnimation.prototype.type:
-
-				morphAnimation = anmTag.tag || this.getModifier( {
-					sea: anmTag
-				} );
-
-				break;
-
-		}
-
-	}
-
-	var uMorph = morpher != undefined || vertexAnimation != undefined,
-		uMorphNormal =
-					( morpher && morpher.attribs.normal != undefined ) ||
-					( vertexAnimation && vertexAnimation.tag.attribs.normal != undefined );
-
-	if ( sea.material ) {
-
-		if ( sea.material.length > 1 ) {
-
-			var mats = [];
-
-			for ( i = 0; i < sea.material.length; i ++ ) {
-
-				mats[ i ] = sea.material[ i ].tag;
-
-				mats[ i ].skinning = skeleton != undefined;
-				mats[ i ].morphTargets = uMorph;
-				mats[ i ].morphNormals = uMorphNormal;
-				mats[ i ].vertexColors = sea.geometry.color ? THREE.VertexColors : THREE.NoColors;
-
-			}
-
-			mat = THREE.SEA3D.useMultiMaterial ? new THREE.MultiMaterial( mats ) : mats;
-
-		} else {
-
-			mat = sea.material[ 0 ].tag;
-
-			mat.skinning = skeleton != undefined;
-			mat.morphTargets = uMorph;
-			mat.morphNormals = uMorphNormal;
-			mat.vertexColors = sea.geometry.color ? THREE.VertexColors : THREE.NoColors;
-
-		}
-
-	}
-
-	if ( skeleton ) {
-
-		mesh = new THREE.SEA3D.SkinnedMesh( geo, mat, this.config.useVertexTexture );
-
-		if ( this.config.autoPlay && skeletonAnimation ) {
-
-			mesh.play( 0 );
-
-		}
-
-	} else if ( vertexAnimation ) {
-
-		mesh = new THREE.SEA3D.VertexAnimationMesh( geo, mat );
-
-		if ( this.config.autoPlay ) {
-
-			mesh.play( 0 );
-
-		}
-
-	} else {
-
-		mesh = new THREE.SEA3D.Mesh( geo, mat );
-
-	}
-
-	if ( uvwAnimationClips ) {
-
-		mesh.uvwAnimator = new THREE.SEA3D.Animator( uvwAnimationClips, new THREE.AnimationMixer( mat.map ) );
-
-		if ( this.config.autoPlay ) {
-
-			mesh.uvwAnimator.play( 0 );
-
-		}
-
-	}
-
-	if ( morphAnimation ) {
-
-		mesh.morphAnimator = new THREE.SEA3D.Animator( morphAnimation, new THREE.AnimationMixer( mesh ) );
-
-		if ( this.config.autoPlay ) {
-
-			mesh.morphAnimator.play( 0 );
-
-		}
-
-	}
-
-	mesh.name = sea.name;
-
-	mesh.castShadow = sea.castShadows;
-	mesh.receiveShadow = sea.material ? sea.material[ 0 ].receiveShadows : true;
-
-	this.domain.meshes = this.meshes = this.meshes || [];
-	this.meshes.push( this.objects[ "m3d/" + sea.name ] = sea.tag = mesh );
-
-	this.addSceneObject( sea );
-	this.updateTransform( mesh, sea );
-
-	this.addDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator );
-
-};
-
-//
-//	Sound Point
-//
-
-THREE.SEA3D.prototype.readSoundPoint = function ( sea ) {
-
-	if ( ! this.audioListener ) {
-
-		 this.audioListener = new THREE.AudioListener();
-
-		 if ( this.config.container ) {
-
-			this.config.container.add( this.audioListener );
-
-		}
-
-	}
-
-	var sound3d = new THREE.SEA3D.PointSound( this.audioListener );
-	sound3d.autoplay = sea.autoPlay;
-	sound3d.setLoop( sea.autoPlay );
-	sound3d.setVolume( sea.volume );
-	sound3d.setRefDistance( sea.distance );
-	sound3d.setRolloffFactor( this.config.audioRolloffFactor );
-	sound3d.setSound( sea.sound.tag );
-
-	sound3d.name = sea.name;
-
-	this.domain.sounds3d = this.sounds3d = this.sounds3d || [];
-	this.sounds3d.push( this.objects[ "sn3d/" + sea.name ] = sea.tag = sound3d );
-
-	this.addSceneObject( sea );
-	this.updateTransform( sound3d, sea );
-
-	this.addDefaultAnimation( sea, THREE.SEA3D.SoundAnimator );
-
-};
-
-//
-//	Cube Render
-//
-
-THREE.SEA3D.prototype.readCubeRender = function ( sea ) {
-
-	var cube = new THREE.CubeCamera( 0.1, 5000, THREE.SEA3D.RTT_SIZE );
-	cube.renderTarget.cubeCamera = cube;
-
-	sea.tag = cube.renderTarget;
-
-	this.domain.cubeRenderers = this.cubeRenderers = this.cubeRenderers || [];
-	this.cubeRenderers.push( this.objects[ "rttc/" + sea.name ] = cube );
-
-	this.addSceneObject( sea, cube );
-	this.updateTransform( cube, sea );
-
-};
-
-//
-//	Texture (WDP, JPEG, PNG and GIF)
-//
-
-THREE.SEA3D.prototype.readTexture = function ( sea ) {
-
-	var image = new Image(),
-		texture = new THREE.Texture();
-
-	texture.name = sea.name;
-	texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
-	texture.flipY = false;
-	texture.image = image;
-
-	if ( this.config.anisotropy !== undefined ) texture.anisotropy = this.config.anisotropy;
-
-	image.onload = function () {
-
-		texture.needsUpdate = true;
-
-	};
-
-	image.src = this.createObjectURL( sea.data.buffer, "image/" + sea.type );
-
-	this.domain.textures = this.textures = this.textures || [];
-	this.textures.push( this.objects[ "tex/" + sea.name ] = sea.tag = texture );
-
-};
-
-//
-//	Cube Map
-//
-
-THREE.SEA3D.prototype.readCubeMap = function ( sea ) {
-
-	var faces = this.toFaces( sea.faces ), texture = new THREE.CubeTexture( [] );
-
-	var loaded = 0;
-
-	texture.name = sea.name;
-	texture.flipY = false;
-	texture.format = THREE.RGBFormat;
-
-	var onLoaded = function () {
-
-		if ( ++ loaded == 6 ) {
-
-			texture.needsUpdate = true;
-
-			if ( ! this.config.async ) this.file.resume = true;
-
-		}
-
-	}.bind( this );
-
-	for ( var i = 0; i < faces.length; ++ i ) {
-
-		var cubeImage = new Image();
-		cubeImage.onload = onLoaded;
-		cubeImage.src = this.createObjectURL( faces[ i ].buffer, "image/" + sea.extension );
-
-		texture.images[ i ] = cubeImage;
-
-	}
-
-	if ( ! this.config.async ) this.file.resume = false;
-
-	this.domain.cubemaps = this.cubemaps = this.cubemaps || [];
-	this.cubemaps.push( this.objects[ "cmap/" + sea.name ] = sea.tag = texture );
-
-};
-
-//
-//	Updaters
-//
-
-THREE.SEA3D.prototype.readTextureUpdate = function ( sea ) {
-
-	var obj = this.file.objects[ sea.index ],
-		tex = obj.tag;
-
-	var image = new Image();
-
-	image.onload = function () {
-
-		tex.image = image;
-		tex.needsUpdate = true;
-
-	};
-
-	image.src = this.createObjectURL( sea.bytes.buffer, "image/" + obj.type );
-
-};
-
-//
-//	Sound (MP3, OGG)
-//
-
-THREE.SEA3D.prototype.readSound = function ( sea ) {
-
-	var sound = new THREE.SEA3D.Sound( this.createObjectURL( sea.data.buffer, "audio/" + sea.type ) );
-	sound.name = sea.name;
-
-	this.domain.sounds = this.sounds = this.sounds || [];
-	this.sounds.push( this.objects[ "snd/" + sea.name ] = sea.tag = sound );
-
-};
-
-//
-//	Script URL
-//
-
-THREE.SEA3D.prototype.readScriptURL = function ( sea ) {
-
-	this.file.resume = false;
-
-	var loader = new THREE.FileLoader();
-
-	loader.setResponseType( "text" ).load( sea.url, function ( src ) {
-
-		this.file.resume = true;
-
-		this.domain.scripts = this.scripts = this.scripts || [];
-		this.scripts.push( this.objects[ "src/" + sea.name ] = sea.tag = src );
-
-	}.bind( this ) );
-
-};
-
-//
-//	Texture URL
-//
-
-THREE.SEA3D.prototype.readTextureURL = function ( sea ) {
-
-	var texture = new THREE.TextureLoader().load( this.parsePath( sea.url ) );
-
-	texture.name = sea.name;
-	texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
-	texture.flipY = false;
-
-	if ( this.config.anisotropy !== undefined ) texture.anisotropy = this.config.anisotropy;
-
-	this.domain.textures = this.textures = this.textures || [];
-	this.textures.push( this.objects[ "tex/" + sea.name ] = sea.tag = texture );
-
-};
-
-//
-//	CubeMap URL
-//
-
-THREE.SEA3D.prototype.readCubeMapURL = function ( sea ) {
-
-	var faces = this.toFaces( sea.faces );
-
-	for ( var i = 0; i < faces.length; i ++ ) {
-
-		faces[ i ] = this.parsePath( faces[ i ] );
-
-	}
-
-	var texture, format = faces[ 0 ].substr( - 3 );
-
-	if ( format == "hdr" ) {
-
-		var usePMREM = THREE.PMREMGenerator != null;
-
-		this.file.resume = ! usePMREM;
-
-		texture = new THREE.HDRCubeTextureLoader().load( THREE.UnsignedByteType, faces, function ( texture ) {
-
-			if ( usePMREM ) {
-
-				var pmremGenerator = new THREE.PMREMGenerator( texture );
-				pmremGenerator.update( this.config.renderer );
-
-				var pmremCubeUVPacker = new THREE.PMREMCubeUVPacker( pmremGenerator.cubeLods );
-				pmremCubeUVPacker.update( this.config.renderer );
-
-				texture.dispose();
-
-				this.objects[ "cmap/" + sea.name ] = sea.tag = pmremCubeUVPacker.CubeUVRenderTarget.texture;
-
-				this.file.resume = true;
-
-			}
-
-		}.bind( this ) );
-
-	} else {
-
-		texture = new THREE.CubeTextureLoader().load( faces );
-
-	}
-
-	texture.name = sea.name;
-	texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
-	texture.flipY = false;
-
-	if ( this.config.anisotropy !== undefined ) texture.anisotropy = this.config.anisotropy;
-
-	this.domain.cubemaps = this.cubemaps = this.cubemaps || [];
-	this.cubemaps.push( this.objects[ "cmap/" + sea.name ] = sea.tag = texture );
-
-};
-
-//
-//	Runtime
-//
-
-THREE.SEA3D.prototype.getJSMList = function ( target, scripts ) {
-
-	var scriptTarget = [];
-
-	for ( var i = 0; i < scripts.length; i ++ ) {
-
-		var script = scripts[ i ];
-
-		if ( script.tag.type == SEA3D.JavaScriptMethod.prototype.type ) {
-
-			scriptTarget.push( script );
-
-		}
-
-	}
-
-	this.domain.scriptTargets = this.scriptTargets = this.scriptTargets || [];
-	this.scriptTargets.push( target );
-
-	return scriptTarget;
-
-};
-
-THREE.SEA3D.prototype.readJavaScriptMethod = function ( sea ) {
-
-	try {
-
-		var src =
-			'(function() {\n' +
-			'var $METHOD = {}\n';
-
-		var declare =
-			'function($INC, $REF, global, local, self, $PARAM) {\n' +
-			'var watch = $INC["watch"],\n' +
-			'scene = $INC["scene"],\n' +
-			'sea3d = $INC["sea3d"],\n' +
-			'print = $INC["print"];\n';
-
-		declare +=
-			'var $SRC = $INC["source"],\n' +
-			'addEventListener = $SRC.addEventListener.bind( $SRC ),\n' +
-			'hasEventListener = $SRC.hasEventListener.bind( $SRC ),\n' +
-			'removeEventListener = $SRC.removeEventListener.bind( $SRC ),\n' +
-			'dispatchEvent = $SRC.dispatchEvent.bind( $SRC ),\n' +
-			'dispose = $SRC.dispose.bind( $SRC );\n';
-
-		for ( var name in sea.methods ) {
-
-			src += '$METHOD["' + name + '"] = ' + declare + sea.methods[ name ].src + '}\n';
-
-		}
-
-		src += 'return $METHOD; })';
-
-		this.domain.methods = eval( src )();
-
-	} catch ( e ) {
-
-		console.error( 'SEA3D JavaScriptMethod: Error running "' + sea.name + '".' );
-		console.error( e );
-
-	}
-
-};
-
-//
-//	GLSL
-//
-
-THREE.SEA3D.prototype.readGLSL = function ( sea ) {
-
-	this.domain.glsl = this.glsl = this.glsl || [];
-	this.glsl.push( this.objects[ "glsl/" + sea.name ] = sea.tag = sea.src );
-
-};
-
-//
-//	Material
-//
-
-THREE.SEA3D.prototype.materialTechnique =
-( function () {
-
-	var techniques = {};
-
-	// FINAL
-	techniques.onComplete = function ( mat, sea ) {
-
-		if ( sea.alpha < 1 || mat.blending > THREE.NormalBlending ) {
-
-			mat.opacity = sea.alpha;
-			mat.transparent = true;
-
-		}
-
-	};
-
-	// PHYSICAL
-	techniques[ SEA3D.Material.PHYSICAL ] =
-	function ( mat, tech ) {
-
-		mat.color.setHex( tech.color );
-		mat.roughness = tech.roughness;
-		mat.metalness = tech.metalness;
-
-	};
-
-	// REFLECTIVITY
-	techniques[ SEA3D.Material.REFLECTIVITY ] =
-	function ( mat, tech ) {
-
-		mat.reflectivity = tech.strength;
-
-	};
-
-	// CLEAR_COAT
-	techniques[ SEA3D.Material.CLEAR_COAT ] =
-	function ( mat, tech ) {
-
-		mat.clearCoat = tech.strength;
-		mat.clearCoatRoughness = tech.roughness;
-
-	};
-
-	// PHONG
-	techniques[ SEA3D.Material.PHONG ] =
-	function ( mat, tech ) {
-
-		mat.color.setHex( tech.diffuseColor );
-		mat.specular.setHex( tech.specularColor ).multiplyScalar( tech.specular );
-		mat.shininess = tech.gloss;
-
-	};
-
-	// DIFFUSE_MAP
-	techniques[ SEA3D.Material.DIFFUSE_MAP ] =
-	function ( mat, tech, sea ) {
-
-		mat.map = tech.texture.tag;
-		mat.color.setHex( 0xFFFFFF );
-
-		mat.map.wrapS = mat.map.wrapT = sea.repeat ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-
-		if ( tech.texture.transparent ) {
-
-			mat.transparent = true;
-
-		}
-
-	};
-
-	// ROUGHNESS_MAP
-	techniques[ SEA3D.Material.ROUGHNESS_MAP ] =
-	function ( mat, tech ) {
-
-		mat.roughnessMap = tech.texture.tag;
-
-	};
-
-	// METALNESS_MAP
-	techniques[ SEA3D.Material.METALNESS_MAP ] =
-	function ( mat, tech ) {
-
-		mat.metalnessMap = tech.texture.tag;
-
-	};
-
-	// SPECULAR_MAP
-	techniques[ SEA3D.Material.SPECULAR_MAP ] =
-	function ( mat, tech ) {
-
-		if ( mat.specular ) {
-
-			mat.specularMap = tech.texture.tag;
-			mat.specular.setHex( 0xFFFFFF );
-
-		}
-
-	};
-
-	// NORMAL_MAP
-	techniques[ SEA3D.Material.NORMAL_MAP ] =
-	function ( mat, tech ) {
-
-		mat.normalMap = tech.texture.tag;
-
-	};
-
-	// REFLECTION
-	techniques[ SEA3D.Material.REFLECTION ] =
-	techniques[ SEA3D.Material.FRESNEL_REFLECTION ] =
-	function ( mat, tech ) {
-
-		mat.envMap = tech.texture.tag;
-		mat.envMap.mapping = THREE.CubeReflectionMapping;
-		mat.combine = THREE.MixOperation;
-
-		mat.reflectivity = tech.alpha;
-
-	};
-
-	// REFLECTION_SPHERICAL
-	techniques[ SEA3D.Material.REFLECTION_SPHERICAL ] =
-	function ( mat, tech ) {
-
-		mat.envMap = tech.texture.tag;
-		mat.envMap.mapping = THREE.SphericalReflectionMapping;
-		mat.combine = THREE.MixOperation;
-
-		mat.reflectivity = tech.alpha;
-
-	};
-
-	// REFRACTION
-	techniques[ SEA3D.Material.REFRACTION_MAP ] =
-	function ( mat, tech ) {
-
-		mat.envMap = tech.texture.tag;
-		mat.envMap.mapping = THREE.CubeRefractionMapping;
-
-		mat.refractionRatio = tech.ior;
-		mat.reflectivity = tech.alpha;
-
-	};
-
-	// LIGHT_MAP
-	techniques[ SEA3D.Material.LIGHT_MAP ] =
-	function ( mat, tech ) {
-
-		if ( tech.blendMode == "multiply" ) mat.aoMap = tech.texture.tag;
-		else mat.lightMap = tech.texture.tag;
-
-	};
-
-	// EMISSIVE
-	techniques[ SEA3D.Material.EMISSIVE ] =
-	function ( mat, tech ) {
-
-		mat.emissive.setHex( tech.color );
-
-	};
-
-	// EMISSIVE_MAP
-	techniques[ SEA3D.Material.EMISSIVE_MAP ] =
-	function ( mat, tech ) {
-
-		mat.emissiveMap = tech.texture.tag;
-
-	};
-
-	// ALPHA_MAP
-	techniques[ SEA3D.Material.ALPHA_MAP ] =
-	function ( mat, tech, sea ) {
-
-		mat.alphaMap = tech.texture.tag;
-		mat.transparent = true;
-
-		mat.alphaMap.wrapS = mat.alphaMap.wrapT = sea.repeat ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-
-	};
-
-	return techniques;
-
-} )();
-
-THREE.SEA3D.prototype.createMaterial = function ( sea ) {
-
-	if ( sea.tecniquesDict[ SEA3D.Material.REFLECTIVITY ] || sea.tecniquesDict[ SEA3D.Material.CLEAR_COAT ] ) {
-
-		return new THREE.MeshPhysicalMaterial();
-
-	} else if ( sea.tecniquesDict[ SEA3D.Material.PHYSICAL ] ) {
-
-		return new THREE.MeshStandardMaterial();
-
-	}
-
-	return new THREE.MeshPhongMaterial();
-
-};
-
-THREE.SEA3D.prototype.setBlending = function ( mat, blendMode ) {
-
-	if ( blendMode === "normal" ) return;
-
-	switch ( blendMode ) {
-
-		case "add":
-
-			mat.blending = THREE.AdditiveBlending;
-
-			break;
-
-		case "subtract":
-
-			mat.blending = THREE.SubtractiveBlending;
-
-			break;
-
-		case "multiply":
-
-			mat.blending = THREE.MultiplyBlending;
-
-			break;
-
-		case "screen":
-
-			mat.blending = THREE.CustomBlending;
-			mat.blendSrc = THREE.OneFactor;
-			mat.blendDst = THREE.OneMinusSrcColorFactor;
-			mat.blendEquation = THREE.AddEquation;
-
-			break;
-
-	}
-
-	mat.transparent = true;
-
-};
-
-THREE.SEA3D.prototype.readMaterial = function ( sea ) {
-
-	var mat = this.createMaterial( sea );
-	mat.name = sea.name;
-
-	mat.lights = sea.receiveLights;
-	mat.fog = sea.receiveFog;
-
-	mat.depthWrite = sea.depthWrite;
-	mat.depthTest = sea.depthTest;
-
-	mat.premultipliedAlpha = sea.premultipliedAlpha;
-
-	mat.side = sea.doubleSided ? THREE.DoubleSide : THREE.FrontSide;
-
-	this.setBlending( mat, sea.blendMode );
-
-	for ( var i = 0; i < sea.technique.length; i ++ ) {
-
-		var tech = sea.technique[ i ];
-
-		if ( this.materialTechnique[ tech.kind ] ) {
-
-			this.materialTechnique[ tech.kind ].call( this, mat, tech, sea );
-
-		}
-
-	}
-
-	if ( this.materialTechnique.onComplete ) {
-
-		this.materialTechnique.onComplete.call( this, mat, sea );
-
-	}
-
-	this.domain.materials = this.materials = this.materials || [];
-	this.materials.push( this.objects[ "mat/" + sea.name ] = sea.tag = mat );
-
-};
-
-//
-//	Point Light
-//
-
-THREE.SEA3D.prototype.readPointLight = function ( sea ) {
-
-	var light = new THREE.SEA3D.PointLight( sea.color, sea.multiplier * this.config.multiplier );
-	light.name = sea.name;
-
-	if ( sea.attenuation ) {
-
-		light.distance = sea.attenuation.end;
-
-	}
-
-	if ( sea.shadow ) this.setShadowMap( light );
-
-	this.domain.lights = this.lights = this.lights || [];
-	this.lights.push( this.objects[ "lht/" + sea.name ] = sea.tag = light );
-
-	this.addSceneObject( sea );
-
-	this.updateTransform( light, sea );
-
-	this.addDefaultAnimation( sea, THREE.SEA3D.LightAnimator );
-
-	this.updateScene();
-
-};
-
-//
-//	Hemisphere Light
-//
-
-THREE.SEA3D.prototype.readHemisphereLight = function ( sea ) {
-
-	var light = new THREE.HemisphereLight( sea.color, sea.secondColor, sea.multiplier * this.config.multiplier );
-	light.position.set( 0, 500, 0 );
-	light.name = sea.name;
-
-	this.domain.lights = this.lights = this.lights || [];
-	this.lights.push( this.objects[ "lht/" + sea.name ] = sea.tag = light );
-
-	this.addSceneObject( sea );
-
-	this.addDefaultAnimation( sea, THREE.SEA3D.LightAnimator );
-
-	this.updateScene();
-
-};
-
-//
-//	Ambient Light
-//
-
-THREE.SEA3D.prototype.readAmbientLight = function ( sea ) {
-
-	var light = new THREE.AmbientLight( sea.color, sea.multiplier * this.config.multiplier );
-	light.name = sea.name;
-
-	this.domain.lights = this.lights = this.lights || [];
-	this.lights.push( this.objects[ "lht/" + sea.name ] = sea.tag = light );
-
-	this.addSceneObject( sea );
-
-	this.addDefaultAnimation( sea, THREE.SEA3D.LightAnimator );
-
-	this.updateScene();
-
-};
-
-//
-//	Directional Light
-//
-
-THREE.SEA3D.prototype.readDirectionalLight = function ( sea ) {
-
-	var light = new THREE.DirectionalLight( sea.color, sea.multiplier * this.config.multiplier );
-	light.name = sea.name;
-
-	if ( sea.shadow ) {
-
-		this.setShadowMap( light );
-
-	}
-
-	this.domain.lights = this.lights = this.lights || [];
-	this.lights.push( this.objects[ "lht/" + sea.name ] = sea.tag = light );
-
-	this.addSceneObject( sea );
-
-	this.updateTransform( light, sea );
-
-	this.addDefaultAnimation( sea, THREE.SEA3D.LightAnimator );
-
-	this.updateScene();
-
-};
-
-//
-//	Camera
-//
-
-THREE.SEA3D.prototype.readCamera = function ( sea ) {
-
-	var camera = new THREE.SEA3D.Camera( sea.fov );
-	camera.name = sea.name;
-
-	this.domain.cameras = this.cameras = this.cameras || [];
-	this.cameras.push( this.objects[ "cam/" + sea.name ] = sea.tag = camera );
-
-	this.addSceneObject( sea );
-	this.updateTransform( camera, sea );
-
-	this.addDefaultAnimation( sea, THREE.SEA3D.CameraAnimator );
-
-};
-
-//
-//	Orthographic Camera
-//
-
-THREE.SEA3D.prototype.readOrthographicCamera = function ( sea ) {
-
-	var aspect, width, height;
-
-	var stageWidth = this.config.stageWidth !== undefined ? this.config.stageWidth : ( window ? window.innerWidth : 1024 );
-	var stageHeight = this.config.stageHeight !== undefined ? this.config.stageHeight : ( window ? window.innerHeight : 1024 );
-
-	if ( stageWidth > stageHeight ) {
-
-		aspect = stageWidth / stageHeight;
-
-		width = sea.height * aspect;
-		height = sea.height;
-
-	} else {
-
-		aspect = stageHeight / stageWidth;
-
-		width = sea.height;
-		height = sea.height * aspect;
-
-	}
-
-	var camera = new THREE.SEA3D.OrthographicCamera( - width, width, height, - height );
-	camera.name = sea.name;
-
-	this.domain.cameras = this.cameras = this.cameras || [];
-	this.cameras.push( this.objects[ "cam/" + sea.name ] = sea.tag = camera );
-
-	this.addSceneObject( sea );
-	this.updateTransform( camera, sea );
-
-	this.addDefaultAnimation( sea, THREE.SEA3D.CameraAnimator );
-
-};
-
-//
-//	Skeleton
-//
-
-THREE.SEA3D.prototype.getSkeletonFromBones = function ( bonesData ) {
-
-	var bones = [], bone, gbone;
-	var i, il;
-
-	for ( i = 0, il = bonesData.length; i < il; i ++ ) {
-
-		gbone = bonesData[ i ];
-
-		bone = new THREE.Bone();
-		bones.push( bone );
-
-		bone.name = gbone.name;
-		bone.position.fromArray( gbone.pos );
-		bone.quaternion.fromArray( gbone.rotq );
-
-		if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl );
-
-	}
-
-	for ( i = 0, il = bonesData.length; i < il; i ++ ) {
-
-		gbone = bonesData[ i ];
-
-		if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) {
-
-			bones[ gbone.parent ].add( bones[ i ] );
-
-		}
-
-	}
-
-	return new THREE.Skeleton( bones );
-
-};
-
-THREE.SEA3D.prototype.readSkeletonLocal = function ( sea ) {
-
-	var bones = [];
-
-	for ( var i = 0; i < sea.joint.length; i ++ ) {
-
-		var bone = sea.joint[ i ];
-
-		bones[ i ] = {
-			name: bone.name,
-			pos: [ bone.x, bone.y, bone.z ],
-			rotq: [ bone.qx, bone.qy, bone.qz, bone.qw ],
-			parent: bone.parentIndex
-		};
-
-	}
-
-	this.domain.bones = this.bones = this.bones || [];
-	this.bones.push( this.objects[ sea.name + '.sklq' ] = sea.tag = bones );
-
-};
-
-//
-//	Joint Object
-//
-
-THREE.SEA3D.prototype.readJointObject = function ( sea ) {
-
-	var mesh = sea.target.tag,
-		bone = mesh.skeleton.bones[ sea.joint ];
-
-	this.domain.joints = this.joints = this.joints || [];
-	this.joints.push( this.objects[ "jnt/" + sea.name ] = sea.tag = bone );
-
-};
-
-//
-//	Morph
-//
-
-THREE.SEA3D.prototype.readMorph = function ( sea ) {
-
-	var attribs = { position: [] }, targets = [];
-
-	for ( var i = 0; i < sea.node.length; i ++ ) {
-
-		var node = sea.node[ i ];
-
-		attribs.position[ i ] = new THREE.Float32BufferAttribute( node.vertex, 3 );
-		attribs.position[ i ].name = node.name;
-
-		if ( node.normal ) {
-
-			attribs.normal = attribs.normal || [];
-			attribs.normal[ i ] = new THREE.Float32BufferAttribute( node.normal, 3 );
-
-		}
-
-		targets[ i ] = { name: node.name };
-
-	}
-
-	sea.tag = {
-		attribs: attribs,
-		targets: targets
-	};
-
-};
-
-//
-//	Animation
-//
-
-THREE.SEA3D.prototype.readAnimation = function ( sea ) {
-
-	var animations = [], delta = ( 1000 / sea.frameRate ) / 1000;
-
-	for ( var i = 0; i < sea.sequence.length; i ++ ) {
-
-		var seq = sea.sequence[ i ];
-
-		var tracks = [];
-
-		for ( var j = 0; j < sea.dataList.length; j ++ ) {
-
-			var anm = sea.dataList[ j ],
-				t, k, times, values,
-				data = anm.data,
-				start = seq.start * anm.blockSize,
-				end = start + ( seq.count * anm.blockSize ),
-				intrpl = seq.intrpl ? THREE.InterpolateLinear : false,
-				name = null;
-
-			switch ( anm.kind ) {
-
-				case SEA3D.Animation.POSITION:
-
-					name = '.position';
-
-					break;
-
-				case SEA3D.Animation.ROTATION:
-
-					name = '.quaternion';
-
-					break;
-
-				case SEA3D.Animation.SCALE:
-
-					name = '.scale';
-
-					break;
-
-				case SEA3D.Animation.COLOR:
-
-					name = '.color';
-
-					break;
-
-				case SEA3D.Animation.MULTIPLIER:
-
-					name = '.intensity';
-
-					break;
-
-				case SEA3D.Animation.FOV:
-
-					name = '.fov';
-
-					break;
-
-				case SEA3D.Animation.OFFSET_U:
-
-					name = '.offset[x]';
-
-					break;
-
-				case SEA3D.Animation.OFFSET_V:
-
-					name = '.offset[y]';
-
-					break;
-
-				case SEA3D.Animation.SCALE_U:
-
-					name = '.repeat[x]';
-
-					break;
-
-				case SEA3D.Animation.SCALE_V:
-
-					name = '.repeat[y]';
-
-					break;
-
-				case SEA3D.Animation.MORPH:
-
-					name = '.morphTargetInfluences[' + anm.name + ']';
-
-					break;
-
-			}
-
-			if ( ! name ) continue;
-
-			switch ( anm.type ) {
-
-				case SEA3D.Stream.BYTE:
-				case SEA3D.Stream.UBYTE:
-				case SEA3D.Stream.INT:
-				case SEA3D.Stream.UINT:
-				case SEA3D.Stream.FLOAT:
-				case SEA3D.Stream.DOUBLE:
-				case SEA3D.Stream.DECIMAL:
-
-					values = data.subarray( start, end );
-					times = new Float32Array( values.length );
-					t = 0;
-
-					for ( k = 0; k < times.length; k ++ ) {
-
-						times[ k ] = t;
-						t += delta;
-
-					}
-
-					tracks.push( new THREE.NumberKeyframeTrack( name, times, values, intrpl ) );
-
-					break;
-
-				case SEA3D.Stream.VECTOR3D:
-
-					values = data.subarray( start, end );
-					times = new Float32Array( values.length / anm.blockSize );
-					t = 0;
-
-					for ( k = 0; k < times.length; k ++ ) {
-
-						times[ k ] = t;
-						t += delta;
-
-					}
-
-					tracks.push( new THREE.VectorKeyframeTrack( name, times, values, intrpl ) );
-
-					break;
-
-				case SEA3D.Stream.VECTOR4D:
-
-					values = data.subarray( start, end );
-					times = new Float32Array( values.length / anm.blockSize );
-					t = 0;
-
-					for ( k = 0; k < times.length; k ++ ) {
-
-						times[ k ] = t;
-						t += delta;
-
-					}
-
-					tracks.push( new THREE.QuaternionKeyframeTrack( name, times, values, intrpl ) );
-
-					break;
-
-				case SEA3D.Stream.INT24:
-				case SEA3D.Stream.UINT24:
-
-					values = new Float32Array( ( end - start ) * 3 );
-					times = new Float32Array( values.length / 3 );
-					t = 0;
-
-					for ( k = 0; k < times.length; k ++ ) {
-
-						values[ ( k * 3 ) ] = ( ( data[ k ] >> 16 ) & 0xFF ) / 255;
-						values[ ( k * 3 ) + 1 ] = ( ( data[ k ] >> 8 ) & 0xFF ) / 255;
-						values[ ( k * 3 ) + 2 ] = ( data[ k ] & 0xFF ) / 255;
-						times[ k ] = t;
-						t += delta;
-
-					}
-
-					tracks.push( new THREE.VectorKeyframeTrack( name, times, values, intrpl ) );//ColorKeyframeTrack
-
-					break;
-
-			}
-
-		}
-
-		animations.push( new THREE.SEA3D.AnimationClip( seq.name, - 1, tracks, seq.repeat ) );
-
-	}
-
-	this.domain.clips = this.clips = this.clips || [];
-	this.clips.push( this.objects[ sea.name + '.anm' ] = sea.tag = animations );
-
-};
-
-//
-//	Skeleton Animation
-//
-
-THREE.SEA3D.prototype.readSkeletonAnimation = function ( sea, skl ) {
-
-	skl = ! skl && sea.metadata && sea.metadata.skeleton ? sea.metadata.skeleton : skl;
-
-	if ( ! skl || sea.tag ) return sea.tag;
-
-	var animations = [], delta = ( 1000 / sea.frameRate ) / 1000;
-
-	for ( var i = 0; i < sea.sequence.length; i ++ ) {
-
-		var seq = sea.sequence[ i ];
-
-		var start = seq.start;
-		var end = start + seq.count;
-
-		var animation = {
-			name: seq.name,
-			fps: sea.frameRate,
-			length: delta * seq.count,
-			hierarchy: []
-		};
-
-		var numJoints = sea.numJoints,
-			raw = sea.raw;
-
-		for ( var j = 0; j < numJoints; j ++ ) {
-
-			var bone = skl.joint[ j ],
-				node = { parent: bone.parentIndex, keys: [] },
-				keys = node.keys,
-				time = 0;
-
-			for ( var frame = start; frame < end; frame ++ ) {
-
-				var idx = ( frame * numJoints * 7 ) + ( j * 7 );
-
-				keys.push( {
-					time: time,
-					pos: [ raw[ idx ], raw[ idx + 1 ], raw[ idx + 2 ] ],
-					rot: [ raw[ idx + 3 ], raw[ idx + 4 ], raw[ idx + 5 ], raw[ idx + 6 ] ],
-					scl: [ 1, 1, 1 ]
-				} );
-
-				time += delta;
-
-			}
-
-			animation.hierarchy[ j ] = node;
-
-		}
-
-		animations.push( THREE.SEA3D.AnimationClip.fromClip( THREE.AnimationClip.parseAnimation( animation, skl.tag ), seq.repeat ) );
-
-	}
-
-	this.domain.clips = this.clips = this.clips || [];
-	this.clips.push( this.objects[ sea.name + '.skla' ] = sea.tag = animations );
-
-};
-
-//
-//	Vertex Animation
-//
-
-THREE.SEA3D.prototype.readVertexAnimation = function ( sea ) {
-
-	var attribs = { position: [] }, targets = [], animations = [], i, j, l;
-
-	for ( i = 0, l = sea.frame.length; i < l; i ++ ) {
-
-		var frame = sea.frame[ i ];
-
-		attribs.position[ i ] = new THREE.Float32BufferAttribute( frame.vertex, 3 );
-
-		if ( frame.normal ) {
-
-			attribs.normal = attribs.normal || [];
-			attribs.normal[ i ] = new THREE.Float32BufferAttribute( frame.normal, 3 );
-
-		}
-
-		targets[ i ] = { name: i };
-
-	}
-
-	for ( i = 0; i < sea.sequence.length; i ++ ) {
-
-		var seq = sea.sequence[ i ];
-		var seqTargets = [];
-
-		for ( j = 0; j < seq.count; j ++ ) {
-
-			seqTargets[ j ] = targets[ seq.start + j ];
-
-		}
-
-		animations.push( THREE.SEA3D.AnimationClip.fromClip( THREE.AnimationClip.CreateFromMorphTargetSequence( seq.name, seqTargets, sea.frameRate ), seq.repeat ) );
-
-	}
-
-	sea.tag = {
-		attribs: attribs,
-		targets: targets,
-		animations: animations
-	};
-
-	this.domain.clips = this.clips = this.clips || [];
-	this.clips.push( this.objects[ sea.name + '.vtxa' ] = sea.tag );
-
-};
-
-//
-//	Selector
-//
-
-THREE.SEA3D.prototype.getModifier = function ( req ) {
-
-	var sea = req.sea;
-
-	switch ( sea.type ) {
-
-		case SEA3D.SkeletonAnimation.prototype.type:
-
-			this.readSkeletonAnimation( sea, req.skeleton );
-
-			break;
-
-		case SEA3D.Animation.prototype.type:
-		case SEA3D.MorphAnimation.prototype.type:
-		case SEA3D.UVWAnimation.prototype.type:
-
-			this.readAnimation( sea );
-
-			break;
-
-		case SEA3D.Morph.prototype.type:
-
-			this.readMorph( sea, req.geometry );
-
-			break;
-
-	}
-
-	return sea.tag;
-
-};
-
-//
-//	Actions
-//
-
-THREE.SEA3D.prototype.applyEnvironment = function ( envMap ) {
-
-	for ( var j = 0, l = this.materials.length; j < l; ++ j ) {
-
-		var mat = this.materials[ j ];
-
-		if ( mat instanceof THREE.MeshStandardMaterial ) {
-
-			if ( mat.envMap ) continue;
-
-			mat.envMap = envMap;
-			mat.envMap.mapping = THREE.CubeReflectionMapping;
-
-			mat.needsUpdate = true;
-
-		}
-
-	}
-
-};
-
-THREE.SEA3D.prototype.readActions = function ( sea ) {
-
-	for ( var i = 0; i < sea.actions.length; i ++ ) {
-
-		var act = sea.actions[ i ];
-
-		switch ( act.kind ) {
-
-			case SEA3D.Actions.ATTRIBUTES:
-
-				this.attribs = this.domain.attribs = act.attributes.tag;
-
-				break;
-
-			case SEA3D.Actions.SCRIPTS:
-
-				this.domain.scripts = this.getJSMList( this.domain, act.scripts );
-
-				if ( this.config.scripts && this.config.runScripts ) this.domain.runJSMList( this.domain );
-
-				break;
-
-			case SEA3D.Actions.CAMERA:
-
-				this.domain.camera = this.camera = act.camera.tag;
-
-				break;
-
-			case SEA3D.Actions.ENVIRONMENT_COLOR:
-
-				this.domain.background = this.background = this.background || {};
-
-				this.background.color = new THREE.Color( act.color );
-
-				break;
-
-			case SEA3D.Actions.ENVIRONMENT:
-
-				this.domain.background = this.background = this.background || {};
-
-				this.background.texture = act.texture.tag;
-
-				if ( this.config.useEnvironment && this.materials != undefined ) {
-
-					this.applyEnvironment( act.texture.tag );
-
-				}
-
-				break;
-
-		}
-
-	}
-
-};
-
-//
-//	Properties
-//
-
-THREE.SEA3D.prototype.updatePropertiesAssets = function ( sea, props ) {
-
-	for ( var name in props ) {
-
-		switch ( props.__type[ name ] ) {
-
-			case SEA3D.Stream.ASSET:
-
-				if ( ! props.__asset ) props.__asset = {};
-				if ( ! props.__asset[ name ] ) props.__asset[ name ] = props[ name ];
-
-				props[ name ] = props.__asset[ name ].tag;
-
-				break;
-
-			case SEA3D.Stream.GROUP:
-
-				props[ name ] = this.updatePropertiesAssets( sea, props[ name ] );
-
-				break;
-
-		}
-
-	}
-
-	return props;
-
-};
-
-THREE.SEA3D.prototype.readProperties = function ( sea ) {
-
-	var props = this.updatePropertiesAssets( sea, sea.props );
-
-	this.domain.properties = this.properties = this.properties || [];
-	this.properties.push( this.objects[ "prop/" + sea.name ] = sea.tag = props );
-
-};
-
-THREE.SEA3D.prototype.readFileInfo = function ( sea ) {
-
-	this.domain.info = this.updatePropertiesAssets( sea, sea.info );
-
-};
-
-//
-//	Events
-//
-
-THREE.SEA3D.Event = {
-	PROGRESS: "sea3d_progress",
-	LOAD_PROGRESS: "sea3d_load",
-	DOWNLOAD_PROGRESS: "sea3d_download",
-	COMPLETE: "sea3d_complete",
-	OBJECT_COMPLETE: "sea3d_object",
-	PARSE_PROGRESS: "parse_progress",
-	PARSE_COMPLETE: "parse_complete",
-	ERROR: "sea3d_error"
-};
-
-THREE.SEA3D.prototype.onProgress = function ( e ) {
-
-	e.status = e.type;
-	e.progress = e.loaded / e.total;
-	e.type = THREE.SEA3D.Event.PROGRESS;
-
-	this.dispatchEvent( e );
-
-};
-
-THREE.SEA3D.prototype.onLoadProgress = function ( e ) {
-
-	e.type = THREE.SEA3D.Event.LOAD_PROGRESS;
-	this.dispatchEvent( e );
-
-	this.onProgress( e );
-
-};
-
-THREE.SEA3D.prototype.onDownloadProgress = function ( e ) {
-
-	e.type = THREE.SEA3D.Event.DOWNLOAD_PROGRESS;
-	this.dispatchEvent( e );
-
-	this.onProgress( e );
-
-};
-
-THREE.SEA3D.prototype.onComplete = function ( e ) {
-
-	e.type = THREE.SEA3D.Event.COMPLETE;
-	this.dispatchEvent( e );
-
-};
-
-THREE.SEA3D.prototype.onCompleteObject = function ( e ) {
-
-	e.type = THREE.SEA3D.Event.OBJECT_COMPLETE;
-	this.dispatchEvent( e );
-
-};
-
-THREE.SEA3D.prototype.onParseProgress = function ( e ) {
-
-	e.type = THREE.SEA3D.Event.PARSE_PROGRESS;
-	this.dispatchEvent( e );
-
-};
-
-THREE.SEA3D.prototype.onParseComplete = function ( e ) {
-
-	e.type = THREE.SEA3D.Event.PARSE_COMPLETE;
-	this.dispatchEvent( e );
-
-};
-
-THREE.SEA3D.prototype.onError = function ( e ) {
-
-	e.type = THREE.SEA3D.Event.ERROR;
-	this.dispatchEvent( e );
-
-};
-
-//
-//	Loader
-//
-
-THREE.SEA3D.prototype.createDomain = function () {
-
-	return this.domain = new THREE.SEA3D.Domain(
-		this.config.id,
-		this.objects = {},
-		this.config.container
-	);
-
-};
-
-THREE.SEA3D.prototype.clone = function ( config, onParseComplete, onParseProgress ) {
-
-	if ( ! this.file.isDone() ) throw new Error( "Previous parse is not completed." );
-
-	this.config.container = config && config.container !== undefined ? config.container : new THREE.Object3D();
-
-	if ( config ) this.loadConfig( config );
-
-	var timeLimit = this.config.timeLimit;
-
-	this.config.timeLimit = config && config.timeLimit !== undefined ? config.timeLimit : Infinity;
-
-	this.parse( onParseComplete, onParseProgress );
-
-	this.config.timeLimit = timeLimit;
-
-	return this.domain;
-
-};
-
-THREE.SEA3D.prototype.loadConfig = function ( config ) {
-
-	for ( var name in config ) {
-
-		this.config[ name ] = config[ name ];
-
-	}
-
-};
-
-THREE.SEA3D.prototype.parse = function ( onParseComplete, onParseProgress ) {
-
-	delete this.cameras;
-	delete this.containers;
-	delete this.lights;
-	delete this.joints;
-	delete this.meshes;
-	delete this.materials;
-	delete this.sprites;
-	delete this.sounds3d;
-	delete this.cubeRenderers;
-	delete this.sounds;
-	delete this.glsl;
-	delete this.dummy;
-	delete this.camera;
-	delete this.background;
-	delete this.properties;
-	delete this.scriptTargets;
-
-	delete this.domain;
-
-	this.createDomain();
-
-	this.setTypeRead();
-
-	this.file.onParseComplete = ( function ( e ) {
-
-		if ( this.config.manager ) this.config.manager.add( this.domain );
-
-		( onParseComplete || this.onParseComplete ).call( this, e );
-
-	} ).bind( this );
-
-	this.file.onParseProgress = onParseProgress || this.onParseProgress;
-
-	// EXTENSIONS
-
-	var i = THREE.SEA3D.EXTENSIONS_LOADER.length;
-
-	while ( i -- ) {
-
-		var loader = THREE.SEA3D.EXTENSIONS_LOADER[ i ];
-
-		if ( loader.parse ) loader.parse.call( this );
-
-	}
-
-	this.file.parse();
-
-	return this.domain;
-
-};
-
-THREE.SEA3D.prototype.onHead = function ( args ) {
-
-	if ( args.sign != 'TJS' ) {
-
-		throw new Error( "Sign '" + args.sign + "' not supported! Use SEA3D Studio to publish or SEA3DLegacy.js" );
-
-	}
-
-};
-
-THREE.SEA3D.EXTENSIONS_LOADER = [];
-THREE.SEA3D.EXTENSIONS_DOMAIN = [];
-
-THREE.SEA3D.prototype.setTypeRead = function () {
-
-	this.file.typeRead = {};
-
-	this.file.typeRead[ SEA3D.Geometry.prototype.type ] = this.readGeometryBuffer;
-	this.file.typeRead[ SEA3D.Mesh.prototype.type ] = this.readMesh;
-	this.file.typeRead[ SEA3D.Sprite.prototype.type ] = this.readSprite;
-	this.file.typeRead[ SEA3D.Container3D.prototype.type ] = this.readContainer3D;
-	this.file.typeRead[ SEA3D.Line.prototype.type ] = this.readLine;
-	this.file.typeRead[ SEA3D.Material.prototype.type ] = this.readMaterial;
-	this.file.typeRead[ SEA3D.Camera.prototype.type ] = this.readCamera;
-	this.file.typeRead[ SEA3D.OrthographicCamera.prototype.type ] = this.readOrthographicCamera;
-	this.file.typeRead[ SEA3D.SkeletonLocal.prototype.type ] = this.readSkeletonLocal;
-	this.file.typeRead[ SEA3D.SkeletonAnimation.prototype.type ] = this.readSkeletonAnimation;
-	this.file.typeRead[ SEA3D.JointObject.prototype.type ] = this.readJointObject;
-	this.file.typeRead[ SEA3D.CubeMap.prototype.type ] = this.readCubeMap;
-	this.file.typeRead[ SEA3D.CubeRender.prototype.type ] = this.readCubeRender;
-	this.file.typeRead[ SEA3D.Animation.prototype.type ] =
-	this.file.typeRead[ SEA3D.MorphAnimation.prototype.type ] =
-	this.file.typeRead[ SEA3D.UVWAnimation.prototype.type ] = this.readAnimation;
-	this.file.typeRead[ SEA3D.SoundPoint.prototype.type ] = this.readSoundPoint;
-	this.file.typeRead[ SEA3D.TextureURL.prototype.type ] = this.readTextureURL;
-	this.file.typeRead[ SEA3D.CubeMapURL.prototype.type ] = this.readCubeMapURL;
-	this.file.typeRead[ SEA3D.TextureUpdate.prototype.type ] = this.readTextureUpdate;
-	this.file.typeRead[ SEA3D.Morph.prototype.type ] = this.readMorph;
-	this.file.typeRead[ SEA3D.VertexAnimation.prototype.type ] = this.readVertexAnimation;
-	this.file.typeRead[ SEA3D.Actions.prototype.type ] = this.readActions;
-	this.file.typeRead[ SEA3D.FileInfo.prototype.type ] = this.readFileInfo;
-	this.file.typeRead[ SEA3D.Properties.prototype.type ] = this.readProperties;
-
-	if ( this.config.dummys ) {
-
-		this.file.typeRead[ SEA3D.Dummy.prototype.type ] = this.readDummy;
-
-	}
-
-	if ( this.config.scripts ) {
-
-		this.file.typeRead[ SEA3D.ScriptURL.prototype.type ] = this.readScriptURL;
-		this.file.typeRead[ SEA3D.JavaScriptMethod.prototype.type ] = this.readJavaScriptMethod;
-
-	}
-
-	if ( this.config.lights ) {
-
-		this.file.typeRead[ SEA3D.PointLight.prototype.type ] = this.readPointLight;
-		this.file.typeRead[ SEA3D.DirectionalLight.prototype.type ] = this.readDirectionalLight;
-		this.file.typeRead[ SEA3D.HemisphereLight.prototype.type ] = this.readHemisphereLight;
-		this.file.typeRead[ SEA3D.AmbientLight.prototype.type ] = this.readAmbientLight;
-
-	}
-
-	// UNIVERSAL
-
-	this.file.typeRead[ SEA3D.JPEG.prototype.type ] =
-	this.file.typeRead[ SEA3D.JPEG_XR.prototype.type ] =
-	this.file.typeRead[ SEA3D.PNG.prototype.type ] =
-	this.file.typeRead[ SEA3D.GIF.prototype.type ] = this.readTexture;
-	this.file.typeRead[ SEA3D.MP3.prototype.type ] = this.readSound;
-	this.file.typeRead[ SEA3D.GLSL.prototype.type ] = this.readGLSL;
-
-	// EXTENSIONS
-
-	var i = THREE.SEA3D.EXTENSIONS_LOADER.length;
-
-	while ( i -- ) {
-
-		var loader = THREE.SEA3D.EXTENSIONS_LOADER[ i ];
-
-		if ( loader.setTypeRead ) loader.setTypeRead.call( this );
-
-	}
-
-};
-
-THREE.SEA3D.prototype.load = function ( data ) {
-
-	this.file = new SEA3D.File();
-	this.file.scope = this;
-	this.file.config = this.config;
-	this.file.onProgress = this.onLoadProgress.bind( this );
-	this.file.onCompleteObject = this.onCompleteObject.bind( this );
-	this.file.onDownloadProgress = this.onDownloadProgress.bind( this );
-	this.file.onParseProgress = this.onParseProgress.bind( this );
-	this.file.onParseComplete = this.onParseComplete.bind( this );
-	this.file.onError = this.onError.bind( this );
-	this.file.onHead = this.onHead.bind( this );
-
-	this.file.onComplete = ( function ( e ) {
-
-		if ( this.config.manager ) this.config.manager.add( this.domain );
-
-		this.onComplete.call( this, e );
-
-	} ).bind( this );
-
-	// SEA3D
-
-	this.createDomain();
-
-	this.setTypeRead();
-
-	if ( data === undefined ) return;
-
-	if ( typeof data === "string" ) this.file.load( data );
-	else this.file.read( data );
-
-};
+/**
+ * 	SEA3D for Three.JS
+ * 	@author Sunag / http://www.sunag.com.br/
+ */
+
+'use strict';
+
+//
+//
+//	SEA3D
+//
+
+THREE.SEA3D = function ( config ) {
+
+	this.config = {
+		id: "",
+		scripts: true,
+		runScripts: true,
+		autoPlay: false,
+		dummys: true,
+		multiplier: 1,
+		bounding: true,
+		audioRolloffFactor: 10,
+		lights: true,
+		useEnvironment: true,
+		useVertexTexture: true,
+		forceStatic: false,
+		streaming: true,
+		async: true,
+		paths: {},
+		timeLimit: 10
+	};
+
+	if ( config ) this.loadConfig( config );
+
+};
+
+//
+//	Polyfills
+//
+
+if ( THREE.Float32BufferAttribute === undefined ) {
+
+	THREE.Float32BufferAttribute = THREE.Float32Attribute;
+
+}
+
+THREE.SEA3D.useMultiMaterial = THREE.MultiMaterial.prototype.isMultiMaterial;
+
+//
+//	Config
+//
+
+THREE.SEA3D.MTXBUF = new THREE.Matrix4();
+THREE.SEA3D.VECBUF = new THREE.Vector3();
+THREE.SEA3D.QUABUF = new THREE.Quaternion();
+
+THREE.SEA3D.BACKGROUND_COLOR = 0x333333;
+THREE.SEA3D.HELPER_COLOR = 0x9AB9E5;
+THREE.SEA3D.RTT_SIZE = 512;
+
+THREE.SEA3D.identityMatrixScale = function () {
+
+	var scl = new THREE.Vector3();
+
+	return function identityMatrixScale( matrix ) {
+
+		scl.setFromMatrixScale( matrix );
+
+		return matrix.scale( scl.set( 1 / scl.x, 1 / scl.y, 1 / scl.z ) );
+
+	};
+
+}();
+
+THREE.SEA3D.prototype = Object.assign( Object.create( THREE.EventDispatcher.prototype ), {
+
+	constructor: THREE.SEA3D,
+
+	setShadowMap: function ( light ) {
+
+		light.shadow.mapSize.width = 2048;
+		light.shadow.mapSize.height = 1024;
+
+		light.castShadow = true;
+
+		light.shadow.camera.left = - 200;
+		light.shadow.camera.right = 200;
+		light.shadow.camera.top = 200;
+		light.shadow.camera.bottom = - 200;
+
+		light.shadow.camera.near = 1;
+		light.shadow.camera.far = 3000;
+		light.shadow.camera.fov = 45;
+
+		light.shadow.bias = - 0.001;
+
+	}
+
+} );
+
+Object.defineProperties( THREE.SEA3D.prototype, {
+
+	container: {
+
+		set: function ( val ) {
+
+			this.config.container = val;
+
+		},
+
+		get: function () {
+
+			return this.config.container;
+
+		}
+
+	},
+
+	elapsedTime: {
+
+		get: function () {
+
+			return this.file.timer.elapsedTime;
+
+		}
+
+	}
+
+} );
+
+//
+//	Domain
+//
+
+THREE.SEA3D.Domain = function ( id, objects, container ) {
+
+	this.id = id;
+	this.objects = objects;
+	this.container = container;
+
+	this.sources = [];
+	this.local = {};
+
+	this.scriptTargets = [];
+
+	this.events = new THREE.EventDispatcher();
+
+};
+
+THREE.SEA3D.Domain.global = {};
+
+THREE.SEA3D.Domain.prototype = Object.assign( Object.create( THREE.EventDispatcher.prototype ), {
+
+	constructor: THREE.SEA3D.Domain,
+
+	add: function ( src ) {
+
+		this.sources.push( src );
+
+	},
+
+	remove: function ( src ) {
+
+		this.sources.splice( this.sources.indexOf( src ), 1 );
+
+	},
+
+	contains: function ( src ) {
+
+		return this.sources.indexOf( src ) != - 1;
+
+	},
+
+	addEventListener: function ( type, listener ) {
+
+		this.events.addEventListener( type, listener );
+
+	},
+
+	hasEventListener: function ( type, listener ) {
+
+		return this.events.hasEventListener( type, listener );
+
+	},
+
+	removeEventListener: function ( type, listener ) {
+
+		this.events.removeEventListener( type, listener );
+
+	},
+
+	print: function () {
+
+		console.log.apply( console, arguments );
+
+	},
+
+	watch: function () {
+
+		console.log.apply( console, 'watch:', arguments );
+
+	},
+
+	runScripts: function () {
+
+		for ( var i = 0; i < this.scriptTargets.length; i ++ ) {
+
+			this.runJSMList( this.scriptTargets[ i ] );
+
+		}
+
+	},
+
+	runJSMList: function ( target ) {
+
+		var scripts = target.scripts;
+
+		for ( var i = 0; i < scripts.length; i ++ ) {
+
+			this.runJSM( target, scripts[ i ] );
+
+		}
+
+		return scripts;
+
+	},
+
+	runJSM: function ( target, script ) {
+
+		var include = {
+			print: this.print,
+			watch: this.watch,
+			sea3d: this,
+			scene: this.container,
+			source: new THREE.SEA3D.ScriptDomain( this, target instanceof THREE.SEA3D.Domain )
+		};
+
+		Object.freeze( include.source );
+
+		THREE.SEA3D.ScriptHandler.add( include.source );
+
+		try {
+
+			this.methods[ script.method ](
+				include,
+				this.getReference,
+				THREE.SEA3D.Domain.global,
+				this.local,
+				target,
+				script.params
+			);
+
+		} catch ( e ) {
+
+			console.error( 'SEA3D JavaScript: Error running method "' + script.method + '".' );
+			console.error( e );
+
+		}
+
+	},
+
+	getReference: function ( ns ) {
+
+		return eval( ns );
+
+	},
+
+	disposeList: function ( list ) {
+
+		if ( ! list || ! list.length ) return;
+
+		list = list.concat();
+
+		var i = list.length;
+
+		while ( i -- ) {
+
+			list[ i ].dispose();
+
+		}
+
+	},
+
+	dispatchEvent: function ( event ) {
+
+		event.domain = this;
+
+		var sources = this.sources.concat(),
+			i = sources.length;
+
+		while ( i -- ) {
+
+			sources[ i ].dispatchEvent( event );
+
+		}
+
+		this.events.dispatchEvent( event );
+
+	},
+
+	dispose: function () {
+
+		this.disposeList( this.sources );
+
+		while ( this.container.children.length ) {
+
+			this.container.remove( this.container.children[ 0 ] );
+
+		}
+
+		var i = THREE.SEA3D.EXTENSIONS_DOMAIN.length;
+
+		while ( i -- ) {
+
+			var domain = THREE.SEA3D.EXTENSIONS_DOMAIN[ i ];
+
+			if ( domain.dispose ) domain.dispose.call( this );
+
+		}
+
+		this.disposeList( this.materials );
+		this.disposeList( this.dummys );
+
+		this.dispatchEvent( { type: "dispose" } );
+
+	}
+} );
+
+//
+//	Domain Manager
+//
+
+THREE.SEA3D.DomainManager = function ( autoDisposeRootDomain ) {
+
+	this.domains = [];
+	this.autoDisposeRootDomain = autoDisposeRootDomain !== undefined ? autoDisposeRootDomain : true;
+
+};
+
+Object.assign( THREE.SEA3D.DomainManager.prototype, {
+
+	onDisposeDomain: function ( e ) {
+
+		this.remove( e.domain );
+
+		if ( this.autoDisposeRootDomain && this.domains.length == 1 ) {
+
+			this.dispose();
+
+		}
+
+	},
+
+	add: function ( domain ) {
+
+		this._onDisposeDomain = this._onDisposeDomain || this.onDisposeDomain.bind( this );
+
+		domain.on( "dispose", this._onDisposeDomain );
+
+		this.domains.push( domain );
+
+		this.textures = this.textures || domain.textures;
+		this.cubemaps = this.cubemaps || domain.cubemaps;
+		this.geometries = this.geometries || domain.geometries;
+
+	},
+
+	remove: function ( domain ) {
+
+		domain.removeEvent( "dispose", this._onDisposeDomain );
+
+		this.domains.splice( this.domains.indexOf( domain ), 1 );
+
+	},
+
+	contains: function ( domain ) {
+
+		return this.domains.indexOf( domain ) != - 1;
+
+	},
+
+	disposeList: function ( list ) {
+
+		if ( ! list || ! list.length ) return;
+
+		list = list.concat();
+
+		var i = list.length;
+
+		while ( i -- ) {
+
+			list[ i ].dispose();
+
+		}
+
+	},
+
+	dispose: function () {
+
+		this.disposeList( this.domains );
+		this.disposeList( this.textures );
+		this.disposeList( this.cubemaps );
+		this.disposeList( this.geometries );
+
+	}
+
+} );
+
+//
+//	Script ( closure for private functions )
+//
+
+THREE.SEA3D.ScriptDomain = function ( domain, root ) {
+
+	domain = domain || new THREE.SEA3D.Domain();
+	domain.add( this );
+
+	var events = new THREE.EventDispatcher();
+
+	this.getId = function () {
+
+		return domain.id;
+
+	};
+
+	this.isRoot = function () {
+
+		return root;
+
+	};
+
+	this.addEventListener = function ( type, listener ) {
+
+		events.addEventListener( type, listener );
+
+	};
+
+	this.hasEventListener = function ( type, listener ) {
+
+		return events.hasEventListener( type, listener );
+
+	};
+
+	this.removeEventListener = function ( type, listener ) {
+
+		events.removeEventListener( type, listener );
+
+	};
+
+	this.dispatchEvent = function ( event ) {
+
+		event.script = this;
+
+		events.dispatchEvent( event );
+
+	};
+
+	this.dispose = function () {
+
+		domain.remove( this );
+
+		if ( root ) domain.dispose();
+
+		this.dispatchEvent( { type: "dispose" } );
+
+	};
+
+};
+
+//
+//	Script Manager ( closure for private functions )
+//
+
+THREE.SEA3D.ScriptManager = function () {
+
+	this.scripts = [];
+
+	var onDisposeScript = ( function ( e ) {
+
+		this.remove( e.script );
+
+	} ).bind( this );
+
+	this.add = function ( src ) {
+
+		src.addEventListener( "dispose", onDisposeScript );
+
+		this.scripts.push( src );
+
+	};
+
+	this.remove = function ( src ) {
+
+		src.removeEventListener( "dispose", onDisposeScript );
+
+		this.scripts.splice( this.scripts.indexOf( src ), 1 );
+
+	};
+
+	this.contains = function ( src ) {
+
+		return this.scripts.indexOf( src ) > - 1;
+
+	};
+
+	this.dispatchEvent = function ( event ) {
+
+		var scripts = this.scripts.concat(),
+			i = scripts.length;
+
+		while ( i -- ) {
+
+			scripts[ i ].dispatchEvent( event );
+
+		}
+
+	};
+
+};
+
+//
+//	Script Handler
+//
+
+THREE.SEA3D.ScriptHandler = new THREE.SEA3D.ScriptManager();
+
+THREE.SEA3D.ScriptHandler.dispatchUpdate = function ( delta ) {
+
+	this.dispatchEvent( {
+		type: "update",
+		delta: delta
+	} );
+
+};
+
+//
+//	Animation Clip
+//
+
+THREE.SEA3D.AnimationClip = function ( name, duration, tracks, repeat ) {
+
+	THREE.AnimationClip.call( this, name, duration, tracks );
+
+	this.repeat = repeat !== undefined ? repeat : true;
+
+};
+
+THREE.SEA3D.AnimationClip.fromClip = function ( clip, repeat ) {
+
+	return new THREE.SEA3D.AnimationClip( clip.name, clip.duration, clip.tracks, repeat );
+
+};
+
+THREE.SEA3D.AnimationClip.prototype = Object.assign( Object.create( THREE.AnimationClip.prototype ), {
+
+	constructor: THREE.SEA3D.AnimationClip
+
+} );
+
+//
+//	Animation
+//
+
+THREE.SEA3D.Animation = function ( clip, timeScale ) {
+
+	this.clip = clip;
+	this.timeScale = timeScale !== undefined ? timeScale : 1;
+
+};
+
+THREE.SEA3D.Animation.COMPLETE = "animationComplete";
+
+THREE.SEA3D.Animation.prototype = Object.assign( Object.create( THREE.EventDispatcher.prototype ), {
+
+	constructor: THREE.SEA3D.Animation,
+
+	onComplete: function ( scope ) {
+
+		this.dispatchEvent( { type: THREE.SEA3D.Animation.COMPLETE, target: this } );
+
+
+	}
+
+} );
+
+Object.defineProperties( THREE.SEA3D.Animation.prototype, {
+
+	name: {
+
+		get: function () {
+
+			return this.clip.name;
+
+		}
+
+	},
+
+	repeat: {
+
+		get: function () {
+
+			return this.clip.repeat;
+
+		}
+
+	},
+
+	duration: {
+
+		get: function () {
+
+			return this.clip.duration;
+
+		}
+
+	},
+
+	mixer: {
+
+		set: function ( val ) {
+
+			if ( this.mx ) {
+
+				this.mx.uncacheClip( this.clip );
+				delete this.mx;
+
+			}
+
+			if ( val ) {
+
+				this.mx = val;
+				this.mx.clipAction( this.clip );
+
+			}
+
+		},
+
+		get: function () {
+
+			return this.mx;
+
+		}
+
+	}
+
+} );
+
+//
+//	Animator
+//
+
+THREE.SEA3D.Animator = function ( clips, mixer ) {
+
+	this.updateAnimations( clips, mixer );
+
+	this.clone = function ( scope ) {
+
+		return new this.constructor( this.clips, new THREE.AnimationMixer( scope ) ).copyFrom( this );
+
+	}.bind( this );
+
+};
+
+Object.assign( THREE.SEA3D.Animator.prototype, {
+
+	update: function ( dt ) {
+
+		this.mixer.update( dt || 0 );
+
+		if ( this.currentAnimationAction && this.currentAnimationAction.paused ) {
+
+			this.pause();
+
+			if ( this.currentAnimation ) {
+
+				this.currentAnimation.onComplete( this );
+
+			}
+
+		}
+
+		return this;
+
+	},
+
+	updateAnimations: function ( clips, mixer ) {
+
+		if ( this.playing ) this.stop();
+
+		if ( this.mixer ) THREE.SEA3D.AnimationHandler.remove( this );
+
+		this.mixer = mixer;
+
+		this.relative = false;
+		this.playing = false;
+		this.paused = false;
+
+		this.timeScale = 1;
+
+		this.animations = [];
+		this.animation = {};
+
+		this.clips = [];
+
+		if ( clips ) {
+
+			for ( var i = 0; i < clips.length; i ++ ) {
+
+				this.addAnimation( clips[ i ] );
+
+			}
+
+		}
+
+		return this;
+
+	},
+
+	addAnimation: function ( animation ) {
+
+		if ( animation instanceof THREE.AnimationClip ) {
+
+			this.clips.push( animation );
+
+			animation = new THREE.SEA3D.Animation( animation );
+
+		}
+
+		this.animations.push( animation );
+		this.animation[ animation.name ] = animation;
+
+		animation.mixer = this.mixer;
+
+		return animation;
+
+	},
+
+	removeAnimation: function ( animation ) {
+
+		if ( animation instanceof THREE.AnimationClip ) {
+
+			animation = this.getAnimationByClip( animation );
+
+		}
+
+		this.clips.splice( this.clips.indexOf( animation.clip ), 1 );
+
+		delete this.animation[ animation.name ];
+		this.animations.splice( this.animations.indexOf( animation ), 1 );
+
+		animation.mixer = null;
+
+		return animation;
+
+	},
+
+	getAnimationByClip: function ( clip ) {
+
+		for ( var i = 0; i < this.animations.length; i ++ ) {
+
+			if ( this.animations[ i ].clip === clip ) return clip;
+
+		}
+
+	},
+
+	getAnimationByName: function ( name ) {
+
+		return typeof name === "number" ? this.animations[ name ] : this.animation[ name ];
+
+	},
+
+	setAnimationWeight: function ( name, val ) {
+
+		this.mixer.clipAction( this.getAnimationByName( name ).clip ).setEffectiveWeight( val );
+
+	},
+
+	getAnimationWeight: function ( name ) {
+
+		return this.mixer.clipAction( this.getAnimationByName( name ).clip ).getEffectiveWeight();
+
+	},
+
+	pause: function () {
+
+		if ( this.playing && this.currentAnimation ) {
+
+			THREE.SEA3D.AnimationHandler.remove( this );
+
+			this.playing = false;
+
+		}
+
+		return this;
+
+	},
+
+	resume: function () {
+
+		if ( ! this.playing && this.currentAnimation ) {
+
+			THREE.SEA3D.AnimationHandler.add( this );
+
+			this.playing = true;
+
+		}
+
+		return this;
+
+	},
+
+	setTimeScale: function ( val ) {
+
+		this.timeScale = val;
+
+		if ( this.currentAnimationAction ) this.updateTimeScale();
+
+		return this;
+
+	},
+
+	getTimeScale: function () {
+
+		return this.timeScale;
+
+	},
+
+	updateTimeScale: function () {
+
+		this.currentAnimationAction.setEffectiveTimeScale( this.timeScale * ( this.currentAnimation ? this.currentAnimation.timeScale : 1 ) );
+
+		return this;
+
+	},
+
+	play: function ( name, crossfade, offset, weight ) {
+
+		var animation = this.getAnimationByName( name );
+
+		if ( ! animation ) throw new Error( 'Animation "' + name + '" not found.' );
+
+		if ( animation == this.currentAnimation ) {
+
+			if ( offset !== undefined || ! animation.repeat ) this.currentAnimationAction.time = offset !== undefined ? offset :
+				( this.currentAnimationAction.timeScale >= 0 ? 0 : this.currentAnimation.duration );
+
+			this.currentAnimationAction.setEffectiveWeight( weight !== undefined ? weight : 1 );
+			this.currentAnimationAction.paused = false;
+
+			return this.resume();
+
+		} else {
+
+			this.previousAnimation = this.currentAnimation;
+			this.currentAnimation = animation;
+
+			this.previousAnimationAction = this.currentAnimationAction;
+			this.currentAnimationAction = this.mixer.clipAction( animation.clip ).setLoop( animation.repeat ? THREE.LoopRepeat : THREE.LoopOnce, Infinity ).reset();
+			this.currentAnimationAction.clampWhenFinished = ! animation.repeat;
+			this.currentAnimationAction.paused = false;
+
+			this.updateTimeScale();
+
+			if ( offset !== undefined || ! animation.repeat ) this.currentAnimationAction.time = offset !== undefined ? offset :
+				( this.currentAnimationAction.timeScale >= 0 ? 0 : this.currentAnimation.duration );
+
+			this.currentAnimationAction.setEffectiveWeight( weight !== undefined ? weight : 1 );
+
+			this.currentAnimationAction.play();
+
+			if ( ! this.playing ) this.mixer.update( 0 );
+
+			this.playing = true;
+
+			if ( this.previousAnimation ) this.previousAnimationAction.crossFadeTo( this.currentAnimationAction, crossfade || 0, false );
+
+			THREE.SEA3D.AnimationHandler.add( this );
+
+		}
+
+		return this;
+
+	},
+
+	stop: function () {
+
+		if ( this.playing ) THREE.SEA3D.AnimationHandler.remove( this );
+
+		if ( this.currentAnimation ) {
+
+			this.currentAnimationAction.stop();
+
+			this.previousAnimation = this.currentAnimation;
+			this.previousAnimationAction = this.currentAnimationAction;
+
+			delete this.currentAnimationAction;
+			delete this.currentAnimation;
+
+			this.playing = false;
+
+		}
+
+		return this;
+
+	},
+
+	playw: function ( name, weight ) {
+
+		if ( ! this.playing && ! this.paused ) THREE.SEA3D.AnimationHandler.add( this );
+
+		var animation = this.getAnimationByName( name );
+
+		this.playing = true;
+
+		var clip = this.mixer.clipAction( animation.clip );
+		clip.setLoop( animation.repeat ? THREE.LoopRepeat : THREE.LoopOnce, Infinity ).reset();
+		clip.clampWhenFinished = ! animation.repeat;
+		clip.paused = false;
+
+		clip.setEffectiveWeight( weight ).play();
+
+		return clip;
+
+	},
+
+	crossFade: function ( fromAnimName, toAnimName, duration, wrap ) {
+
+		this.mixer.stopAllAction();
+
+		var fromAction = this.playw( fromAnimName, 1 );
+		var toAction = this.playw( toAnimName, 1 );
+
+		fromAction.crossFadeTo( toAction, duration, wrap !== undefined ? wrap : false );
+
+		return this;
+
+	},
+
+	stopAll: function () {
+
+		this.stop().mixer.stopAllAction();
+
+		this.playing = false;
+
+		return this;
+
+	},
+
+	unPauseAll: function () {
+
+		this.mixer.timeScale = 1;
+
+		this.playing = true;
+		this.paused = false;
+
+		return this;
+
+	},
+
+	pauseAll: function () {
+
+		this.mixer.timeScale = 0;
+
+		this.playing = false;
+		this.paused = true;
+
+		return this;
+
+	},
+
+	setRelative: function ( val ) {
+
+		if ( this.relative == val ) return;
+
+		this.stop();
+
+		this.relative = val;
+
+		return this;
+
+	},
+
+	getRelative: function () {
+
+		return this.relative;
+
+	},
+
+	copyFrom: function ( scope ) {
+
+		for ( var i = 0; i < this.animations.length; i ++ ) {
+
+			this.animations[ i ].timeScale = scope.animations[ i ].timeScale;
+
+		}
+
+		return this;
+
+	}
+
+} );
+
+//
+//	Object3D Animator
+//
+
+THREE.SEA3D.Object3DAnimator = function ( clips, object3d ) {
+
+	this.object3d = object3d;
+
+	THREE.SEA3D.Animator.call( this, clips, new THREE.AnimationMixer( object3d ) );
+
+	this.clone = function ( scope ) {
+
+		return new this.constructor( this.clips, scope ).copyFrom( this );
+
+	}.bind( this );
+
+};
+
+THREE.SEA3D.Object3DAnimator.prototype = Object.assign( Object.create( THREE.SEA3D.Animator.prototype ), {
+
+	constructor: THREE.SEA3D.Object3DAnimator,
+
+	stop: function () {
+
+		if ( this.currentAnimation ) {
+
+			var animate = this.object3d.animate;
+
+			if ( animate && this instanceof THREE.SEA3D.Object3DAnimator ) {
+
+				animate.position.set( 0, 0, 0 );
+				animate.quaternion.set( 0, 0, 0, 1 );
+				animate.scale.set( 1, 1, 1 );
+
+			}
+
+		}
+
+		THREE.SEA3D.Animator.prototype.stop.call( this );
+
+	},
+
+	setRelative: function ( val ) {
+
+		THREE.SEA3D.Animator.prototype.setRelative.call( this, val );
+
+		this.object3d.setAnimator( this.relative );
+
+		this.updateAnimations( this.clips, new THREE.AnimationMixer( this.relative ? this.object3d.animate : this.object3d ) );
+
+	}
+
+} );
+
+//
+//	Camera Animator
+//
+
+THREE.SEA3D.CameraAnimator = function ( clips, object3d ) {
+
+	THREE.SEA3D.Object3DAnimator.call( this, clips, object3d );
+
+};
+
+THREE.SEA3D.CameraAnimator.prototype = Object.assign( Object.create( THREE.SEA3D.Object3DAnimator.prototype ), {
+
+	constructor: THREE.SEA3D.CameraAnimator
+
+} );
+
+//
+//	Sound Animator
+//
+
+THREE.SEA3D.SoundAnimator = function ( clips, object3d ) {
+
+	THREE.SEA3D.Object3DAnimator.call( this, clips, object3d );
+
+};
+
+THREE.SEA3D.SoundAnimator.prototype = Object.assign( Object.create( THREE.SEA3D.Object3DAnimator.prototype ), {
+
+	constructor: THREE.SEA3D.SoundAnimator
+
+} );
+
+//
+//	Light Animator
+//
+
+THREE.SEA3D.LightAnimator = function ( clips, object3d ) {
+
+	THREE.SEA3D.Object3DAnimator.call( this, clips, object3d );
+
+};
+
+THREE.SEA3D.LightAnimator.prototype = Object.assign( Object.create( THREE.SEA3D.Object3DAnimator.prototype ), {
+
+	constructor: THREE.SEA3D.LightAnimator
+
+} );
+
+//
+//	Container
+//
+
+THREE.SEA3D.Object3D = function ( ) {
+
+	THREE.Object3D.call( this );
+
+};
+
+THREE.SEA3D.Object3D.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), {
+
+	constructor: THREE.SEA3D.Object3D,
+
+	// Relative Animation Extension ( Only used if relative animation is enabled )
+	// TODO: It can be done with shader
+
+	updateAnimateMatrix: function ( force ) {
+
+		if ( this.matrixAutoUpdate === true ) this.updateMatrix();
+
+		if ( this.matrixWorldNeedsUpdate === true || force === true ) {
+
+			if ( this.parent === null ) {
+
+				this.matrixWorld.copy( this.matrix );
+
+			} else {
+
+				this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+
+			}
+
+			this.animate.updateMatrix();
+
+			this.matrixWorld.multiplyMatrices( this.matrixWorld, this.animate.matrix );
+
+			this.matrixWorldNeedsUpdate = false;
+
+			force = true;
+
+		}
+
+		// update children
+
+		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+			this.children[ i ].updateMatrixWorld( force );
+
+		}
+
+	},
+
+	setAnimator: function ( val ) {
+
+		if ( this.getAnimator() == val )
+			return;
+
+		if ( val ) {
+
+			this.animate = new THREE.Object3D();
+
+			this.updateMatrixWorld = THREE.SEA3D.Object3D.prototype.updateAnimateMatrix;
+
+		} else {
+
+			delete this.animate;
+
+			this.updateMatrixWorld = THREE.Object3D.prototype.updateMatrixWorld;
+
+		}
+
+		this.matrixWorldNeedsUpdate = true;
+
+	},
+
+	getAnimator: function () {
+
+		return this.animate != undefined;
+
+	},
+
+	copy: function ( source ) {
+
+		THREE.Object3D.prototype.copy.call( this, source );
+
+		this.attribs = source.attribs;
+		this.scripts = source.scripts;
+
+		if ( source.animator ) this.animator = source.animator.clone( this );
+
+		return this;
+
+	}
+
+} );
+
+//
+//	Dummy
+//
+
+THREE.SEA3D.Dummy = function ( width, height, depth ) {
+
+	this.width = width != undefined ? width : 100;
+	this.height = height != undefined ? height : 100;
+	this.depth = depth != undefined ? depth : 100;
+
+	var geo = new THREE.BoxGeometry( this.width, this.height, this.depth, 1, 1, 1 );
+
+	geo.computeBoundingBox();
+	geo.computeBoundingSphere();
+
+	THREE.Mesh.call( this, geo, THREE.SEA3D.Dummy.MATERIAL );
+
+};
+
+THREE.SEA3D.Dummy.MATERIAL = new THREE.MeshBasicMaterial( { wireframe: true, color: THREE.SEA3D.HELPER_COLOR } );
+
+THREE.SEA3D.Dummy.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), THREE.SEA3D.Object3D.prototype, {
+
+	constructor: THREE.SEA3D.Dummy,
+
+	copy: function ( source ) {
+
+		THREE.Mesh.prototype.copy.call( this, source );
+
+		this.attribs = source.attribs;
+		this.scripts = source.scripts;
+
+		if ( source.animator ) this.animator = source.animator.clone( this );
+
+		return this;
+
+	},
+
+	dispose: function () {
+
+		this.geometry.dispose();
+
+	}
+
+} );
+
+//
+//	Mesh
+//
+
+THREE.SEA3D.Mesh = function ( geometry, material ) {
+
+	THREE.Mesh.call( this, geometry, material );
+
+};
+
+THREE.SEA3D.Mesh.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), THREE.SEA3D.Object3D.prototype, {
+
+	constructor: THREE.SEA3D.Mesh,
+
+	setWeight: function ( name, val ) {
+
+		var index = typeof name === "number" ? name : this.morphTargetDictionary[ name ];
+
+		this.morphTargetInfluences[ index ] = val;
+
+	},
+
+	getWeight: function ( name ) {
+
+		var index = typeof name === "number" ? name : this.morphTargetDictionary[ name ];
+
+		return this.morphTargetInfluences[ index ];
+
+	},
+
+	copy: function ( source ) {
+
+		THREE.Mesh.prototype.copy.call( this, source );
+
+		this.attribs = source.attribs;
+		this.scripts = source.scripts;
+
+		if ( source.animator ) this.animator = source.animator.clone( this );
+
+		return this;
+
+	}
+
+} );
+
+//
+//	Skinning
+//
+
+THREE.SEA3D.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
+
+	THREE.SkinnedMesh.call( this, geometry, material, useVertexTexture );
+
+	this.updateAnimations( geometry.animations, new THREE.AnimationMixer( this ) );
+
+};
+
+THREE.SEA3D.SkinnedMesh.prototype = Object.assign( Object.create( THREE.SkinnedMesh.prototype ), THREE.SEA3D.Mesh.prototype, THREE.SEA3D.Animator.prototype, {
+
+	constructor: THREE.SEA3D.SkinnedMesh,
+
+	boneByName: function ( name ) {
+
+		var bones = this.skeleton.bones;
+
+		for ( var i = 0, bl = bones.length; i < bl; i ++ ) {
+
+			if ( name == bones[ i ].name )
+				return bones[ i ];
+
+		}
+
+	},
+
+	copy: function ( source ) {
+
+		THREE.SkinnedMesh.prototype.copy.call( this, source );
+
+		this.attribs = source.attribs;
+		this.scripts = source.scripts;
+
+		if ( source.animator ) this.animator = source.animator.clone( this );
+
+		return this;
+
+	}
+
+} );
+
+//
+//	Vertex Animation
+//
+
+THREE.SEA3D.VertexAnimationMesh = function ( geometry, material ) {
+
+	THREE.Mesh.call( this, geometry, material );
+
+	this.type = 'MorphAnimMesh';
+
+	this.updateAnimations( geometry.animations, new THREE.AnimationMixer( this ) );
+
+};
+
+THREE.SEA3D.VertexAnimationMesh.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), THREE.SEA3D.Mesh.prototype, THREE.SEA3D.Animator.prototype, {
+
+	constructor: THREE.SEA3D.VertexAnimationMesh,
+
+	copy: function ( source ) {
+
+		THREE.Mesh.prototype.copy.call( this, source );
+
+		this.attribs = source.attribs;
+		this.scripts = source.scripts;
+
+		if ( source.animator ) this.animator = source.animator.clone( this );
+
+		return this;
+
+	}
+
+} );
+
+//
+//	Camera
+//
+
+THREE.SEA3D.Camera = function ( fov, aspect, near, far ) {
+
+	THREE.PerspectiveCamera.call( this, fov, aspect, near, far );
+
+};
+
+THREE.SEA3D.Camera.prototype = Object.assign( Object.create( THREE.PerspectiveCamera.prototype ), THREE.SEA3D.Object3D.prototype, {
+
+	constructor: THREE.SEA3D.Camera,
+
+	copy: function ( source ) {
+
+		THREE.PerspectiveCamera.prototype.copy.call( this, source );
+
+		this.attribs = source.attribs;
+		this.scripts = source.scripts;
+
+		if ( source.animator ) this.animator = source.animator.clone( this );
+
+		return this;
+
+	}
+
+} );
+
+//
+//	Orthographic Camera
+//
+
+THREE.SEA3D.OrthographicCamera = function ( left, right, top, bottom, near, far ) {
+
+	THREE.OrthographicCamera.call( this, left, right, top, bottom, near, far );
+
+};
+
+THREE.SEA3D.OrthographicCamera.prototype = Object.assign( Object.create( THREE.OrthographicCamera.prototype ), THREE.SEA3D.Object3D.prototype, {
+
+	constructor: THREE.SEA3D.OrthographicCamera,
+
+	copy: function ( source ) {
+
+		THREE.OrthographicCamera.prototype.copy.call( this, source );
+
+		this.attribs = source.attribs;
+		this.scripts = source.scripts;
+
+		if ( source.animator ) this.animator = source.animator.clone( this );
+
+		return this;
+
+	}
+
+} );
+
+//
+//	PointLight
+//
+
+THREE.SEA3D.PointLight = function ( hex, intensity, distance, decay ) {
+
+	THREE.PointLight.call( this, hex, intensity, distance, decay );
+
+};
+
+THREE.SEA3D.PointLight.prototype = Object.assign( Object.create( THREE.PointLight.prototype ), THREE.SEA3D.Object3D.prototype, {
+
+	constructor: THREE.SEA3D.PointLight,
+
+	copy: function ( source ) {
+
+		THREE.PointLight.prototype.copy.call( this, source );
+
+		this.attribs = source.attribs;
+		this.scripts = source.scripts;
+
+		if ( source.animator ) this.animator = source.animator.clone( this );
+
+		return this;
+
+	}
+
+} );
+
+//
+//	Point Sound
+//
+
+THREE.SEA3D.PointSound = function ( listener, sound ) {
+
+	THREE.PositionalAudio.call( this, listener );
+
+	this.setSound( sound );
+
+};
+
+THREE.SEA3D.PointSound.prototype = Object.assign( Object.create( THREE.PositionalAudio.prototype ), THREE.SEA3D.Object3D.prototype, {
+
+	constructor: THREE.SEA3D.PointSound,
+
+	setSound: function ( sound ) {
+
+		this.sound = sound;
+
+		if ( sound ) {
+
+			if ( sound.buffer ) {
+
+				this.setBuffer( sound.buffer );
+
+			} else {
+
+				sound.addEventListener( "complete", function ( e ) {
+
+					this.setBuffer( sound.buffer );
+
+				}.bind( this ) );
+
+			}
+
+		}
+
+		return this;
+
+	},
+
+	copy: function ( source ) {
+
+		THREE.PositionalAudio.prototype.copy.call( this, source );
+
+		this.attribs = source.attribs;
+		this.scripts = source.scripts;
+
+		if ( source.animator ) this.animator = source.animator.clone( this );
+
+		return this;
+
+	}
+
+} );
+
+//
+//	Animation Handler
+//
+
+THREE.SEA3D.AnimationHandler = {
+
+	animators: [],
+
+	update: function ( dt ) {
+
+		var i = 0;
+
+		while ( i < this.animators.length ) {
+
+			this.animators[ i ++ ].update( dt );
+
+		}
+
+	},
+
+	add: function ( animator ) {
+
+		var index = this.animators.indexOf( animator );
+
+		if ( index === - 1 ) this.animators.push( animator );
+
+	},
+
+	remove: function ( animator ) {
+
+		var index = this.animators.indexOf( animator );
+
+		if ( index !== - 1 ) this.animators.splice( index, 1 );
+
+	}
+
+};
+
+//
+//	Sound
+//
+
+THREE.SEA3D.Sound = function ( src ) {
+
+	this.uuid = THREE.Math.generateUUID();
+
+	this.src = src;
+
+	new THREE.AudioLoader().load( src, function ( buffer ) {
+
+		this.buffer = buffer;
+
+		this.dispatchEvent( { type: "complete" } );
+
+	}.bind( this ) );
+
+};
+
+THREE.SEA3D.Sound.prototype = Object.assign( Object.create( THREE.EventDispatcher.prototype ), {
+
+	constructor: THREE.SEA3D.Sound
+
+} );
+
+//
+//	Output
+//
+
+THREE.SEA3D.Domain.prototype.getMesh = THREE.SEA3D.prototype.getMesh = function ( name ) {
+
+	return this.objects[ "m3d/" + name ];
+
+};
+
+THREE.SEA3D.Domain.prototype.getDummy = THREE.SEA3D.prototype.getDummy = function ( name ) {
+
+	return this.objects[ "dmy/" + name ];
+
+};
+
+THREE.SEA3D.Domain.prototype.getLine = THREE.SEA3D.prototype.getLine = function ( name ) {
+
+	return this.objects[ "line/" + name ];
+
+};
+
+THREE.SEA3D.Domain.prototype.getSound3D = THREE.SEA3D.prototype.getSound3D = function ( name ) {
+
+	return this.objects[ "sn3d/" + name ];
+
+};
+
+THREE.SEA3D.Domain.prototype.getMaterial = THREE.SEA3D.prototype.getMaterial = function ( name ) {
+
+	return this.objects[ "mat/" + name ];
+
+};
+
+THREE.SEA3D.Domain.prototype.getLight = THREE.SEA3D.prototype.getLight = function ( name ) {
+
+	return this.objects[ "lht/" + name ];
+
+};
+
+THREE.SEA3D.Domain.prototype.getGLSL = THREE.SEA3D.prototype.getGLSL = function ( name ) {
+
+	return this.objects[ "glsl/" + name ];
+
+};
+
+THREE.SEA3D.Domain.prototype.getCamera = THREE.SEA3D.prototype.getCamera = function ( name ) {
+
+	return this.objects[ "cam/" + name ];
+
+};
+
+THREE.SEA3D.Domain.prototype.getTexture = THREE.SEA3D.prototype.getTexture = function ( name ) {
+
+	return this.objects[ "tex/" + name ];
+
+};
+
+THREE.SEA3D.Domain.prototype.getCubeMap = THREE.SEA3D.prototype.getCubeMap = function ( name ) {
+
+	return this.objects[ "cmap/" + name ];
+
+};
+
+THREE.SEA3D.Domain.prototype.getJointObject = THREE.SEA3D.prototype.getJointObject = function ( name ) {
+
+	return this.objects[ "jnt/" + name ];
+
+};
+
+THREE.SEA3D.Domain.prototype.getContainer3D = THREE.SEA3D.prototype.getContainer3D = function ( name ) {
+
+	return this.objects[ "c3d/" + name ];
+
+};
+
+THREE.SEA3D.Domain.prototype.getSprite = THREE.SEA3D.prototype.getSprite = function ( name ) {
+
+	return this.objects[ "m2d/" + name ];
+
+};
+
+THREE.SEA3D.Domain.prototype.getProperties = THREE.SEA3D.prototype.getProperties = function ( name ) {
+
+	return this.objects[ "prop/" + name ];
+
+};
+
+//
+//	Utils
+//
+
+THREE.SEA3D.prototype.isPowerOfTwo = function ( num ) {
+
+	return num ? ( ( num & - num ) == num ) : false;
+
+};
+
+THREE.SEA3D.prototype.nearestPowerOfTwo = function ( num ) {
+
+	return Math.pow( 2, Math.round( Math.log( num ) / Math.LN2 ) );
+
+};
+
+THREE.SEA3D.prototype.updateTransform = function ( obj3d, sea ) {
+
+	var mtx = THREE.SEA3D.MTXBUF, vec = THREE.SEA3D.VECBUF;
+
+	if ( sea.transform ) mtx.fromArray( sea.transform );
+	else mtx.makeTranslation( sea.position.x, sea.position.y, sea.position.z );
+
+	// matrix
+
+	obj3d.position.setFromMatrixPosition( mtx );
+	obj3d.scale.setFromMatrixScale( mtx );
+
+	// ignore rotation scale
+
+	obj3d.rotation.setFromRotationMatrix( THREE.SEA3D.identityMatrixScale( mtx ) );
+
+	// optimize if is static
+
+	if ( this.config.forceStatic || sea.isStatic ) {
+
+		obj3d.updateMatrix();
+		obj3d.matrixAutoUpdate = false;
+
+	}
+
+};
+
+THREE.SEA3D.prototype.toVector3 = function ( data ) {
+
+	return new THREE.Vector3( data.x, data.y, data.z );
+
+};
+
+THREE.SEA3D.prototype.toFaces = function ( faces ) {
+
+	// xyz(- / +) to xyz(+ / -) sequence
+	var f = [];
+
+	f[ 0 ] = faces[ 1 ];
+	f[ 1 ] = faces[ 0 ];
+	f[ 2 ] = faces[ 3 ];
+	f[ 3 ] = faces[ 2 ];
+	f[ 4 ] = faces[ 5 ];
+	f[ 5 ] = faces[ 4 ];
+
+	return f;
+
+};
+
+THREE.SEA3D.prototype.updateScene = function () {
+
+	if ( this.materials != undefined ) {
+
+		for ( var i = 0, l = this.materials.length; i < l; ++ i ) {
+
+			this.materials[ i ].needsUpdate = true;
+
+		}
+
+	}
+
+};
+
+THREE.SEA3D.prototype.addSceneObject = function ( sea, obj3d ) {
+
+	obj3d = obj3d || sea.tag;
+
+	obj3d.visible = sea.visible;
+
+	if ( sea.parent ) sea.parent.tag.add( obj3d );
+	else if ( this.config.container ) this.config.container.add( obj3d );
+
+	if ( sea.attributes ) obj3d.attribs = sea.attributes.tag;
+
+	if ( sea.scripts ) {
+
+		obj3d.scripts = this.getJSMList( obj3d, sea.scripts );
+
+		if ( this.config.scripts && this.config.runScripts ) this.domain.runJSMList( obj3d );
+
+	}
+
+};
+
+THREE.SEA3D.prototype.createObjectURL = function ( raw, mime ) {
+
+	return ( window.URL || window.webkitURL ).createObjectURL( new Blob( [ raw ], { type: mime } ) );
+
+};
+
+THREE.SEA3D.prototype.parsePath = function ( url ) {
+
+	var paths = this.config.paths;
+
+	for ( var name in paths ) {
+
+		url = url.replace( new RegExp( "%" + name + "%", "g" ), paths[ name ] );
+
+	}
+
+	return url;
+
+};
+
+THREE.SEA3D.prototype.addDefaultAnimation = function ( sea, animatorClass ) {
+
+	var scope = sea.tag;
+
+	for ( var i = 0, count = sea.animations ? sea.animations.length : 0; i < count; i ++ ) {
+
+		var anm = sea.animations[ i ];
+
+		switch ( anm.tag.type ) {
+
+			case SEA3D.Animation.prototype.type:
+
+				var animation = anm.tag.tag || this.getModifier( {
+					sea: anm.tag,
+					scope: scope,
+					relative: anm.relative
+				} );
+
+				scope.animator = new animatorClass( animation, scope );
+				scope.animator.setRelative( anm.relative );
+
+				if ( this.config.autoPlay ) {
+
+					scope.animator.play( 0 );
+
+				}
+
+				return scope.animator;
+
+				break;
+
+		}
+
+	}
+
+};
+
+//
+//	Geometry
+//
+
+THREE.SEA3D.prototype.readGeometryBuffer = function ( sea ) {
+
+	var geo = sea.tag || new THREE.BufferGeometry();
+
+	for ( var i = 0; i < sea.groups.length; i ++ ) {
+
+		var g = sea.groups[ i ];
+
+		geo.addGroup( g.start, g.count, i );
+
+	}
+
+	// not indexes? use polygon soup
+	if ( sea.indexes ) geo.setIndex( new THREE.BufferAttribute( sea.indexes, 1 ) );
+
+	geo.addAttribute( 'position', new THREE.BufferAttribute( sea.vertex, 3 ) );
+
+	if ( sea.uv ) {
+
+		geo.addAttribute( 'uv', new THREE.BufferAttribute( sea.uv[ 0 ], 2 ) );
+		if ( sea.uv.length > 1 ) geo.addAttribute( 'uv2', new THREE.BufferAttribute( sea.uv[ 1 ], 2 ) );
+
+	}
+
+	if ( sea.normal ) geo.addAttribute( 'normal', new THREE.BufferAttribute( sea.normal, 3 ) );
+	else geo.computeVertexNormals();
+
+	if ( sea.tangent4 ) geo.addAttribute( 'tangent', new THREE.BufferAttribute( sea.tangent4, 4 ) );
+
+	if ( sea.color ) geo.addAttribute( 'color', new THREE.BufferAttribute( sea.color[ 0 ], sea.numColor ) );
+
+	if ( sea.joint ) {
+
+		geo.addAttribute( 'skinIndex', new THREE.Float32BufferAttribute( sea.joint, sea.jointPerVertex ) );
+		geo.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( sea.weight, sea.jointPerVertex ) );
+
+	}
+
+	if ( this.config.bounding ) {
+
+		geo.computeBoundingBox();
+		geo.computeBoundingSphere();
+
+	}
+
+	geo.name = sea.name;
+
+	this.domain.geometries = this.geometries = this.geometries || [];
+	this.geometries.push( this.objects[ "geo/" + sea.name ] = sea.tag = geo );
+
+};
+
+//
+//	Dummy
+//
+
+THREE.SEA3D.prototype.readDummy = function ( sea ) {
+
+	var dummy = new THREE.SEA3D.Dummy( sea.width, sea.height, sea.depth );
+	dummy.name = sea.name;
+
+	this.domain.dummys = this.dummys = this.dummys || [];
+	this.dummys.push( this.objects[ "dmy/" + sea.name ] = sea.tag = dummy );
+
+	this.addSceneObject( sea );
+	this.updateTransform( dummy, sea );
+
+	this.addDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator );
+
+};
+
+//
+//	Line
+//
+
+THREE.SEA3D.prototype.readLine = function ( sea ) {
+
+	var	geo = new THREE.BufferGeometry();
+
+	if ( sea.closed )
+		sea.vertex.push( sea.vertex[ 0 ], sea.vertex[ 1 ], sea.vertex[ 2 ] );
+
+	geo.addAttribute( 'position', new THREE.Float32BufferAttribute( sea.vertex, 3 ) );
+
+	var line = new THREE.Line( geo, new THREE.LineBasicMaterial( { color: THREE.SEA3D.HELPER_COLOR, linewidth: 3 } ) );
+	line.name = sea.name;
+
+	this.lines = this.lines || [];
+	this.lines.push( this.objects[ "line/" + sea.name ] = sea.tag = line );
+
+	this.addSceneObject( sea );
+	this.updateTransform( line, sea );
+
+	this.addDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator );
+
+};
+
+//
+//	Container3D
+//
+
+THREE.SEA3D.prototype.readContainer3D = function ( sea ) {
+
+	var container = new THREE.SEA3D.Object3D();
+
+	this.domain.containers = this.containers = this.containers || [];
+	this.containers.push( this.objects[ "c3d/" + sea.name ] = sea.tag = container );
+
+	this.addSceneObject( sea );
+	this.updateTransform( container, sea );
+
+	this.addDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator );
+
+};
+
+//
+//	Sprite
+//
+
+THREE.SEA3D.prototype.readSprite = function ( sea ) {
+
+	var mat;
+
+	if ( sea.material ) {
+
+		if ( ! sea.material.tag.sprite ) {
+
+			mat = sea.material.tag.sprite = new THREE.SpriteMaterial();
+
+			this.setBlending( mat, sea.blendMode );
+
+			var map = sea.material.tag.map;
+
+			if ( map ) {
+
+				map.flipY = true;
+				mat.map = map;
+
+			}
+
+			mat.color.set( sea.material.tag.color );
+			mat.opacity = sea.material.tag.opacity;
+			mat.fog = sea.material.receiveFog;
+
+		} else {
+
+			mat = sea.material.tag.sprite;
+
+		}
+
+	}
+
+	var sprite = new THREE.Sprite( mat );
+	sprite.name = sea.name;
+
+	this.domain.sprites = this.sprites = this.sprites || [];
+	this.sprites.push( this.objects[ "m2d/" + sea.name ] = sea.tag = sprite );
+
+	this.addSceneObject( sea );
+	this.updateTransform( sprite, sea );
+
+	sprite.scale.set( sea.width, sea.height, 1 );
+
+};
+
+//
+//	Mesh
+//
+
+THREE.SEA3D.prototype.readMesh = function ( sea ) {
+
+	var i, count, geo = sea.geometry.tag, mesh, mat, skeleton, morpher, skeletonAnimation, vertexAnimation, uvwAnimationClips, morphAnimation;
+
+	for ( i = 0, count = sea.modifiers ? sea.modifiers.length : 0; i < count; i ++ ) {
+
+		var mod = sea.modifiers[ i ];
+
+		switch ( mod.type ) {
+
+			case SEA3D.Skeleton.prototype.type:
+			case SEA3D.SkeletonLocal.prototype.type:
+
+				skeleton = mod;
+
+				geo.bones = skeleton.tag;
+
+				break;
+
+			case SEA3D.Morph.prototype.type:
+
+				morpher = mod.tag || this.getModifier( {
+					sea: mod,
+					geometry: sea.geometry
+				} );
+
+				geo.morphAttributes = morpher.attribs;
+				geo.morphTargets = morpher.targets;
+
+				break;
+
+		}
+
+	}
+
+	for ( i = 0, count = sea.animations ? sea.animations.length : 0; i < count; i ++ ) {
+
+		var anm = sea.animations[ i ],
+			anmTag = anm.tag;
+
+		switch ( anmTag.type ) {
+
+			case SEA3D.SkeletonAnimation.prototype.type:
+
+				skeletonAnimation = anmTag;
+
+				geo.animations = skeletonAnimation.tag || this.getModifier( {
+					sea: skeletonAnimation,
+					skeleton: skeleton,
+					relative: true
+				} );
+
+				break;
+
+			case SEA3D.VertexAnimation.prototype.type:
+
+				vertexAnimation = anmTag;
+
+				geo.morphAttributes = vertexAnimation.tag.attribs;
+				geo.morphTargets = vertexAnimation.tag.targets;
+				geo.animations = vertexAnimation.tag.animations;
+
+				break;
+
+			case SEA3D.UVWAnimation.prototype.type:
+
+				uvwAnimationClips = anmTag.tag || this.getModifier( {
+					sea: anmTag
+				} );
+
+				break;
+
+			case SEA3D.MorphAnimation.prototype.type:
+
+				morphAnimation = anmTag.tag || this.getModifier( {
+					sea: anmTag
+				} );
+
+				break;
+
+		}
+
+	}
+
+	var uMorph = morpher != undefined || vertexAnimation != undefined,
+		uMorphNormal =
+					( morpher && morpher.attribs.normal != undefined ) ||
+					( vertexAnimation && vertexAnimation.tag.attribs.normal != undefined );
+
+	if ( sea.material ) {
+
+		if ( sea.material.length > 1 ) {
+
+			var mats = [];
+
+			for ( i = 0; i < sea.material.length; i ++ ) {
+
+				mats[ i ] = sea.material[ i ].tag;
+
+				mats[ i ].skinning = skeleton != undefined;
+				mats[ i ].morphTargets = uMorph;
+				mats[ i ].morphNormals = uMorphNormal;
+				mats[ i ].vertexColors = sea.geometry.color ? THREE.VertexColors : THREE.NoColors;
+
+			}
+
+			mat = THREE.SEA3D.useMultiMaterial ? new THREE.MultiMaterial( mats ) : mats;
+
+		} else {
+
+			mat = sea.material[ 0 ].tag;
+
+			mat.skinning = skeleton != undefined;
+			mat.morphTargets = uMorph;
+			mat.morphNormals = uMorphNormal;
+			mat.vertexColors = sea.geometry.color ? THREE.VertexColors : THREE.NoColors;
+
+		}
+
+	}
+
+	if ( skeleton ) {
+
+		mesh = new THREE.SEA3D.SkinnedMesh( geo, mat, this.config.useVertexTexture );
+
+		if ( this.config.autoPlay && skeletonAnimation ) {
+
+			mesh.play( 0 );
+
+		}
+
+	} else if ( vertexAnimation ) {
+
+		mesh = new THREE.SEA3D.VertexAnimationMesh( geo, mat );
+
+		if ( this.config.autoPlay ) {
+
+			mesh.play( 0 );
+
+		}
+
+	} else {
+
+		mesh = new THREE.SEA3D.Mesh( geo, mat );
+
+	}
+
+	if ( uvwAnimationClips ) {
+
+		mesh.uvwAnimator = new THREE.SEA3D.Animator( uvwAnimationClips, new THREE.AnimationMixer( mat.map ) );
+
+		if ( this.config.autoPlay ) {
+
+			mesh.uvwAnimator.play( 0 );
+
+		}
+
+	}
+
+	if ( morphAnimation ) {
+
+		mesh.morphAnimator = new THREE.SEA3D.Animator( morphAnimation, new THREE.AnimationMixer( mesh ) );
+
+		if ( this.config.autoPlay ) {
+
+			mesh.morphAnimator.play( 0 );
+
+		}
+
+	}
+
+	mesh.name = sea.name;
+
+	mesh.castShadow = sea.castShadows;
+	mesh.receiveShadow = sea.material ? sea.material[ 0 ].receiveShadows : true;
+
+	this.domain.meshes = this.meshes = this.meshes || [];
+	this.meshes.push( this.objects[ "m3d/" + sea.name ] = sea.tag = mesh );
+
+	this.addSceneObject( sea );
+	this.updateTransform( mesh, sea );
+
+	this.addDefaultAnimation( sea, THREE.SEA3D.Object3DAnimator );
+
+};
+
+//
+//	Sound Point
+//
+
+THREE.SEA3D.prototype.readSoundPoint = function ( sea ) {
+
+	if ( ! this.audioListener ) {
+
+		 this.audioListener = new THREE.AudioListener();
+
+		 if ( this.config.container ) {
+
+			this.config.container.add( this.audioListener );
+
+		}
+
+	}
+
+	var sound3d = new THREE.SEA3D.PointSound( this.audioListener );
+	sound3d.autoplay = sea.autoPlay;
+	sound3d.setLoop( sea.autoPlay );
+	sound3d.setVolume( sea.volume );
+	sound3d.setRefDistance( sea.distance );
+	sound3d.setRolloffFactor( this.config.audioRolloffFactor );
+	sound3d.setSound( sea.sound.tag );
+
+	sound3d.name = sea.name;
+
+	this.domain.sounds3d = this.sounds3d = this.sounds3d || [];
+	this.sounds3d.push( this.objects[ "sn3d/" + sea.name ] = sea.tag = sound3d );
+
+	this.addSceneObject( sea );
+	this.updateTransform( sound3d, sea );
+
+	this.addDefaultAnimation( sea, THREE.SEA3D.SoundAnimator );
+
+};
+
+//
+//	Cube Render
+//
+
+THREE.SEA3D.prototype.readCubeRender = function ( sea ) {
+
+	var cube = new THREE.CubeCamera( 0.1, 5000, THREE.SEA3D.RTT_SIZE );
+	cube.renderTarget.cubeCamera = cube;
+
+	sea.tag = cube.renderTarget;
+
+	this.domain.cubeRenderers = this.cubeRenderers = this.cubeRenderers || [];
+	this.cubeRenderers.push( this.objects[ "rttc/" + sea.name ] = cube );
+
+	this.addSceneObject( sea, cube );
+	this.updateTransform( cube, sea );
+
+};
+
+//
+//	Texture (WDP, JPEG, PNG and GIF)
+//
+
+THREE.SEA3D.prototype.readTexture = function ( sea ) {
+
+	var image = new Image(),
+		texture = new THREE.Texture();
+
+	texture.name = sea.name;
+	texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+	texture.flipY = false;
+	texture.image = image;
+
+	if ( this.config.anisotropy !== undefined ) texture.anisotropy = this.config.anisotropy;
+
+	image.onload = function () {
+
+		texture.needsUpdate = true;
+
+	};
+
+	image.src = this.createObjectURL( sea.data.buffer, "image/" + sea.type );
+
+	this.domain.textures = this.textures = this.textures || [];
+	this.textures.push( this.objects[ "tex/" + sea.name ] = sea.tag = texture );
+
+};
+
+//
+//	Cube Map
+//
+
+THREE.SEA3D.prototype.readCubeMap = function ( sea ) {
+
+	var faces = this.toFaces( sea.faces ), texture = new THREE.CubeTexture( [] );
+
+	var loaded = 0;
+
+	texture.name = sea.name;
+	texture.flipY = false;
+	texture.format = THREE.RGBFormat;
+
+	var onLoaded = function () {
+
+		if ( ++ loaded == 6 ) {
+
+			texture.needsUpdate = true;
+
+			if ( ! this.config.async ) this.file.resume = true;
+
+		}
+
+	}.bind( this );
+
+	for ( var i = 0; i < faces.length; ++ i ) {
+
+		var cubeImage = new Image();
+		cubeImage.onload = onLoaded;
+		cubeImage.src = this.createObjectURL( faces[ i ].buffer, "image/" + sea.extension );
+
+		texture.images[ i ] = cubeImage;
+
+	}
+
+	if ( ! this.config.async ) this.file.resume = false;
+
+	this.domain.cubemaps = this.cubemaps = this.cubemaps || [];
+	this.cubemaps.push( this.objects[ "cmap/" + sea.name ] = sea.tag = texture );
+
+};
+
+//
+//	Updaters
+//
+
+THREE.SEA3D.prototype.readTextureUpdate = function ( sea ) {
+
+	var obj = this.file.objects[ sea.index ],
+		tex = obj.tag;
+
+	var image = new Image();
+
+	image.onload = function () {
+
+		tex.image = image;
+		tex.needsUpdate = true;
+
+	};
+
+	image.src = this.createObjectURL( sea.bytes.buffer, "image/" + obj.type );
+
+};
+
+//
+//	Sound (MP3, OGG)
+//
+
+THREE.SEA3D.prototype.readSound = function ( sea ) {
+
+	var sound = new THREE.SEA3D.Sound( this.createObjectURL( sea.data.buffer, "audio/" + sea.type ) );
+	sound.name = sea.name;
+
+	this.domain.sounds = this.sounds = this.sounds || [];
+	this.sounds.push( this.objects[ "snd/" + sea.name ] = sea.tag = sound );
+
+};
+
+//
+//	Script URL
+//
+
+THREE.SEA3D.prototype.readScriptURL = function ( sea ) {
+
+	this.file.resume = false;
+
+	var loader = new THREE.FileLoader();
+
+	loader.setResponseType( "text" ).load( sea.url, function ( src ) {
+
+		this.file.resume = true;
+
+		this.domain.scripts = this.scripts = this.scripts || [];
+		this.scripts.push( this.objects[ "src/" + sea.name ] = sea.tag = src );
+
+	}.bind( this ) );
+
+};
+
+//
+//	Texture URL
+//
+
+THREE.SEA3D.prototype.readTextureURL = function ( sea ) {
+
+	var texture = new THREE.TextureLoader().load( this.parsePath( sea.url ) );
+
+	texture.name = sea.name;
+	texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+	texture.flipY = false;
+
+	if ( this.config.anisotropy !== undefined ) texture.anisotropy = this.config.anisotropy;
+
+	this.domain.textures = this.textures = this.textures || [];
+	this.textures.push( this.objects[ "tex/" + sea.name ] = sea.tag = texture );
+
+};
+
+//
+//	CubeMap URL
+//
+
+THREE.SEA3D.prototype.readCubeMapURL = function ( sea ) {
+
+	var faces = this.toFaces( sea.faces );
+
+	for ( var i = 0; i < faces.length; i ++ ) {
+
+		faces[ i ] = this.parsePath( faces[ i ] );
+
+	}
+
+	var texture, format = faces[ 0 ].substr( - 3 );
+
+	if ( format == "hdr" ) {
+
+		var usePMREM = THREE.PMREMGenerator != null;
+
+		this.file.resume = ! usePMREM;
+
+		texture = new THREE.HDRCubeTextureLoader().load( THREE.UnsignedByteType, faces, function ( texture ) {
+
+			if ( usePMREM ) {
+
+				var pmremGenerator = new THREE.PMREMGenerator( texture );
+				pmremGenerator.update( this.config.renderer );
+
+				var pmremCubeUVPacker = new THREE.PMREMCubeUVPacker( pmremGenerator.cubeLods );
+				pmremCubeUVPacker.update( this.config.renderer );
+
+				texture.dispose();
+
+				this.objects[ "cmap/" + sea.name ] = sea.tag = pmremCubeUVPacker.CubeUVRenderTarget.texture;
+
+				this.file.resume = true;
+
+			}
+
+		}.bind( this ) );
+
+	} else {
+
+		texture = new THREE.CubeTextureLoader().load( faces );
+
+	}
+
+	texture.name = sea.name;
+	texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+	texture.flipY = false;
+
+	if ( this.config.anisotropy !== undefined ) texture.anisotropy = this.config.anisotropy;
+
+	this.domain.cubemaps = this.cubemaps = this.cubemaps || [];
+	this.cubemaps.push( this.objects[ "cmap/" + sea.name ] = sea.tag = texture );
+
+};
+
+//
+//	Runtime
+//
+
+THREE.SEA3D.prototype.getJSMList = function ( target, scripts ) {
+
+	var scriptTarget = [];
+
+	for ( var i = 0; i < scripts.length; i ++ ) {
+
+		var script = scripts[ i ];
+
+		if ( script.tag.type == SEA3D.JavaScriptMethod.prototype.type ) {
+
+			scriptTarget.push( script );
+
+		}
+
+	}
+
+	this.domain.scriptTargets = this.scriptTargets = this.scriptTargets || [];
+	this.scriptTargets.push( target );
+
+	return scriptTarget;
+
+};
+
+THREE.SEA3D.prototype.readJavaScriptMethod = function ( sea ) {
+
+	try {
+
+		var src =
+			'(function() {\n' +
+			'var $METHOD = {}\n';
+
+		var declare =
+			'function($INC, $REF, global, local, self, $PARAM) {\n' +
+			'var watch = $INC["watch"],\n' +
+			'scene = $INC["scene"],\n' +
+			'sea3d = $INC["sea3d"],\n' +
+			'print = $INC["print"];\n';
+
+		declare +=
+			'var $SRC = $INC["source"],\n' +
+			'addEventListener = $SRC.addEventListener.bind( $SRC ),\n' +
+			'hasEventListener = $SRC.hasEventListener.bind( $SRC ),\n' +
+			'removeEventListener = $SRC.removeEventListener.bind( $SRC ),\n' +
+			'dispatchEvent = $SRC.dispatchEvent.bind( $SRC ),\n' +
+			'dispose = $SRC.dispose.bind( $SRC );\n';
+
+		for ( var name in sea.methods ) {
+
+			src += '$METHOD["' + name + '"] = ' + declare + sea.methods[ name ].src + '}\n';
+
+		}
+
+		src += 'return $METHOD; })';
+
+		this.domain.methods = eval( src )();
+
+	} catch ( e ) {
+
+		console.error( 'SEA3D JavaScriptMethod: Error running "' + sea.name + '".' );
+		console.error( e );
+
+	}
+
+};
+
+//
+//	GLSL
+//
+
+THREE.SEA3D.prototype.readGLSL = function ( sea ) {
+
+	this.domain.glsl = this.glsl = this.glsl || [];
+	this.glsl.push( this.objects[ "glsl/" + sea.name ] = sea.tag = sea.src );
+
+};
+
+//
+//	Material
+//
+
+THREE.SEA3D.prototype.materialTechnique =
+( function () {
+
+	var techniques = {};
+
+	// FINAL
+	techniques.onComplete = function ( mat, sea ) {
+
+		if ( sea.alpha < 1 || mat.blending > THREE.NormalBlending ) {
+
+			mat.opacity = sea.alpha;
+			mat.transparent = true;
+
+		}
+
+	};
+
+	// PHYSICAL
+	techniques[ SEA3D.Material.PHYSICAL ] =
+	function ( mat, tech ) {
+
+		mat.color.setHex( tech.color );
+		mat.roughness = tech.roughness;
+		mat.metalness = tech.metalness;
+
+	};
+
+	// REFLECTIVITY
+	techniques[ SEA3D.Material.REFLECTIVITY ] =
+	function ( mat, tech ) {
+
+		mat.reflectivity = tech.strength;
+
+	};
+
+	// CLEAR_COAT
+	techniques[ SEA3D.Material.CLEAR_COAT ] =
+	function ( mat, tech ) {
+
+		mat.clearCoat = tech.strength;
+		mat.clearCoatRoughness = tech.roughness;
+
+	};
+
+	// PHONG
+	techniques[ SEA3D.Material.PHONG ] =
+	function ( mat, tech ) {
+
+		mat.color.setHex( tech.diffuseColor );
+		mat.specular.setHex( tech.specularColor ).multiplyScalar( tech.specular );
+		mat.shininess = tech.gloss;
+
+	};
+
+	// DIFFUSE_MAP
+	techniques[ SEA3D.Material.DIFFUSE_MAP ] =
+	function ( mat, tech, sea ) {
+
+		mat.map = tech.texture.tag;
+		mat.color.setHex( 0xFFFFFF );
+
+		mat.map.wrapS = mat.map.wrapT = sea.repeat ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
+
+		if ( tech.texture.transparent ) {
+
+			mat.transparent = true;
+
+		}
+
+	};
+
+	// ROUGHNESS_MAP
+	techniques[ SEA3D.Material.ROUGHNESS_MAP ] =
+	function ( mat, tech ) {
+
+		mat.roughnessMap = tech.texture.tag;
+
+	};
+
+	// METALNESS_MAP
+	techniques[ SEA3D.Material.METALNESS_MAP ] =
+	function ( mat, tech ) {
+
+		mat.metalnessMap = tech.texture.tag;
+
+	};
+
+	// SPECULAR_MAP
+	techniques[ SEA3D.Material.SPECULAR_MAP ] =
+	function ( mat, tech ) {
+
+		if ( mat.specular ) {
+
+			mat.specularMap = tech.texture.tag;
+			mat.specular.setHex( 0xFFFFFF );
+
+		}
+
+	};
+
+	// NORMAL_MAP
+	techniques[ SEA3D.Material.NORMAL_MAP ] =
+	function ( mat, tech ) {
+
+		mat.normalMap = tech.texture.tag;
+
+	};
+
+	// REFLECTION
+	techniques[ SEA3D.Material.REFLECTION ] =
+	techniques[ SEA3D.Material.FRESNEL_REFLECTION ] =
+	function ( mat, tech ) {
+
+		mat.envMap = tech.texture.tag;
+		mat.envMap.mapping = THREE.CubeReflectionMapping;
+		mat.combine = THREE.MixOperation;
+
+		mat.reflectivity = tech.alpha;
+
+	};
+
+	// REFLECTION_SPHERICAL
+	techniques[ SEA3D.Material.REFLECTION_SPHERICAL ] =
+	function ( mat, tech ) {
+
+		mat.envMap = tech.texture.tag;
+		mat.envMap.mapping = THREE.SphericalReflectionMapping;
+		mat.combine = THREE.MixOperation;
+
+		mat.reflectivity = tech.alpha;
+
+	};
+
+	// REFRACTION
+	techniques[ SEA3D.Material.REFRACTION_MAP ] =
+	function ( mat, tech ) {
+
+		mat.envMap = tech.texture.tag;
+		mat.envMap.mapping = THREE.CubeRefractionMapping;
+
+		mat.refractionRatio = tech.ior;
+		mat.reflectivity = tech.alpha;
+
+	};
+
+	// LIGHT_MAP
+	techniques[ SEA3D.Material.LIGHT_MAP ] =
+	function ( mat, tech ) {
+
+		if ( tech.blendMode == "multiply" ) mat.aoMap = tech.texture.tag;
+		else mat.lightMap = tech.texture.tag;
+
+	};
+
+	// EMISSIVE
+	techniques[ SEA3D.Material.EMISSIVE ] =
+	function ( mat, tech ) {
+
+		mat.emissive.setHex( tech.color );
+
+	};
+
+	// EMISSIVE_MAP
+	techniques[ SEA3D.Material.EMISSIVE_MAP ] =
+	function ( mat, tech ) {
+
+		mat.emissiveMap = tech.texture.tag;
+
+	};
+
+	// ALPHA_MAP
+	techniques[ SEA3D.Material.ALPHA_MAP ] =
+	function ( mat, tech, sea ) {
+
+		mat.alphaMap = tech.texture.tag;
+		mat.transparent = true;
+
+		mat.alphaMap.wrapS = mat.alphaMap.wrapT = sea.repeat ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
+
+	};
+
+	return techniques;
+
+} )();
+
+THREE.SEA3D.prototype.createMaterial = function ( sea ) {
+
+	if ( sea.tecniquesDict[ SEA3D.Material.REFLECTIVITY ] || sea.tecniquesDict[ SEA3D.Material.CLEAR_COAT ] ) {
+
+		return new THREE.MeshPhysicalMaterial();
+
+	} else if ( sea.tecniquesDict[ SEA3D.Material.PHYSICAL ] ) {
+
+		return new THREE.MeshStandardMaterial();
+
+	}
+
+	return new THREE.MeshPhongMaterial();
+
+};
+
+THREE.SEA3D.prototype.setBlending = function ( mat, blendMode ) {
+
+	if ( blendMode === "normal" ) return;
+
+	switch ( blendMode ) {
+
+		case "add":
+
+			mat.blending = THREE.AdditiveBlending;
+
+			break;
+
+		case "subtract":
+
+			mat.blending = THREE.SubtractiveBlending;
+
+			break;
+
+		case "multiply":
+
+			mat.blending = THREE.MultiplyBlending;
+
+			break;
+
+		case "screen":
+
+			mat.blending = THREE.CustomBlending;
+			mat.blendSrc = THREE.OneFactor;
+			mat.blendDst = THREE.OneMinusSrcColorFactor;
+			mat.blendEquation = THREE.AddEquation;
+
+			break;
+
+	}
+
+	mat.transparent = true;
+
+};
+
+THREE.SEA3D.prototype.readMaterial = function ( sea ) {
+
+	var mat = this.createMaterial( sea );
+	mat.name = sea.name;
+
+	mat.lights = sea.receiveLights;
+	mat.fog = sea.receiveFog;
+
+	mat.depthWrite = sea.depthWrite;
+	mat.depthTest = sea.depthTest;
+
+	mat.premultipliedAlpha = sea.premultipliedAlpha;
+
+	mat.side = sea.doubleSided ? THREE.DoubleSide : THREE.FrontSide;
+
+	this.setBlending( mat, sea.blendMode );
+
+	for ( var i = 0; i < sea.technique.length; i ++ ) {
+
+		var tech = sea.technique[ i ];
+
+		if ( this.materialTechnique[ tech.kind ] ) {
+
+			this.materialTechnique[ tech.kind ].call( this, mat, tech, sea );
+
+		}
+
+	}
+
+	if ( this.materialTechnique.onComplete ) {
+
+		this.materialTechnique.onComplete.call( this, mat, sea );
+
+	}
+
+	this.domain.materials = this.materials = this.materials || [];
+	this.materials.push( this.objects[ "mat/" + sea.name ] = sea.tag = mat );
+
+};
+
+//
+//	Point Light
+//
+
+THREE.SEA3D.prototype.readPointLight = function ( sea ) {
+
+	var light = new THREE.SEA3D.PointLight( sea.color, sea.multiplier * this.config.multiplier );
+	light.name = sea.name;
+
+	if ( sea.attenuation ) {
+
+		light.distance = sea.attenuation.end;
+
+	}
+
+	if ( sea.shadow ) this.setShadowMap( light );
+
+	this.domain.lights = this.lights = this.lights || [];
+	this.lights.push( this.objects[ "lht/" + sea.name ] = sea.tag = light );
+
+	this.addSceneObject( sea );
+
+	this.updateTransform( light, sea );
+
+	this.addDefaultAnimation( sea, THREE.SEA3D.LightAnimator );
+
+	this.updateScene();
+
+};
+
+//
+//	Hemisphere Light
+//
+
+THREE.SEA3D.prototype.readHemisphereLight = function ( sea ) {
+
+	var light = new THREE.HemisphereLight( sea.color, sea.secondColor, sea.multiplier * this.config.multiplier );
+	light.position.set( 0, 500, 0 );
+	light.name = sea.name;
+
+	this.domain.lights = this.lights = this.lights || [];
+	this.lights.push( this.objects[ "lht/" + sea.name ] = sea.tag = light );
+
+	this.addSceneObject( sea );
+
+	this.addDefaultAnimation( sea, THREE.SEA3D.LightAnimator );
+
+	this.updateScene();
+
+};
+
+//
+//	Ambient Light
+//
+
+THREE.SEA3D.prototype.readAmbientLight = function ( sea ) {
+
+	var light = new THREE.AmbientLight( sea.color, sea.multiplier * this.config.multiplier );
+	light.name = sea.name;
+
+	this.domain.lights = this.lights = this.lights || [];
+	this.lights.push( this.objects[ "lht/" + sea.name ] = sea.tag = light );
+
+	this.addSceneObject( sea );
+
+	this.addDefaultAnimation( sea, THREE.SEA3D.LightAnimator );
+
+	this.updateScene();
+
+};
+
+//
+//	Directional Light
+//
+
+THREE.SEA3D.prototype.readDirectionalLight = function ( sea ) {
+
+	var light = new THREE.DirectionalLight( sea.color, sea.multiplier * this.config.multiplier );
+	light.name = sea.name;
+
+	if ( sea.shadow ) {
+
+		this.setShadowMap( light );
+
+	}
+
+	this.domain.lights = this.lights = this.lights || [];
+	this.lights.push( this.objects[ "lht/" + sea.name ] = sea.tag = light );
+
+	this.addSceneObject( sea );
+
+	this.updateTransform( light, sea );
+
+	this.addDefaultAnimation( sea, THREE.SEA3D.LightAnimator );
+
+	this.updateScene();
+
+};
+
+//
+//	Camera
+//
+
+THREE.SEA3D.prototype.readCamera = function ( sea ) {
+
+	var camera = new THREE.SEA3D.Camera( sea.fov );
+	camera.name = sea.name;
+
+	this.domain.cameras = this.cameras = this.cameras || [];
+	this.cameras.push( this.objects[ "cam/" + sea.name ] = sea.tag = camera );
+
+	this.addSceneObject( sea );
+	this.updateTransform( camera, sea );
+
+	this.addDefaultAnimation( sea, THREE.SEA3D.CameraAnimator );
+
+};
+
+//
+//	Orthographic Camera
+//
+
+THREE.SEA3D.prototype.readOrthographicCamera = function ( sea ) {
+
+	var aspect, width, height;
+
+	var stageWidth = this.config.stageWidth !== undefined ? this.config.stageWidth : ( window ? window.innerWidth : 1024 );
+	var stageHeight = this.config.stageHeight !== undefined ? this.config.stageHeight : ( window ? window.innerHeight : 1024 );
+
+	if ( stageWidth > stageHeight ) {
+
+		aspect = stageWidth / stageHeight;
+
+		width = sea.height * aspect;
+		height = sea.height;
+
+	} else {
+
+		aspect = stageHeight / stageWidth;
+
+		width = sea.height;
+		height = sea.height * aspect;
+
+	}
+
+	var camera = new THREE.SEA3D.OrthographicCamera( - width, width, height, - height );
+	camera.name = sea.name;
+
+	this.domain.cameras = this.cameras = this.cameras || [];
+	this.cameras.push( this.objects[ "cam/" + sea.name ] = sea.tag = camera );
+
+	this.addSceneObject( sea );
+	this.updateTransform( camera, sea );
+
+	this.addDefaultAnimation( sea, THREE.SEA3D.CameraAnimator );
+
+};
+
+//
+//	Skeleton
+//
+
+THREE.SEA3D.prototype.getSkeletonFromBones = function ( bonesData ) {
+
+	var bones = [], bone, gbone;
+	var i, il;
+
+	for ( i = 0, il = bonesData.length; i < il; i ++ ) {
+
+		gbone = bonesData[ i ];
+
+		bone = new THREE.Bone();
+		bones.push( bone );
+
+		bone.name = gbone.name;
+		bone.position.fromArray( gbone.pos );
+		bone.quaternion.fromArray( gbone.rotq );
+
+		if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl );
+
+	}
+
+	for ( i = 0, il = bonesData.length; i < il; i ++ ) {
+
+		gbone = bonesData[ i ];
+
+		if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) {
+
+			bones[ gbone.parent ].add( bones[ i ] );
+
+		}
+
+	}
+
+	return new THREE.Skeleton( bones );
+
+};
+
+THREE.SEA3D.prototype.readSkeletonLocal = function ( sea ) {
+
+	var bones = [];
+
+	for ( var i = 0; i < sea.joint.length; i ++ ) {
+
+		var bone = sea.joint[ i ];
+
+		bones[ i ] = {
+			name: bone.name,
+			pos: [ bone.x, bone.y, bone.z ],
+			rotq: [ bone.qx, bone.qy, bone.qz, bone.qw ],
+			parent: bone.parentIndex
+		};
+
+	}
+
+	this.domain.bones = this.bones = this.bones || [];
+	this.bones.push( this.objects[ sea.name + '.sklq' ] = sea.tag = bones );
+
+};
+
+//
+//	Joint Object
+//
+
+THREE.SEA3D.prototype.readJointObject = function ( sea ) {
+
+	var mesh = sea.target.tag,
+		bone = mesh.skeleton.bones[ sea.joint ];
+
+	this.domain.joints = this.joints = this.joints || [];
+	this.joints.push( this.objects[ "jnt/" + sea.name ] = sea.tag = bone );
+
+};
+
+//
+//	Morph
+//
+
+THREE.SEA3D.prototype.readMorph = function ( sea ) {
+
+	var attribs = { position: [] }, targets = [];
+
+	for ( var i = 0; i < sea.node.length; i ++ ) {
+
+		var node = sea.node[ i ];
+
+		attribs.position[ i ] = new THREE.Float32BufferAttribute( node.vertex, 3 );
+		attribs.position[ i ].name = node.name;
+
+		if ( node.normal ) {
+
+			attribs.normal = attribs.normal || [];
+			attribs.normal[ i ] = new THREE.Float32BufferAttribute( node.normal, 3 );
+
+		}
+
+		targets[ i ] = { name: node.name };
+
+	}
+
+	sea.tag = {
+		attribs: attribs,
+		targets: targets
+	};
+
+};
+
+//
+//	Animation
+//
+
+THREE.SEA3D.prototype.readAnimation = function ( sea ) {
+
+	var animations = [], delta = ( 1000 / sea.frameRate ) / 1000;
+
+	for ( var i = 0; i < sea.sequence.length; i ++ ) {
+
+		var seq = sea.sequence[ i ];
+
+		var tracks = [];
+
+		for ( var j = 0; j < sea.dataList.length; j ++ ) {
+
+			var anm = sea.dataList[ j ],
+				t, k, times, values,
+				data = anm.data,
+				start = seq.start * anm.blockSize,
+				end = start + ( seq.count * anm.blockSize ),
+				intrpl = seq.intrpl ? THREE.InterpolateLinear : false,
+				name = null;
+
+			switch ( anm.kind ) {
+
+				case SEA3D.Animation.POSITION:
+
+					name = '.position';
+
+					break;
+
+				case SEA3D.Animation.ROTATION:
+
+					name = '.quaternion';
+
+					break;
+
+				case SEA3D.Animation.SCALE:
+
+					name = '.scale';
+
+					break;
+
+				case SEA3D.Animation.COLOR:
+
+					name = '.color';
+
+					break;
+
+				case SEA3D.Animation.MULTIPLIER:
+
+					name = '.intensity';
+
+					break;
+
+				case SEA3D.Animation.FOV:
+
+					name = '.fov';
+
+					break;
+
+				case SEA3D.Animation.OFFSET_U:
+
+					name = '.offset[x]';
+
+					break;
+
+				case SEA3D.Animation.OFFSET_V:
+
+					name = '.offset[y]';
+
+					break;
+
+				case SEA3D.Animation.SCALE_U:
+
+					name = '.repeat[x]';
+
+					break;
+
+				case SEA3D.Animation.SCALE_V:
+
+					name = '.repeat[y]';
+
+					break;
+
+				case SEA3D.Animation.MORPH:
+
+					name = '.morphTargetInfluences[' + anm.name + ']';
+
+					break;
+
+			}
+
+			if ( ! name ) continue;
+
+			switch ( anm.type ) {
+
+				case SEA3D.Stream.BYTE:
+				case SEA3D.Stream.UBYTE:
+				case SEA3D.Stream.INT:
+				case SEA3D.Stream.UINT:
+				case SEA3D.Stream.FLOAT:
+				case SEA3D.Stream.DOUBLE:
+				case SEA3D.Stream.DECIMAL:
+
+					values = data.subarray( start, end );
+					times = new Float32Array( values.length );
+					t = 0;
+
+					for ( k = 0; k < times.length; k ++ ) {
+
+						times[ k ] = t;
+						t += delta;
+
+					}
+
+					tracks.push( new THREE.NumberKeyframeTrack( name, times, values, intrpl ) );
+
+					break;
+
+				case SEA3D.Stream.VECTOR3D:
+
+					values = data.subarray( start, end );
+					times = new Float32Array( values.length / anm.blockSize );
+					t = 0;
+
+					for ( k = 0; k < times.length; k ++ ) {
+
+						times[ k ] = t;
+						t += delta;
+
+					}
+
+					tracks.push( new THREE.VectorKeyframeTrack( name, times, values, intrpl ) );
+
+					break;
+
+				case SEA3D.Stream.VECTOR4D:
+
+					values = data.subarray( start, end );
+					times = new Float32Array( values.length / anm.blockSize );
+					t = 0;
+
+					for ( k = 0; k < times.length; k ++ ) {
+
+						times[ k ] = t;
+						t += delta;
+
+					}
+
+					tracks.push( new THREE.QuaternionKeyframeTrack( name, times, values, intrpl ) );
+
+					break;
+
+				case SEA3D.Stream.INT24:
+				case SEA3D.Stream.UINT24:
+
+					values = new Float32Array( ( end - start ) * 3 );
+					times = new Float32Array( values.length / 3 );
+					t = 0;
+
+					for ( k = 0; k < times.length; k ++ ) {
+
+						values[ ( k * 3 ) ] = ( ( data[ k ] >> 16 ) & 0xFF ) / 255;
+						values[ ( k * 3 ) + 1 ] = ( ( data[ k ] >> 8 ) & 0xFF ) / 255;
+						values[ ( k * 3 ) + 2 ] = ( data[ k ] & 0xFF ) / 255;
+						times[ k ] = t;
+						t += delta;
+
+					}
+
+					tracks.push( new THREE.VectorKeyframeTrack( name, times, values, intrpl ) );//ColorKeyframeTrack
+
+					break;
+
+			}
+
+		}
+
+		animations.push( new THREE.SEA3D.AnimationClip( seq.name, - 1, tracks, seq.repeat ) );
+
+	}
+
+	this.domain.clips = this.clips = this.clips || [];
+	this.clips.push( this.objects[ sea.name + '.anm' ] = sea.tag = animations );
+
+};
+
+//
+//	Skeleton Animation
+//
+
+THREE.SEA3D.prototype.readSkeletonAnimation = function ( sea, skl ) {
+
+	skl = ! skl && sea.metadata && sea.metadata.skeleton ? sea.metadata.skeleton : skl;
+
+	if ( ! skl || sea.tag ) return sea.tag;
+
+	var animations = [], delta = ( 1000 / sea.frameRate ) / 1000;
+
+	for ( var i = 0; i < sea.sequence.length; i ++ ) {
+
+		var seq = sea.sequence[ i ];
+
+		var start = seq.start;
+		var end = start + seq.count;
+
+		var animation = {
+			name: seq.name,
+			fps: sea.frameRate,
+			length: delta * seq.count,
+			hierarchy: []
+		};
+
+		var numJoints = sea.numJoints,
+			raw = sea.raw;
+
+		for ( var j = 0; j < numJoints; j ++ ) {
+
+			var bone = skl.joint[ j ],
+				node = { parent: bone.parentIndex, keys: [] },
+				keys = node.keys,
+				time = 0;
+
+			for ( var frame = start; frame < end; frame ++ ) {
+
+				var idx = ( frame * numJoints * 7 ) + ( j * 7 );
+
+				keys.push( {
+					time: time,
+					pos: [ raw[ idx ], raw[ idx + 1 ], raw[ idx + 2 ] ],
+					rot: [ raw[ idx + 3 ], raw[ idx + 4 ], raw[ idx + 5 ], raw[ idx + 6 ] ],
+					scl: [ 1, 1, 1 ]
+				} );
+
+				time += delta;
+
+			}
+
+			animation.hierarchy[ j ] = node;
+
+		}
+
+		animations.push( THREE.SEA3D.AnimationClip.fromClip( THREE.AnimationClip.parseAnimation( animation, skl.tag ), seq.repeat ) );
+
+	}
+
+	this.domain.clips = this.clips = this.clips || [];
+	this.clips.push( this.objects[ sea.name + '.skla' ] = sea.tag = animations );
+
+};
+
+//
+//	Vertex Animation
+//
+
+THREE.SEA3D.prototype.readVertexAnimation = function ( sea ) {
+
+	var attribs = { position: [] }, targets = [], animations = [], i, j, l;
+
+	for ( i = 0, l = sea.frame.length; i < l; i ++ ) {
+
+		var frame = sea.frame[ i ];
+
+		attribs.position[ i ] = new THREE.Float32BufferAttribute( frame.vertex, 3 );
+
+		if ( frame.normal ) {
+
+			attribs.normal = attribs.normal || [];
+			attribs.normal[ i ] = new THREE.Float32BufferAttribute( frame.normal, 3 );
+
+		}
+
+		targets[ i ] = { name: i };
+
+	}
+
+	for ( i = 0; i < sea.sequence.length; i ++ ) {
+
+		var seq = sea.sequence[ i ];
+		var seqTargets = [];
+
+		for ( j = 0; j < seq.count; j ++ ) {
+
+			seqTargets[ j ] = targets[ seq.start + j ];
+
+		}
+
+		animations.push( THREE.SEA3D.AnimationClip.fromClip( THREE.AnimationClip.CreateFromMorphTargetSequence( seq.name, seqTargets, sea.frameRate ), seq.repeat ) );
+
+	}
+
+	sea.tag = {
+		attribs: attribs,
+		targets: targets,
+		animations: animations
+	};
+
+	this.domain.clips = this.clips = this.clips || [];
+	this.clips.push( this.objects[ sea.name + '.vtxa' ] = sea.tag );
+
+};
+
+//
+//	Selector
+//
+
+THREE.SEA3D.prototype.getModifier = function ( req ) {
+
+	var sea = req.sea;
+
+	switch ( sea.type ) {
+
+		case SEA3D.SkeletonAnimation.prototype.type:
+
+			this.readSkeletonAnimation( sea, req.skeleton );
+
+			break;
+
+		case SEA3D.Animation.prototype.type:
+		case SEA3D.MorphAnimation.prototype.type:
+		case SEA3D.UVWAnimation.prototype.type:
+
+			this.readAnimation( sea );
+
+			break;
+
+		case SEA3D.Morph.prototype.type:
+
+			this.readMorph( sea, req.geometry );
+
+			break;
+
+	}
+
+	return sea.tag;
+
+};
+
+//
+//	Actions
+//
+
+THREE.SEA3D.prototype.applyEnvironment = function ( envMap ) {
+
+	for ( var j = 0, l = this.materials.length; j < l; ++ j ) {
+
+		var mat = this.materials[ j ];
+
+		if ( mat instanceof THREE.MeshStandardMaterial ) {
+
+			if ( mat.envMap ) continue;
+
+			mat.envMap = envMap;
+			mat.envMap.mapping = THREE.CubeReflectionMapping;
+
+			mat.needsUpdate = true;
+
+		}
+
+	}
+
+};
+
+THREE.SEA3D.prototype.readActions = function ( sea ) {
+
+	for ( var i = 0; i < sea.actions.length; i ++ ) {
+
+		var act = sea.actions[ i ];
+
+		switch ( act.kind ) {
+
+			case SEA3D.Actions.ATTRIBUTES:
+
+				this.attribs = this.domain.attribs = act.attributes.tag;
+
+				break;
+
+			case SEA3D.Actions.SCRIPTS:
+
+				this.domain.scripts = this.getJSMList( this.domain, act.scripts );
+
+				if ( this.config.scripts && this.config.runScripts ) this.domain.runJSMList( this.domain );
+
+				break;
+
+			case SEA3D.Actions.CAMERA:
+
+				this.domain.camera = this.camera = act.camera.tag;
+
+				break;
+
+			case SEA3D.Actions.ENVIRONMENT_COLOR:
+
+				this.domain.background = this.background = this.background || {};
+
+				this.background.color = new THREE.Color( act.color );
+
+				break;
+
+			case SEA3D.Actions.ENVIRONMENT:
+
+				this.domain.background = this.background = this.background || {};
+
+				this.background.texture = act.texture.tag;
+
+				if ( this.config.useEnvironment && this.materials != undefined ) {
+
+					this.applyEnvironment( act.texture.tag );
+
+				}
+
+				break;
+
+		}
+
+	}
+
+};
+
+//
+//	Properties
+//
+
+THREE.SEA3D.prototype.updatePropertiesAssets = function ( sea, props ) {
+
+	for ( var name in props ) {
+
+		switch ( props.__type[ name ] ) {
+
+			case SEA3D.Stream.ASSET:
+
+				if ( ! props.__asset ) props.__asset = {};
+				if ( ! props.__asset[ name ] ) props.__asset[ name ] = props[ name ];
+
+				props[ name ] = props.__asset[ name ].tag;
+
+				break;
+
+			case SEA3D.Stream.GROUP:
+
+				props[ name ] = this.updatePropertiesAssets( sea, props[ name ] );
+
+				break;
+
+		}
+
+	}
+
+	return props;
+
+};
+
+THREE.SEA3D.prototype.readProperties = function ( sea ) {
+
+	var props = this.updatePropertiesAssets( sea, sea.props );
+
+	this.domain.properties = this.properties = this.properties || [];
+	this.properties.push( this.objects[ "prop/" + sea.name ] = sea.tag = props );
+
+};
+
+THREE.SEA3D.prototype.readFileInfo = function ( sea ) {
+
+	this.domain.info = this.updatePropertiesAssets( sea, sea.info );
+
+};
+
+//
+//	Events
+//
+
+THREE.SEA3D.Event = {
+	PROGRESS: "sea3d_progress",
+	LOAD_PROGRESS: "sea3d_load",
+	DOWNLOAD_PROGRESS: "sea3d_download",
+	COMPLETE: "sea3d_complete",
+	OBJECT_COMPLETE: "sea3d_object",
+	PARSE_PROGRESS: "parse_progress",
+	PARSE_COMPLETE: "parse_complete",
+	ERROR: "sea3d_error"
+};
+
+THREE.SEA3D.prototype.onProgress = function ( e ) {
+
+	e.status = e.type;
+	e.progress = e.loaded / e.total;
+	e.type = THREE.SEA3D.Event.PROGRESS;
+
+	this.dispatchEvent( e );
+
+};
+
+THREE.SEA3D.prototype.onLoadProgress = function ( e ) {
+
+	e.type = THREE.SEA3D.Event.LOAD_PROGRESS;
+	this.dispatchEvent( e );
+
+	this.onProgress( e );
+
+};
+
+THREE.SEA3D.prototype.onDownloadProgress = function ( e ) {
+
+	e.type = THREE.SEA3D.Event.DOWNLOAD_PROGRESS;
+	this.dispatchEvent( e );
+
+	this.onProgress( e );
+
+};
+
+THREE.SEA3D.prototype.onComplete = function ( e ) {
+
+	e.type = THREE.SEA3D.Event.COMPLETE;
+	this.dispatchEvent( e );
+
+};
+
+THREE.SEA3D.prototype.onCompleteObject = function ( e ) {
+
+	e.type = THREE.SEA3D.Event.OBJECT_COMPLETE;
+	this.dispatchEvent( e );
+
+};
+
+THREE.SEA3D.prototype.onParseProgress = function ( e ) {
+
+	e.type = THREE.SEA3D.Event.PARSE_PROGRESS;
+	this.dispatchEvent( e );
+
+};
+
+THREE.SEA3D.prototype.onParseComplete = function ( e ) {
+
+	e.type = THREE.SEA3D.Event.PARSE_COMPLETE;
+	this.dispatchEvent( e );
+
+};
+
+THREE.SEA3D.prototype.onError = function ( e ) {
+
+	e.type = THREE.SEA3D.Event.ERROR;
+	this.dispatchEvent( e );
+
+};
+
+//
+//	Loader
+//
+
+THREE.SEA3D.prototype.createDomain = function () {
+
+	return this.domain = new THREE.SEA3D.Domain(
+		this.config.id,
+		this.objects = {},
+		this.config.container
+	);
+
+};
+
+THREE.SEA3D.prototype.clone = function ( config, onParseComplete, onParseProgress ) {
+
+	if ( ! this.file.isDone() ) throw new Error( "Previous parse is not completed." );
+
+	this.config.container = config && config.container !== undefined ? config.container : new THREE.Object3D();
+
+	if ( config ) this.loadConfig( config );
+
+	var timeLimit = this.config.timeLimit;
+
+	this.config.timeLimit = config && config.timeLimit !== undefined ? config.timeLimit : Infinity;
+
+	this.parse( onParseComplete, onParseProgress );
+
+	this.config.timeLimit = timeLimit;
+
+	return this.domain;
+
+};
+
+THREE.SEA3D.prototype.loadConfig = function ( config ) {
+
+	for ( var name in config ) {
+
+		this.config[ name ] = config[ name ];
+
+	}
+
+};
+
+THREE.SEA3D.prototype.parse = function ( onParseComplete, onParseProgress ) {
+
+	delete this.cameras;
+	delete this.containers;
+	delete this.lights;
+	delete this.joints;
+	delete this.meshes;
+	delete this.materials;
+	delete this.sprites;
+	delete this.sounds3d;
+	delete this.cubeRenderers;
+	delete this.sounds;
+	delete this.glsl;
+	delete this.dummy;
+	delete this.camera;
+	delete this.background;
+	delete this.properties;
+	delete this.scriptTargets;
+
+	delete this.domain;
+
+	this.createDomain();
+
+	this.setTypeRead();
+
+	this.file.onParseComplete = ( function ( e ) {
+
+		if ( this.config.manager ) this.config.manager.add( this.domain );
+
+		( onParseComplete || this.onParseComplete ).call( this, e );
+
+	} ).bind( this );
+
+	this.file.onParseProgress = onParseProgress || this.onParseProgress;
+
+	// EXTENSIONS
+
+	var i = THREE.SEA3D.EXTENSIONS_LOADER.length;
+
+	while ( i -- ) {
+
+		var loader = THREE.SEA3D.EXTENSIONS_LOADER[ i ];
+
+		if ( loader.parse ) loader.parse.call( this );
+
+	}
+
+	this.file.parse();
+
+	return this.domain;
+
+};
+
+THREE.SEA3D.prototype.onHead = function ( args ) {
+
+	if ( args.sign != 'TJS' ) {
+
+		throw new Error( "Sign '" + args.sign + "' not supported! Use SEA3D Studio to publish or SEA3DLegacy.js" );
+
+	}
+
+};
+
+THREE.SEA3D.EXTENSIONS_LOADER = [];
+THREE.SEA3D.EXTENSIONS_DOMAIN = [];
+
+THREE.SEA3D.prototype.setTypeRead = function () {
+
+	this.file.typeRead = {};
+
+	this.file.typeRead[ SEA3D.Geometry.prototype.type ] = this.readGeometryBuffer;
+	this.file.typeRead[ SEA3D.Mesh.prototype.type ] = this.readMesh;
+	this.file.typeRead[ SEA3D.Sprite.prototype.type ] = this.readSprite;
+	this.file.typeRead[ SEA3D.Container3D.prototype.type ] = this.readContainer3D;
+	this.file.typeRead[ SEA3D.Line.prototype.type ] = this.readLine;
+	this.file.typeRead[ SEA3D.Material.prototype.type ] = this.readMaterial;
+	this.file.typeRead[ SEA3D.Camera.prototype.type ] = this.readCamera;
+	this.file.typeRead[ SEA3D.OrthographicCamera.prototype.type ] = this.readOrthographicCamera;
+	this.file.typeRead[ SEA3D.SkeletonLocal.prototype.type ] = this.readSkeletonLocal;
+	this.file.typeRead[ SEA3D.SkeletonAnimation.prototype.type ] = this.readSkeletonAnimation;
+	this.file.typeRead[ SEA3D.JointObject.prototype.type ] = this.readJointObject;
+	this.file.typeRead[ SEA3D.CubeMap.prototype.type ] = this.readCubeMap;
+	this.file.typeRead[ SEA3D.CubeRender.prototype.type ] = this.readCubeRender;
+	this.file.typeRead[ SEA3D.Animation.prototype.type ] =
+	this.file.typeRead[ SEA3D.MorphAnimation.prototype.type ] =
+	this.file.typeRead[ SEA3D.UVWAnimation.prototype.type ] = this.readAnimation;
+	this.file.typeRead[ SEA3D.SoundPoint.prototype.type ] = this.readSoundPoint;
+	this.file.typeRead[ SEA3D.TextureURL.prototype.type ] = this.readTextureURL;
+	this.file.typeRead[ SEA3D.CubeMapURL.prototype.type ] = this.readCubeMapURL;
+	this.file.typeRead[ SEA3D.TextureUpdate.prototype.type ] = this.readTextureUpdate;
+	this.file.typeRead[ SEA3D.Morph.prototype.type ] = this.readMorph;
+	this.file.typeRead[ SEA3D.VertexAnimation.prototype.type ] = this.readVertexAnimation;
+	this.file.typeRead[ SEA3D.Actions.prototype.type ] = this.readActions;
+	this.file.typeRead[ SEA3D.FileInfo.prototype.type ] = this.readFileInfo;
+	this.file.typeRead[ SEA3D.Properties.prototype.type ] = this.readProperties;
+
+	if ( this.config.dummys ) {
+
+		this.file.typeRead[ SEA3D.Dummy.prototype.type ] = this.readDummy;
+
+	}
+
+	if ( this.config.scripts ) {
+
+		this.file.typeRead[ SEA3D.ScriptURL.prototype.type ] = this.readScriptURL;
+		this.file.typeRead[ SEA3D.JavaScriptMethod.prototype.type ] = this.readJavaScriptMethod;
+
+	}
+
+	if ( this.config.lights ) {
+
+		this.file.typeRead[ SEA3D.PointLight.prototype.type ] = this.readPointLight;
+		this.file.typeRead[ SEA3D.DirectionalLight.prototype.type ] = this.readDirectionalLight;
+		this.file.typeRead[ SEA3D.HemisphereLight.prototype.type ] = this.readHemisphereLight;
+		this.file.typeRead[ SEA3D.AmbientLight.prototype.type ] = this.readAmbientLight;
+
+	}
+
+	// UNIVERSAL
+
+	this.file.typeRead[ SEA3D.JPEG.prototype.type ] =
+	this.file.typeRead[ SEA3D.JPEG_XR.prototype.type ] =
+	this.file.typeRead[ SEA3D.PNG.prototype.type ] =
+	this.file.typeRead[ SEA3D.GIF.prototype.type ] = this.readTexture;
+	this.file.typeRead[ SEA3D.MP3.prototype.type ] = this.readSound;
+	this.file.typeRead[ SEA3D.GLSL.prototype.type ] = this.readGLSL;
+
+	// EXTENSIONS
+
+	var i = THREE.SEA3D.EXTENSIONS_LOADER.length;
+
+	while ( i -- ) {
+
+		var loader = THREE.SEA3D.EXTENSIONS_LOADER[ i ];
+
+		if ( loader.setTypeRead ) loader.setTypeRead.call( this );
+
+	}
+
+};
+
+THREE.SEA3D.prototype.load = function ( data ) {
+
+	this.file = new SEA3D.File();
+	this.file.scope = this;
+	this.file.config = this.config;
+	this.file.onProgress = this.onLoadProgress.bind( this );
+	this.file.onCompleteObject = this.onCompleteObject.bind( this );
+	this.file.onDownloadProgress = this.onDownloadProgress.bind( this );
+	this.file.onParseProgress = this.onParseProgress.bind( this );
+	this.file.onParseComplete = this.onParseComplete.bind( this );
+	this.file.onError = this.onError.bind( this );
+	this.file.onHead = this.onHead.bind( this );
+
+	this.file.onComplete = ( function ( e ) {
+
+		if ( this.config.manager ) this.config.manager.add( this.domain );
+
+		this.onComplete.call( this, e );
+
+	} ).bind( this );
+
+	// SEA3D
+
+	this.createDomain();
+
+	this.setTypeRead();
+
+	if ( data === undefined ) return;
+
+	if ( typeof data === "string" ) this.file.load( data );
+	else this.file.read( data );
+
+};

+ 262 - 262
examples/js/objects/Reflector.js

@@ -1,262 +1,262 @@
-/**
- * @author Slayvin / http://slayvin.net
- */
-
-THREE.Reflector = function ( geometry, options ) {
-
-	THREE.Mesh.call( this, geometry );
-
-	this.type = 'Reflector';
-
-	var scope = this;
-
-	options = options || {};
-
-	var color = ( options.color !== undefined ) ? new THREE.Color( options.color ) : new THREE.Color( 0x7F7F7F );
-	var textureWidth = options.textureWidth || 512;
-	var textureHeight = options.textureHeight || 512;
-	var clipBias = options.clipBias || 0;
-	var shader = options.shader || THREE.Reflector.ReflectorShader;
-	var recursion = options.recursion !== undefined ? options.recursion : 0;
-
-	//
-
-	var reflectorPlane = new THREE.Plane();
-	var normal = new THREE.Vector3();
-	var reflectorWorldPosition = new THREE.Vector3();
-	var cameraWorldPosition = new THREE.Vector3();
-	var rotationMatrix = new THREE.Matrix4();
-	var lookAtPosition = new THREE.Vector3( 0, 0, - 1 );
-	var clipPlane = new THREE.Vector4();
-	var viewport = new THREE.Vector4();
-
-	var view = new THREE.Vector3();
-	var target = new THREE.Vector3();
-	var q = new THREE.Vector4();
-
-	var textureMatrix = new THREE.Matrix4();
-	var virtualCamera = new THREE.PerspectiveCamera();
-
-	var parameters = {
-		minFilter: THREE.LinearFilter,
-		magFilter: THREE.LinearFilter,
-		format: THREE.RGBFormat,
-		stencilBuffer: false
-	};
-
-	var renderTarget = new THREE.WebGLRenderTarget( textureWidth, textureHeight, parameters );
-
-	if ( ! THREE.Math.isPowerOfTwo( textureWidth ) || ! THREE.Math.isPowerOfTwo( textureHeight ) ) {
-
-		renderTarget.texture.generateMipmaps = false;
-
-	}
-
-	var material = new THREE.ShaderMaterial( {
-		uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
-		fragmentShader: shader.fragmentShader,
-		vertexShader: shader.vertexShader,
-
-	} );
-
-	material.uniforms.tDiffuse.value = renderTarget.texture;
-	material.uniforms.color.value = color;
-	material.uniforms.textureMatrix.value = textureMatrix;
-
-	this.material = material;
-
-	this.onBeforeRender = function ( renderer, scene, camera ) {
-
-		if ( 'recursion' in camera.userData ) {
-
-			if ( camera.userData.recursion === recursion ) return;
-
-			camera.userData.recursion ++;
-
-		}
-
-		reflectorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
-		cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
-
-		rotationMatrix.extractRotation( scope.matrixWorld );
-
-		normal.set( 0, 0, 1 );
-		normal.applyMatrix4( rotationMatrix );
-
-		view.subVectors( reflectorWorldPosition, cameraWorldPosition );
-
-		// Avoid rendering when reflector is facing away
-
-		if ( view.dot( normal ) > 0 ) return;
-
-		view.reflect( normal ).negate();
-		view.add( reflectorWorldPosition );
-
-		rotationMatrix.extractRotation( camera.matrixWorld );
-
-		lookAtPosition.set( 0, 0, - 1 );
-		lookAtPosition.applyMatrix4( rotationMatrix );
-		lookAtPosition.add( cameraWorldPosition );
-
-		target.subVectors( reflectorWorldPosition, lookAtPosition );
-		target.reflect( normal ).negate();
-		target.add( reflectorWorldPosition );
-
-		virtualCamera.position.copy( view );
-		virtualCamera.up.set( 0, 1, 0 );
-		virtualCamera.up.applyMatrix4( rotationMatrix );
-		virtualCamera.up.reflect( normal );
-		virtualCamera.lookAt( target );
-
-		virtualCamera.far = camera.far; // Used in WebGLBackground
-
-		virtualCamera.updateMatrixWorld();
-		virtualCamera.projectionMatrix.copy( camera.projectionMatrix );
-
-		virtualCamera.userData.recursion = 0;
-
-		// Update the texture matrix
-		textureMatrix.set(
-			0.5, 0.0, 0.0, 0.5,
-			0.0, 0.5, 0.0, 0.5,
-			0.0, 0.0, 0.5, 0.5,
-			0.0, 0.0, 0.0, 1.0
-		);
-		textureMatrix.multiply( virtualCamera.projectionMatrix );
-		textureMatrix.multiply( virtualCamera.matrixWorldInverse );
-		textureMatrix.multiply( scope.matrixWorld );
-
-		// Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html
-		// Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
-		reflectorPlane.setFromNormalAndCoplanarPoint( normal, reflectorWorldPosition );
-		reflectorPlane.applyMatrix4( virtualCamera.matrixWorldInverse );
-
-		clipPlane.set( reflectorPlane.normal.x, reflectorPlane.normal.y, reflectorPlane.normal.z, reflectorPlane.constant );
-
-		var projectionMatrix = virtualCamera.projectionMatrix;
-
-		q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
-		q.y = ( Math.sign( clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ];
-		q.z = - 1.0;
-		q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ];
-
-		// Calculate the scaled plane vector
-		clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) );
-
-		// Replacing the third row of the projection matrix
-		projectionMatrix.elements[ 2 ] = clipPlane.x;
-		projectionMatrix.elements[ 6 ] = clipPlane.y;
-		projectionMatrix.elements[ 10 ] = clipPlane.z + 1.0 - clipBias;
-		projectionMatrix.elements[ 14 ] = clipPlane.w;
-
-		// Render
-
-		scope.visible = false;
-
-		var currentRenderTarget = renderer.getRenderTarget();
-
-		var currentVrEnabled = renderer.vr.enabled;
-		var currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
-
-		renderer.vr.enabled = false; // Avoid camera modification and recursion
-		renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows
-
-		renderer.render( scene, virtualCamera, renderTarget, true );
-
-		renderer.vr.enabled = currentVrEnabled;
-		renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
-
-		renderer.setRenderTarget( currentRenderTarget );
-
-		// Restore viewport
-
-		var bounds = camera.bounds;
-
-		if ( bounds !== undefined ) {
-
-			var size = renderer.getSize();
-			var pixelRatio = renderer.getPixelRatio();
-
-			viewport.x = bounds.x * size.width * pixelRatio;
-			viewport.y = bounds.y * size.height * pixelRatio;
-			viewport.z = bounds.z * size.width * pixelRatio;
-			viewport.w = bounds.w * size.height * pixelRatio;
-
-			renderer.state.viewport( viewport );
-
-		}
-
-		scope.visible = true;
-
-	};
-
-	this.getRenderTarget = function () {
-
-		return renderTarget;
-
-	};
-
-};
-
-THREE.Reflector.prototype = Object.create( THREE.Mesh.prototype );
-THREE.Reflector.prototype.constructor = THREE.Reflector;
-
-THREE.Reflector.ReflectorShader = {
-
-	uniforms: {
-
-		'color': {
-			type: 'c',
-			value: null
-		},
-
-		'tDiffuse': {
-			type: 't',
-			value: null
-		},
-
-		'textureMatrix': {
-			type: 'm4',
-			value: null
-		}
-
-	},
-
-	vertexShader: [
-		'uniform mat4 textureMatrix;',
-		'varying vec4 vUv;',
-
-		'void main() {',
-
-		'	vUv = textureMatrix * vec4( position, 1.0 );',
-
-		'	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
-
-		'}'
-	].join( '\n' ),
-
-	fragmentShader: [
-		'uniform vec3 color;',
-		'uniform sampler2D tDiffuse;',
-		'varying vec4 vUv;',
-
-		'float blendOverlay( float base, float blend ) {',
-
-		'	return( base < 0.5 ? ( 2.0 * base * blend ) : ( 1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );',
-
-		'}',
-
-		'vec3 blendOverlay( vec3 base, vec3 blend ) {',
-
-		'	return vec3( blendOverlay( base.r, blend.r ), blendOverlay( base.g, blend.g ), blendOverlay( base.b, blend.b ) );',
-
-		'}',
-
-		'void main() {',
-
-		'	vec4 base = texture2DProj( tDiffuse, vUv );',
-		'	gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 );',
-
-		'}'
-	].join( '\n' )
-};
+/**
+ * @author Slayvin / http://slayvin.net
+ */
+
+THREE.Reflector = function ( geometry, options ) {
+
+	THREE.Mesh.call( this, geometry );
+
+	this.type = 'Reflector';
+
+	var scope = this;
+
+	options = options || {};
+
+	var color = ( options.color !== undefined ) ? new THREE.Color( options.color ) : new THREE.Color( 0x7F7F7F );
+	var textureWidth = options.textureWidth || 512;
+	var textureHeight = options.textureHeight || 512;
+	var clipBias = options.clipBias || 0;
+	var shader = options.shader || THREE.Reflector.ReflectorShader;
+	var recursion = options.recursion !== undefined ? options.recursion : 0;
+
+	//
+
+	var reflectorPlane = new THREE.Plane();
+	var normal = new THREE.Vector3();
+	var reflectorWorldPosition = new THREE.Vector3();
+	var cameraWorldPosition = new THREE.Vector3();
+	var rotationMatrix = new THREE.Matrix4();
+	var lookAtPosition = new THREE.Vector3( 0, 0, - 1 );
+	var clipPlane = new THREE.Vector4();
+	var viewport = new THREE.Vector4();
+
+	var view = new THREE.Vector3();
+	var target = new THREE.Vector3();
+	var q = new THREE.Vector4();
+
+	var textureMatrix = new THREE.Matrix4();
+	var virtualCamera = new THREE.PerspectiveCamera();
+
+	var parameters = {
+		minFilter: THREE.LinearFilter,
+		magFilter: THREE.LinearFilter,
+		format: THREE.RGBFormat,
+		stencilBuffer: false
+	};
+
+	var renderTarget = new THREE.WebGLRenderTarget( textureWidth, textureHeight, parameters );
+
+	if ( ! THREE.Math.isPowerOfTwo( textureWidth ) || ! THREE.Math.isPowerOfTwo( textureHeight ) ) {
+
+		renderTarget.texture.generateMipmaps = false;
+
+	}
+
+	var material = new THREE.ShaderMaterial( {
+		uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
+		fragmentShader: shader.fragmentShader,
+		vertexShader: shader.vertexShader,
+
+	} );
+
+	material.uniforms.tDiffuse.value = renderTarget.texture;
+	material.uniforms.color.value = color;
+	material.uniforms.textureMatrix.value = textureMatrix;
+
+	this.material = material;
+
+	this.onBeforeRender = function ( renderer, scene, camera ) {
+
+		if ( 'recursion' in camera.userData ) {
+
+			if ( camera.userData.recursion === recursion ) return;
+
+			camera.userData.recursion ++;
+
+		}
+
+		reflectorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
+		cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
+
+		rotationMatrix.extractRotation( scope.matrixWorld );
+
+		normal.set( 0, 0, 1 );
+		normal.applyMatrix4( rotationMatrix );
+
+		view.subVectors( reflectorWorldPosition, cameraWorldPosition );
+
+		// Avoid rendering when reflector is facing away
+
+		if ( view.dot( normal ) > 0 ) return;
+
+		view.reflect( normal ).negate();
+		view.add( reflectorWorldPosition );
+
+		rotationMatrix.extractRotation( camera.matrixWorld );
+
+		lookAtPosition.set( 0, 0, - 1 );
+		lookAtPosition.applyMatrix4( rotationMatrix );
+		lookAtPosition.add( cameraWorldPosition );
+
+		target.subVectors( reflectorWorldPosition, lookAtPosition );
+		target.reflect( normal ).negate();
+		target.add( reflectorWorldPosition );
+
+		virtualCamera.position.copy( view );
+		virtualCamera.up.set( 0, 1, 0 );
+		virtualCamera.up.applyMatrix4( rotationMatrix );
+		virtualCamera.up.reflect( normal );
+		virtualCamera.lookAt( target );
+
+		virtualCamera.far = camera.far; // Used in WebGLBackground
+
+		virtualCamera.updateMatrixWorld();
+		virtualCamera.projectionMatrix.copy( camera.projectionMatrix );
+
+		virtualCamera.userData.recursion = 0;
+
+		// Update the texture matrix
+		textureMatrix.set(
+			0.5, 0.0, 0.0, 0.5,
+			0.0, 0.5, 0.0, 0.5,
+			0.0, 0.0, 0.5, 0.5,
+			0.0, 0.0, 0.0, 1.0
+		);
+		textureMatrix.multiply( virtualCamera.projectionMatrix );
+		textureMatrix.multiply( virtualCamera.matrixWorldInverse );
+		textureMatrix.multiply( scope.matrixWorld );
+
+		// Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html
+		// Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
+		reflectorPlane.setFromNormalAndCoplanarPoint( normal, reflectorWorldPosition );
+		reflectorPlane.applyMatrix4( virtualCamera.matrixWorldInverse );
+
+		clipPlane.set( reflectorPlane.normal.x, reflectorPlane.normal.y, reflectorPlane.normal.z, reflectorPlane.constant );
+
+		var projectionMatrix = virtualCamera.projectionMatrix;
+
+		q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
+		q.y = ( Math.sign( clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ];
+		q.z = - 1.0;
+		q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ];
+
+		// Calculate the scaled plane vector
+		clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) );
+
+		// Replacing the third row of the projection matrix
+		projectionMatrix.elements[ 2 ] = clipPlane.x;
+		projectionMatrix.elements[ 6 ] = clipPlane.y;
+		projectionMatrix.elements[ 10 ] = clipPlane.z + 1.0 - clipBias;
+		projectionMatrix.elements[ 14 ] = clipPlane.w;
+
+		// Render
+
+		scope.visible = false;
+
+		var currentRenderTarget = renderer.getRenderTarget();
+
+		var currentVrEnabled = renderer.vr.enabled;
+		var currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
+
+		renderer.vr.enabled = false; // Avoid camera modification and recursion
+		renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows
+
+		renderer.render( scene, virtualCamera, renderTarget, true );
+
+		renderer.vr.enabled = currentVrEnabled;
+		renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
+
+		renderer.setRenderTarget( currentRenderTarget );
+
+		// Restore viewport
+
+		var bounds = camera.bounds;
+
+		if ( bounds !== undefined ) {
+
+			var size = renderer.getSize();
+			var pixelRatio = renderer.getPixelRatio();
+
+			viewport.x = bounds.x * size.width * pixelRatio;
+			viewport.y = bounds.y * size.height * pixelRatio;
+			viewport.z = bounds.z * size.width * pixelRatio;
+			viewport.w = bounds.w * size.height * pixelRatio;
+
+			renderer.state.viewport( viewport );
+
+		}
+
+		scope.visible = true;
+
+	};
+
+	this.getRenderTarget = function () {
+
+		return renderTarget;
+
+	};
+
+};
+
+THREE.Reflector.prototype = Object.create( THREE.Mesh.prototype );
+THREE.Reflector.prototype.constructor = THREE.Reflector;
+
+THREE.Reflector.ReflectorShader = {
+
+	uniforms: {
+
+		'color': {
+			type: 'c',
+			value: null
+		},
+
+		'tDiffuse': {
+			type: 't',
+			value: null
+		},
+
+		'textureMatrix': {
+			type: 'm4',
+			value: null
+		}
+
+	},
+
+	vertexShader: [
+		'uniform mat4 textureMatrix;',
+		'varying vec4 vUv;',
+
+		'void main() {',
+
+		'	vUv = textureMatrix * vec4( position, 1.0 );',
+
+		'	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
+
+		'}'
+	].join( '\n' ),
+
+	fragmentShader: [
+		'uniform vec3 color;',
+		'uniform sampler2D tDiffuse;',
+		'varying vec4 vUv;',
+
+		'float blendOverlay( float base, float blend ) {',
+
+		'	return( base < 0.5 ? ( 2.0 * base * blend ) : ( 1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );',
+
+		'}',
+
+		'vec3 blendOverlay( vec3 base, vec3 blend ) {',
+
+		'	return vec3( blendOverlay( base.r, blend.r ), blendOverlay( base.g, blend.g ), blendOverlay( base.b, blend.b ) );',
+
+		'}',
+
+		'void main() {',
+
+		'	vec4 base = texture2DProj( tDiffuse, vUv );',
+		'	gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 );',
+
+		'}'
+	].join( '\n' )
+};

文件差異過大導致無法顯示
+ 0 - 161
examples/js/postprocessing/SMAAPass.js


+ 64 - 64
examples/js/shaders/LuminosityHighPassShader.js

@@ -1,64 +1,64 @@
-/**
- * @author bhouston / http://clara.io/
- *
- * Luminosity
- * http://en.wikipedia.org/wiki/Luminosity
- */
-
-THREE.LuminosityHighPassShader = {
-
-  shaderID: "luminosityHighPass",
-
-	uniforms: {
-
-		"tDiffuse": { type: "t", value: null },
-		"luminosityThreshold": { type: "f", value: 1.0 },
-		"smoothWidth": { type: "f", value: 1.0 },
-		"defaultColor": { type: "c", value: new THREE.Color( 0x000000 ) },
-		"defaultOpacity":  { type: "f", value: 0.0 }
-
-	},
-
-	vertexShader: [
-
-		"varying vec2 vUv;",
-
-		"void main() {",
-
-			"vUv = uv;",
-
-			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-
-		"}"
-
-	].join("\n"),
-
-	fragmentShader: [
-
-		"uniform sampler2D tDiffuse;",
-		"uniform vec3 defaultColor;",
-		"uniform float defaultOpacity;",
-		"uniform float luminosityThreshold;",
-		"uniform float smoothWidth;",
-
-		"varying vec2 vUv;",
-
-		"void main() {",
-
-			"vec4 texel = texture2D( tDiffuse, vUv );",
-
-			"vec3 luma = vec3( 0.299, 0.587, 0.114 );",
-
-			"float v = dot( texel.xyz, luma );",
-
-			"vec4 outputColor = vec4( defaultColor.rgb, defaultOpacity );",
-
-			"float alpha = smoothstep( luminosityThreshold, luminosityThreshold + smoothWidth, v );",
-
-			"gl_FragColor = mix( outputColor, texel, alpha );",
-
-		"}"
-
-	].join("\n")
-
-};
+/**
+ * @author bhouston / http://clara.io/
+ *
+ * Luminosity
+ * http://en.wikipedia.org/wiki/Luminosity
+ */
+
+THREE.LuminosityHighPassShader = {
+
+  shaderID: "luminosityHighPass",
+
+	uniforms: {
+
+		"tDiffuse": { type: "t", value: null },
+		"luminosityThreshold": { type: "f", value: 1.0 },
+		"smoothWidth": { type: "f", value: 1.0 },
+		"defaultColor": { type: "c", value: new THREE.Color( 0x000000 ) },
+		"defaultOpacity":  { type: "f", value: 0.0 }
+
+	},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vUv = uv;",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join("\n"),
+
+	fragmentShader: [
+
+		"uniform sampler2D tDiffuse;",
+		"uniform vec3 defaultColor;",
+		"uniform float defaultOpacity;",
+		"uniform float luminosityThreshold;",
+		"uniform float smoothWidth;",
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vec4 texel = texture2D( tDiffuse, vUv );",
+
+			"vec3 luma = vec3( 0.299, 0.587, 0.114 );",
+
+			"float v = dot( texel.xyz, luma );",
+
+			"vec4 outputColor = vec4( defaultColor.rgb, defaultOpacity );",
+
+			"float alpha = smoothstep( luminosityThreshold, luminosityThreshold + smoothWidth, v );",
+
+			"gl_FragColor = mix( outputColor, texel, alpha );",
+
+		"}"
+
+	].join("\n")
+
+};

+ 1 - 1
examples/js/shaders/OceanShaders.js

@@ -1,4 +1,4 @@
-// Author: Aleksandr Albert
+// Author: Aleksandr Albert
 // Website: www.routter.co.tt
 
 // Description: A deep water ocean shader set

+ 462 - 462
examples/js/shaders/SMAAShader.js

@@ -1,462 +1,462 @@
-/**
- * @author mpk / http://polko.me/
- *
- * WebGL port of Subpixel Morphological Antialiasing (SMAA) v2.8
- * Preset: SMAA 1x Medium (with color edge detection)
- * https://github.com/iryoku/smaa/releases/tag/v2.8
- */
-
-THREE.SMAAShader = [ {
-
-	defines: {
-
-		"SMAA_THRESHOLD": "0.1"
-
-	},
-
-	uniforms: {
-
-		"tDiffuse":		{ value: null },
-		"resolution":	{ value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
-
-	},
-
-	vertexShader: [
-
-		"uniform vec2 resolution;",
-
-		"varying vec2 vUv;",
-		"varying vec4 vOffset[ 3 ];",
-
-		"void SMAAEdgeDetectionVS( vec2 texcoord ) {",
-			"vOffset[ 0 ] = texcoord.xyxy + resolution.xyxy * vec4( -1.0, 0.0, 0.0,  1.0 );", // WebGL port note: Changed sign in W component
-			"vOffset[ 1 ] = texcoord.xyxy + resolution.xyxy * vec4(  1.0, 0.0, 0.0, -1.0 );", // WebGL port note: Changed sign in W component
-			"vOffset[ 2 ] = texcoord.xyxy + resolution.xyxy * vec4( -2.0, 0.0, 0.0,  2.0 );", // WebGL port note: Changed sign in W component
-		"}",
-
-		"void main() {",
-
-			"vUv = uv;",
-
-			"SMAAEdgeDetectionVS( vUv );",
-
-			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-
-		"}"
-
-	].join("\n"),
-
-	fragmentShader: [
-
-		"uniform sampler2D tDiffuse;",
-
-		"varying vec2 vUv;",
-		"varying vec4 vOffset[ 3 ];",
-
-		"vec4 SMAAColorEdgeDetectionPS( vec2 texcoord, vec4 offset[3], sampler2D colorTex ) {",
-			"vec2 threshold = vec2( SMAA_THRESHOLD, SMAA_THRESHOLD );",
-
-			// Calculate color deltas:
-			"vec4 delta;",
-			"vec3 C = texture2D( colorTex, texcoord ).rgb;",
-
-			"vec3 Cleft = texture2D( colorTex, offset[0].xy ).rgb;",
-			"vec3 t = abs( C - Cleft );",
-			"delta.x = max( max( t.r, t.g ), t.b );",
-
-			"vec3 Ctop = texture2D( colorTex, offset[0].zw ).rgb;",
-			"t = abs( C - Ctop );",
-			"delta.y = max( max( t.r, t.g ), t.b );",
-
-			// We do the usual threshold:
-			"vec2 edges = step( threshold, delta.xy );",
-
-			// Then discard if there is no edge:
-			"if ( dot( edges, vec2( 1.0, 1.0 ) ) == 0.0 )",
-				"discard;",
-
-			// Calculate right and bottom deltas:
-			"vec3 Cright = texture2D( colorTex, offset[1].xy ).rgb;",
-			"t = abs( C - Cright );",
-			"delta.z = max( max( t.r, t.g ), t.b );",
-
-			"vec3 Cbottom  = texture2D( colorTex, offset[1].zw ).rgb;",
-			"t = abs( C - Cbottom );",
-			"delta.w = max( max( t.r, t.g ), t.b );",
-
-			// Calculate the maximum delta in the direct neighborhood:
-			"float maxDelta = max( max( max( delta.x, delta.y ), delta.z ), delta.w );",
-
-			// Calculate left-left and top-top deltas:
-			"vec3 Cleftleft  = texture2D( colorTex, offset[2].xy ).rgb;",
-			"t = abs( C - Cleftleft );",
-			"delta.z = max( max( t.r, t.g ), t.b );",
-
-			"vec3 Ctoptop = texture2D( colorTex, offset[2].zw ).rgb;",
-			"t = abs( C - Ctoptop );",
-			"delta.w = max( max( t.r, t.g ), t.b );",
-
-			// Calculate the final maximum delta:
-			"maxDelta = max( max( maxDelta, delta.z ), delta.w );",
-
-			// Local contrast adaptation in action:
-			"edges.xy *= step( 0.5 * maxDelta, delta.xy );",
-
-			"return vec4( edges, 0.0, 0.0 );",
-		"}",
-
-		"void main() {",
-
-			"gl_FragColor = SMAAColorEdgeDetectionPS( vUv, vOffset, tDiffuse );",
-
-		"}"
-
-	].join("\n")
-
-}, {
-
-	defines: {
-
-		"SMAA_MAX_SEARCH_STEPS":		"8",
-		"SMAA_AREATEX_MAX_DISTANCE":	"16",
-		"SMAA_AREATEX_PIXEL_SIZE":		"( 1.0 / vec2( 160.0, 560.0 ) )",
-		"SMAA_AREATEX_SUBTEX_SIZE":		"( 1.0 / 7.0 )"
-
-	},
-
-	uniforms: {
-
-		"tDiffuse":		{ value: null },
-		"tArea":		{ value: null },
-		"tSearch":		{ value: null },
-		"resolution":	{ value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
-
-	},
-
-	vertexShader: [
-
-		"uniform vec2 resolution;",
-
-		"varying vec2 vUv;",
-		"varying vec4 vOffset[ 3 ];",
-		"varying vec2 vPixcoord;",
-
-		"void SMAABlendingWeightCalculationVS( vec2 texcoord ) {",
-			"vPixcoord = texcoord / resolution;",
-
-			// We will use these offsets for the searches later on (see @PSEUDO_GATHER4):
-			"vOffset[ 0 ] = texcoord.xyxy + resolution.xyxy * vec4( -0.25, 0.125, 1.25, 0.125 );", // WebGL port note: Changed sign in Y and W components
-			"vOffset[ 1 ] = texcoord.xyxy + resolution.xyxy * vec4( -0.125, 0.25, -0.125, -1.25 );", // WebGL port note: Changed sign in Y and W components
-
-			// And these for the searches, they indicate the ends of the loops:
-			"vOffset[ 2 ] = vec4( vOffset[ 0 ].xz, vOffset[ 1 ].yw ) + vec4( -2.0, 2.0, -2.0, 2.0 ) * resolution.xxyy * float( SMAA_MAX_SEARCH_STEPS );",
-
-		"}",
-
-		"void main() {",
-
-			"vUv = uv;",
-
-			"SMAABlendingWeightCalculationVS( vUv );",
-
-			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-
-		"}"
-
-	].join("\n"),
-
-	fragmentShader: [
-
-		"#define SMAASampleLevelZeroOffset( tex, coord, offset ) texture2D( tex, coord + float( offset ) * resolution, 0.0 )",
-
-		"uniform sampler2D tDiffuse;",
-		"uniform sampler2D tArea;",
-		"uniform sampler2D tSearch;",
-		"uniform vec2 resolution;",
-
-		"varying vec2 vUv;",
-		"varying vec4 vOffset[3];",
-		"varying vec2 vPixcoord;",
-
-		"vec2 round( vec2 x ) {",
-			"return sign( x ) * floor( abs( x ) + 0.5 );",
-		"}",
-
-		"float SMAASearchLength( sampler2D searchTex, vec2 e, float bias, float scale ) {",
-			// Not required if searchTex accesses are set to point:
-			// float2 SEARCH_TEX_PIXEL_SIZE = 1.0 / float2(66.0, 33.0);
-			// e = float2(bias, 0.0) + 0.5 * SEARCH_TEX_PIXEL_SIZE +
-			//     e * float2(scale, 1.0) * float2(64.0, 32.0) * SEARCH_TEX_PIXEL_SIZE;
-			"e.r = bias + e.r * scale;",
-			"return 255.0 * texture2D( searchTex, e, 0.0 ).r;",
-		"}",
-
-		"float SMAASearchXLeft( sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end ) {",
-			/**
-			* @PSEUDO_GATHER4
-			* This texcoord has been offset by (-0.25, -0.125) in the vertex shader to
-			* sample between edge, thus fetching four edges in a row.
-			* Sampling with different offsets in each direction allows to disambiguate
-			* which edges are active from the four fetched ones.
-			*/
-			"vec2 e = vec2( 0.0, 1.0 );",
-
-			"for ( int i = 0; i < SMAA_MAX_SEARCH_STEPS; i ++ ) {", // WebGL port note: Changed while to for
-				"e = texture2D( edgesTex, texcoord, 0.0 ).rg;",
-				"texcoord -= vec2( 2.0, 0.0 ) * resolution;",
-				"if ( ! ( texcoord.x > end && e.g > 0.8281 && e.r == 0.0 ) ) break;",
-			"}",
-
-			// We correct the previous (-0.25, -0.125) offset we applied:
-			"texcoord.x += 0.25 * resolution.x;",
-
-			// The searches are bias by 1, so adjust the coords accordingly:
-			"texcoord.x += resolution.x;",
-
-			// Disambiguate the length added by the last step:
-			"texcoord.x += 2.0 * resolution.x;", // Undo last step
-			"texcoord.x -= resolution.x * SMAASearchLength(searchTex, e, 0.0, 0.5);",
-
-			"return texcoord.x;",
-		"}",
-
-		"float SMAASearchXRight( sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end ) {",
-			"vec2 e = vec2( 0.0, 1.0 );",
-
-			"for ( int i = 0; i < SMAA_MAX_SEARCH_STEPS; i ++ ) {", // WebGL port note: Changed while to for
-				"e = texture2D( edgesTex, texcoord, 0.0 ).rg;",
-				"texcoord += vec2( 2.0, 0.0 ) * resolution;",
-				"if ( ! ( texcoord.x < end && e.g > 0.8281 && e.r == 0.0 ) ) break;",
-			"}",
-
-			"texcoord.x -= 0.25 * resolution.x;",
-			"texcoord.x -= resolution.x;",
-			"texcoord.x -= 2.0 * resolution.x;",
-			"texcoord.x += resolution.x * SMAASearchLength( searchTex, e, 0.5, 0.5 );",
-
-			"return texcoord.x;",
-		"}",
-
-		"float SMAASearchYUp( sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end ) {",
-			"vec2 e = vec2( 1.0, 0.0 );",
-
-			"for ( int i = 0; i < SMAA_MAX_SEARCH_STEPS; i ++ ) {", // WebGL port note: Changed while to for
-				"e = texture2D( edgesTex, texcoord, 0.0 ).rg;",
-				"texcoord += vec2( 0.0, 2.0 ) * resolution;", // WebGL port note: Changed sign
-				"if ( ! ( texcoord.y > end && e.r > 0.8281 && e.g == 0.0 ) ) break;",
-			"}",
-
-			"texcoord.y -= 0.25 * resolution.y;", // WebGL port note: Changed sign
-			"texcoord.y -= resolution.y;", // WebGL port note: Changed sign
-			"texcoord.y -= 2.0 * resolution.y;", // WebGL port note: Changed sign
-			"texcoord.y += resolution.y * SMAASearchLength( searchTex, e.gr, 0.0, 0.5 );", // WebGL port note: Changed sign
-
-			"return texcoord.y;",
-		"}",
-
-		"float SMAASearchYDown( sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end ) {",
-			"vec2 e = vec2( 1.0, 0.0 );",
-
-			"for ( int i = 0; i < SMAA_MAX_SEARCH_STEPS; i ++ ) {", // WebGL port note: Changed while to for
-				"e = texture2D( edgesTex, texcoord, 0.0 ).rg;",
-				"texcoord -= vec2( 0.0, 2.0 ) * resolution;", // WebGL port note: Changed sign
-				"if ( ! ( texcoord.y < end && e.r > 0.8281 && e.g == 0.0 ) ) break;",
-			"}",
-
-			"texcoord.y += 0.25 * resolution.y;", // WebGL port note: Changed sign
-			"texcoord.y += resolution.y;", // WebGL port note: Changed sign
-			"texcoord.y += 2.0 * resolution.y;", // WebGL port note: Changed sign
-			"texcoord.y -= resolution.y * SMAASearchLength( searchTex, e.gr, 0.5, 0.5 );", // WebGL port note: Changed sign
-
-			"return texcoord.y;",
-		"}",
-
-		"vec2 SMAAArea( sampler2D areaTex, vec2 dist, float e1, float e2, float offset ) {",
-			// Rounding prevents precision errors of bilinear filtering:
-			"vec2 texcoord = float( SMAA_AREATEX_MAX_DISTANCE ) * round( 4.0 * vec2( e1, e2 ) ) + dist;",
-
-			// We do a scale and bias for mapping to texel space:
-			"texcoord = SMAA_AREATEX_PIXEL_SIZE * texcoord + ( 0.5 * SMAA_AREATEX_PIXEL_SIZE );",
-
-			// Move to proper place, according to the subpixel offset:
-			"texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset;",
-
-			"return texture2D( areaTex, texcoord, 0.0 ).rg;",
-		"}",
-
-		"vec4 SMAABlendingWeightCalculationPS( vec2 texcoord, vec2 pixcoord, vec4 offset[ 3 ], sampler2D edgesTex, sampler2D areaTex, sampler2D searchTex, ivec4 subsampleIndices ) {",
-			"vec4 weights = vec4( 0.0, 0.0, 0.0, 0.0 );",
-
-			"vec2 e = texture2D( edgesTex, texcoord ).rg;",
-
-			"if ( e.g > 0.0 ) {", // Edge at north
-				"vec2 d;",
-
-				// Find the distance to the left:
-				"vec2 coords;",
-				"coords.x = SMAASearchXLeft( edgesTex, searchTex, offset[ 0 ].xy, offset[ 2 ].x );",
-				"coords.y = offset[ 1 ].y;", // offset[1].y = texcoord.y - 0.25 * resolution.y (@CROSSING_OFFSET)
-				"d.x = coords.x;",
-
-				// Now fetch the left crossing edges, two at a time using bilinear
-				// filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to
-				// discern what value each edge has:
-				"float e1 = texture2D( edgesTex, coords, 0.0 ).r;",
-
-				// Find the distance to the right:
-				"coords.x = SMAASearchXRight( edgesTex, searchTex, offset[ 0 ].zw, offset[ 2 ].y );",
-				"d.y = coords.x;",
-
-				// We want the distances to be in pixel units (doing this here allow to
-				// better interleave arithmetic and memory accesses):
-				"d = d / resolution.x - pixcoord.x;",
-
-				// SMAAArea below needs a sqrt, as the areas texture is compressed
-				// quadratically:
-				"vec2 sqrt_d = sqrt( abs( d ) );",
-
-				// Fetch the right crossing edges:
-				"coords.y -= 1.0 * resolution.y;", // WebGL port note: Added
-				"float e2 = SMAASampleLevelZeroOffset( edgesTex, coords, ivec2( 1, 0 ) ).r;",
-
-				// Ok, we know how this pattern looks like, now it is time for getting
-				// the actual area:
-				"weights.rg = SMAAArea( areaTex, sqrt_d, e1, e2, float( subsampleIndices.y ) );",
-			"}",
-
-			"if ( e.r > 0.0 ) {", // Edge at west
-				"vec2 d;",
-
-				// Find the distance to the top:
-				"vec2 coords;",
-
-				"coords.y = SMAASearchYUp( edgesTex, searchTex, offset[ 1 ].xy, offset[ 2 ].z );",
-				"coords.x = offset[ 0 ].x;", // offset[1].x = texcoord.x - 0.25 * resolution.x;
-				"d.x = coords.y;",
-
-				// Fetch the top crossing edges:
-				"float e1 = texture2D( edgesTex, coords, 0.0 ).g;",
-
-				// Find the distance to the bottom:
-				"coords.y = SMAASearchYDown( edgesTex, searchTex, offset[ 1 ].zw, offset[ 2 ].w );",
-				"d.y = coords.y;",
-
-				// We want the distances to be in pixel units:
-				"d = d / resolution.y - pixcoord.y;",
-
-				// SMAAArea below needs a sqrt, as the areas texture is compressed
-				// quadratically:
-				"vec2 sqrt_d = sqrt( abs( d ) );",
-
-				// Fetch the bottom crossing edges:
-				"coords.y -= 1.0 * resolution.y;", // WebGL port note: Added
-				"float e2 = SMAASampleLevelZeroOffset( edgesTex, coords, ivec2( 0, 1 ) ).g;",
-
-				// Get the area for this direction:
-				"weights.ba = SMAAArea( areaTex, sqrt_d, e1, e2, float( subsampleIndices.x ) );",
-			"}",
-
-			"return weights;",
-		"}",
-
-		"void main() {",
-
-			"gl_FragColor = SMAABlendingWeightCalculationPS( vUv, vPixcoord, vOffset, tDiffuse, tArea, tSearch, ivec4( 0.0 ) );",
-
-		"}"
-
-	].join("\n")
-
-}, {
-
-	uniforms: {
-
-		"tDiffuse":		{ value: null },
-		"tColor":		{ value: null },
-		"resolution":	{ value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
-
-	},
-
-	vertexShader: [
-
-		"uniform vec2 resolution;",
-
-		"varying vec2 vUv;",
-		"varying vec4 vOffset[ 2 ];",
-
-		"void SMAANeighborhoodBlendingVS( vec2 texcoord ) {",
-			"vOffset[ 0 ] = texcoord.xyxy + resolution.xyxy * vec4( -1.0, 0.0, 0.0, 1.0 );", // WebGL port note: Changed sign in W component
-			"vOffset[ 1 ] = texcoord.xyxy + resolution.xyxy * vec4( 1.0, 0.0, 0.0, -1.0 );", // WebGL port note: Changed sign in W component
-		"}",
-
-		"void main() {",
-
-			"vUv = uv;",
-
-			"SMAANeighborhoodBlendingVS( vUv );",
-
-			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-
-		"}"
-
-	].join("\n"),
-
-	fragmentShader: [
-
-		"uniform sampler2D tDiffuse;",
-		"uniform sampler2D tColor;",
-		"uniform vec2 resolution;",
-
-		"varying vec2 vUv;",
-		"varying vec4 vOffset[ 2 ];",
-
-		"vec4 SMAANeighborhoodBlendingPS( vec2 texcoord, vec4 offset[ 2 ], sampler2D colorTex, sampler2D blendTex ) {",
-			// Fetch the blending weights for current pixel:
-			"vec4 a;",
-			"a.xz = texture2D( blendTex, texcoord ).xz;",
-			"a.y = texture2D( blendTex, offset[ 1 ].zw ).g;",
-			"a.w = texture2D( blendTex, offset[ 1 ].xy ).a;",
-
-			// Is there any blending weight with a value greater than 0.0?
-			"if ( dot(a, vec4( 1.0, 1.0, 1.0, 1.0 )) < 1e-5 ) {",
-				"return texture2D( colorTex, texcoord, 0.0 );",
-			"} else {",
-				// Up to 4 lines can be crossing a pixel (one through each edge). We
-				// favor blending by choosing the line with the maximum weight for each
-				// direction:
-				"vec2 offset;",
-				"offset.x = a.a > a.b ? a.a : -a.b;", // left vs. right
-				"offset.y = a.g > a.r ? -a.g : a.r;", // top vs. bottom // WebGL port note: Changed signs
-
-				// Then we go in the direction that has the maximum weight:
-				"if ( abs( offset.x ) > abs( offset.y )) {", // horizontal vs. vertical
-					"offset.y = 0.0;",
-				"} else {",
-					"offset.x = 0.0;",
-				"}",
-
-				// Fetch the opposite color and lerp by hand:
-				"vec4 C = texture2D( colorTex, texcoord, 0.0 );",
-				"texcoord += sign( offset ) * resolution;",
-				"vec4 Cop = texture2D( colorTex, texcoord, 0.0 );",
-				"float s = abs( offset.x ) > abs( offset.y ) ? abs( offset.x ) : abs( offset.y );",
-
-				// WebGL port note: Added gamma correction
-				"C.xyz = pow(C.xyz, vec3(2.2));",
-				"Cop.xyz = pow(Cop.xyz, vec3(2.2));",
-				"vec4 mixed = mix(C, Cop, s);",
-				"mixed.xyz = pow(mixed.xyz, vec3(1.0 / 2.2));",
-
-				"return mixed;",
-			"}",
-		"}",
-
-		"void main() {",
-
-			"gl_FragColor = SMAANeighborhoodBlendingPS( vUv, vOffset, tColor, tDiffuse );",
-
-		"}"
-
-	].join("\n")
-
-} ];
+/**
+ * @author mpk / http://polko.me/
+ *
+ * WebGL port of Subpixel Morphological Antialiasing (SMAA) v2.8
+ * Preset: SMAA 1x Medium (with color edge detection)
+ * https://github.com/iryoku/smaa/releases/tag/v2.8
+ */
+
+THREE.SMAAShader = [ {
+
+	defines: {
+
+		"SMAA_THRESHOLD": "0.1"
+
+	},
+
+	uniforms: {
+
+		"tDiffuse":		{ value: null },
+		"resolution":	{ value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
+
+	},
+
+	vertexShader: [
+
+		"uniform vec2 resolution;",
+
+		"varying vec2 vUv;",
+		"varying vec4 vOffset[ 3 ];",
+
+		"void SMAAEdgeDetectionVS( vec2 texcoord ) {",
+			"vOffset[ 0 ] = texcoord.xyxy + resolution.xyxy * vec4( -1.0, 0.0, 0.0,  1.0 );", // WebGL port note: Changed sign in W component
+			"vOffset[ 1 ] = texcoord.xyxy + resolution.xyxy * vec4(  1.0, 0.0, 0.0, -1.0 );", // WebGL port note: Changed sign in W component
+			"vOffset[ 2 ] = texcoord.xyxy + resolution.xyxy * vec4( -2.0, 0.0, 0.0,  2.0 );", // WebGL port note: Changed sign in W component
+		"}",
+
+		"void main() {",
+
+			"vUv = uv;",
+
+			"SMAAEdgeDetectionVS( vUv );",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join("\n"),
+
+	fragmentShader: [
+
+		"uniform sampler2D tDiffuse;",
+
+		"varying vec2 vUv;",
+		"varying vec4 vOffset[ 3 ];",
+
+		"vec4 SMAAColorEdgeDetectionPS( vec2 texcoord, vec4 offset[3], sampler2D colorTex ) {",
+			"vec2 threshold = vec2( SMAA_THRESHOLD, SMAA_THRESHOLD );",
+
+			// Calculate color deltas:
+			"vec4 delta;",
+			"vec3 C = texture2D( colorTex, texcoord ).rgb;",
+
+			"vec3 Cleft = texture2D( colorTex, offset[0].xy ).rgb;",
+			"vec3 t = abs( C - Cleft );",
+			"delta.x = max( max( t.r, t.g ), t.b );",
+
+			"vec3 Ctop = texture2D( colorTex, offset[0].zw ).rgb;",
+			"t = abs( C - Ctop );",
+			"delta.y = max( max( t.r, t.g ), t.b );",
+
+			// We do the usual threshold:
+			"vec2 edges = step( threshold, delta.xy );",
+
+			// Then discard if there is no edge:
+			"if ( dot( edges, vec2( 1.0, 1.0 ) ) == 0.0 )",
+				"discard;",
+
+			// Calculate right and bottom deltas:
+			"vec3 Cright = texture2D( colorTex, offset[1].xy ).rgb;",
+			"t = abs( C - Cright );",
+			"delta.z = max( max( t.r, t.g ), t.b );",
+
+			"vec3 Cbottom  = texture2D( colorTex, offset[1].zw ).rgb;",
+			"t = abs( C - Cbottom );",
+			"delta.w = max( max( t.r, t.g ), t.b );",
+
+			// Calculate the maximum delta in the direct neighborhood:
+			"float maxDelta = max( max( max( delta.x, delta.y ), delta.z ), delta.w );",
+
+			// Calculate left-left and top-top deltas:
+			"vec3 Cleftleft  = texture2D( colorTex, offset[2].xy ).rgb;",
+			"t = abs( C - Cleftleft );",
+			"delta.z = max( max( t.r, t.g ), t.b );",
+
+			"vec3 Ctoptop = texture2D( colorTex, offset[2].zw ).rgb;",
+			"t = abs( C - Ctoptop );",
+			"delta.w = max( max( t.r, t.g ), t.b );",
+
+			// Calculate the final maximum delta:
+			"maxDelta = max( max( maxDelta, delta.z ), delta.w );",
+
+			// Local contrast adaptation in action:
+			"edges.xy *= step( 0.5 * maxDelta, delta.xy );",
+
+			"return vec4( edges, 0.0, 0.0 );",
+		"}",
+
+		"void main() {",
+
+			"gl_FragColor = SMAAColorEdgeDetectionPS( vUv, vOffset, tDiffuse );",
+
+		"}"
+
+	].join("\n")
+
+}, {
+
+	defines: {
+
+		"SMAA_MAX_SEARCH_STEPS":		"8",
+		"SMAA_AREATEX_MAX_DISTANCE":	"16",
+		"SMAA_AREATEX_PIXEL_SIZE":		"( 1.0 / vec2( 160.0, 560.0 ) )",
+		"SMAA_AREATEX_SUBTEX_SIZE":		"( 1.0 / 7.0 )"
+
+	},
+
+	uniforms: {
+
+		"tDiffuse":		{ value: null },
+		"tArea":		{ value: null },
+		"tSearch":		{ value: null },
+		"resolution":	{ value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
+
+	},
+
+	vertexShader: [
+
+		"uniform vec2 resolution;",
+
+		"varying vec2 vUv;",
+		"varying vec4 vOffset[ 3 ];",
+		"varying vec2 vPixcoord;",
+
+		"void SMAABlendingWeightCalculationVS( vec2 texcoord ) {",
+			"vPixcoord = texcoord / resolution;",
+
+			// We will use these offsets for the searches later on (see @PSEUDO_GATHER4):
+			"vOffset[ 0 ] = texcoord.xyxy + resolution.xyxy * vec4( -0.25, 0.125, 1.25, 0.125 );", // WebGL port note: Changed sign in Y and W components
+			"vOffset[ 1 ] = texcoord.xyxy + resolution.xyxy * vec4( -0.125, 0.25, -0.125, -1.25 );", // WebGL port note: Changed sign in Y and W components
+
+			// And these for the searches, they indicate the ends of the loops:
+			"vOffset[ 2 ] = vec4( vOffset[ 0 ].xz, vOffset[ 1 ].yw ) + vec4( -2.0, 2.0, -2.0, 2.0 ) * resolution.xxyy * float( SMAA_MAX_SEARCH_STEPS );",
+
+		"}",
+
+		"void main() {",
+
+			"vUv = uv;",
+
+			"SMAABlendingWeightCalculationVS( vUv );",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join("\n"),
+
+	fragmentShader: [
+
+		"#define SMAASampleLevelZeroOffset( tex, coord, offset ) texture2D( tex, coord + float( offset ) * resolution, 0.0 )",
+
+		"uniform sampler2D tDiffuse;",
+		"uniform sampler2D tArea;",
+		"uniform sampler2D tSearch;",
+		"uniform vec2 resolution;",
+
+		"varying vec2 vUv;",
+		"varying vec4 vOffset[3];",
+		"varying vec2 vPixcoord;",
+
+		"vec2 round( vec2 x ) {",
+			"return sign( x ) * floor( abs( x ) + 0.5 );",
+		"}",
+
+		"float SMAASearchLength( sampler2D searchTex, vec2 e, float bias, float scale ) {",
+			// Not required if searchTex accesses are set to point:
+			// float2 SEARCH_TEX_PIXEL_SIZE = 1.0 / float2(66.0, 33.0);
+			// e = float2(bias, 0.0) + 0.5 * SEARCH_TEX_PIXEL_SIZE +
+			//     e * float2(scale, 1.0) * float2(64.0, 32.0) * SEARCH_TEX_PIXEL_SIZE;
+			"e.r = bias + e.r * scale;",
+			"return 255.0 * texture2D( searchTex, e, 0.0 ).r;",
+		"}",
+
+		"float SMAASearchXLeft( sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end ) {",
+			/**
+			* @PSEUDO_GATHER4
+			* This texcoord has been offset by (-0.25, -0.125) in the vertex shader to
+			* sample between edge, thus fetching four edges in a row.
+			* Sampling with different offsets in each direction allows to disambiguate
+			* which edges are active from the four fetched ones.
+			*/
+			"vec2 e = vec2( 0.0, 1.0 );",
+
+			"for ( int i = 0; i < SMAA_MAX_SEARCH_STEPS; i ++ ) {", // WebGL port note: Changed while to for
+				"e = texture2D( edgesTex, texcoord, 0.0 ).rg;",
+				"texcoord -= vec2( 2.0, 0.0 ) * resolution;",
+				"if ( ! ( texcoord.x > end && e.g > 0.8281 && e.r == 0.0 ) ) break;",
+			"}",
+
+			// We correct the previous (-0.25, -0.125) offset we applied:
+			"texcoord.x += 0.25 * resolution.x;",
+
+			// The searches are bias by 1, so adjust the coords accordingly:
+			"texcoord.x += resolution.x;",
+
+			// Disambiguate the length added by the last step:
+			"texcoord.x += 2.0 * resolution.x;", // Undo last step
+			"texcoord.x -= resolution.x * SMAASearchLength(searchTex, e, 0.0, 0.5);",
+
+			"return texcoord.x;",
+		"}",
+
+		"float SMAASearchXRight( sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end ) {",
+			"vec2 e = vec2( 0.0, 1.0 );",
+
+			"for ( int i = 0; i < SMAA_MAX_SEARCH_STEPS; i ++ ) {", // WebGL port note: Changed while to for
+				"e = texture2D( edgesTex, texcoord, 0.0 ).rg;",
+				"texcoord += vec2( 2.0, 0.0 ) * resolution;",
+				"if ( ! ( texcoord.x < end && e.g > 0.8281 && e.r == 0.0 ) ) break;",
+			"}",
+
+			"texcoord.x -= 0.25 * resolution.x;",
+			"texcoord.x -= resolution.x;",
+			"texcoord.x -= 2.0 * resolution.x;",
+			"texcoord.x += resolution.x * SMAASearchLength( searchTex, e, 0.5, 0.5 );",
+
+			"return texcoord.x;",
+		"}",
+
+		"float SMAASearchYUp( sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end ) {",
+			"vec2 e = vec2( 1.0, 0.0 );",
+
+			"for ( int i = 0; i < SMAA_MAX_SEARCH_STEPS; i ++ ) {", // WebGL port note: Changed while to for
+				"e = texture2D( edgesTex, texcoord, 0.0 ).rg;",
+				"texcoord += vec2( 0.0, 2.0 ) * resolution;", // WebGL port note: Changed sign
+				"if ( ! ( texcoord.y > end && e.r > 0.8281 && e.g == 0.0 ) ) break;",
+			"}",
+
+			"texcoord.y -= 0.25 * resolution.y;", // WebGL port note: Changed sign
+			"texcoord.y -= resolution.y;", // WebGL port note: Changed sign
+			"texcoord.y -= 2.0 * resolution.y;", // WebGL port note: Changed sign
+			"texcoord.y += resolution.y * SMAASearchLength( searchTex, e.gr, 0.0, 0.5 );", // WebGL port note: Changed sign
+
+			"return texcoord.y;",
+		"}",
+
+		"float SMAASearchYDown( sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end ) {",
+			"vec2 e = vec2( 1.0, 0.0 );",
+
+			"for ( int i = 0; i < SMAA_MAX_SEARCH_STEPS; i ++ ) {", // WebGL port note: Changed while to for
+				"e = texture2D( edgesTex, texcoord, 0.0 ).rg;",
+				"texcoord -= vec2( 0.0, 2.0 ) * resolution;", // WebGL port note: Changed sign
+				"if ( ! ( texcoord.y < end && e.r > 0.8281 && e.g == 0.0 ) ) break;",
+			"}",
+
+			"texcoord.y += 0.25 * resolution.y;", // WebGL port note: Changed sign
+			"texcoord.y += resolution.y;", // WebGL port note: Changed sign
+			"texcoord.y += 2.0 * resolution.y;", // WebGL port note: Changed sign
+			"texcoord.y -= resolution.y * SMAASearchLength( searchTex, e.gr, 0.5, 0.5 );", // WebGL port note: Changed sign
+
+			"return texcoord.y;",
+		"}",
+
+		"vec2 SMAAArea( sampler2D areaTex, vec2 dist, float e1, float e2, float offset ) {",
+			// Rounding prevents precision errors of bilinear filtering:
+			"vec2 texcoord = float( SMAA_AREATEX_MAX_DISTANCE ) * round( 4.0 * vec2( e1, e2 ) ) + dist;",
+
+			// We do a scale and bias for mapping to texel space:
+			"texcoord = SMAA_AREATEX_PIXEL_SIZE * texcoord + ( 0.5 * SMAA_AREATEX_PIXEL_SIZE );",
+
+			// Move to proper place, according to the subpixel offset:
+			"texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset;",
+
+			"return texture2D( areaTex, texcoord, 0.0 ).rg;",
+		"}",
+
+		"vec4 SMAABlendingWeightCalculationPS( vec2 texcoord, vec2 pixcoord, vec4 offset[ 3 ], sampler2D edgesTex, sampler2D areaTex, sampler2D searchTex, ivec4 subsampleIndices ) {",
+			"vec4 weights = vec4( 0.0, 0.0, 0.0, 0.0 );",
+
+			"vec2 e = texture2D( edgesTex, texcoord ).rg;",
+
+			"if ( e.g > 0.0 ) {", // Edge at north
+				"vec2 d;",
+
+				// Find the distance to the left:
+				"vec2 coords;",
+				"coords.x = SMAASearchXLeft( edgesTex, searchTex, offset[ 0 ].xy, offset[ 2 ].x );",
+				"coords.y = offset[ 1 ].y;", // offset[1].y = texcoord.y - 0.25 * resolution.y (@CROSSING_OFFSET)
+				"d.x = coords.x;",
+
+				// Now fetch the left crossing edges, two at a time using bilinear
+				// filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to
+				// discern what value each edge has:
+				"float e1 = texture2D( edgesTex, coords, 0.0 ).r;",
+
+				// Find the distance to the right:
+				"coords.x = SMAASearchXRight( edgesTex, searchTex, offset[ 0 ].zw, offset[ 2 ].y );",
+				"d.y = coords.x;",
+
+				// We want the distances to be in pixel units (doing this here allow to
+				// better interleave arithmetic and memory accesses):
+				"d = d / resolution.x - pixcoord.x;",
+
+				// SMAAArea below needs a sqrt, as the areas texture is compressed
+				// quadratically:
+				"vec2 sqrt_d = sqrt( abs( d ) );",
+
+				// Fetch the right crossing edges:
+				"coords.y -= 1.0 * resolution.y;", // WebGL port note: Added
+				"float e2 = SMAASampleLevelZeroOffset( edgesTex, coords, ivec2( 1, 0 ) ).r;",
+
+				// Ok, we know how this pattern looks like, now it is time for getting
+				// the actual area:
+				"weights.rg = SMAAArea( areaTex, sqrt_d, e1, e2, float( subsampleIndices.y ) );",
+			"}",
+
+			"if ( e.r > 0.0 ) {", // Edge at west
+				"vec2 d;",
+
+				// Find the distance to the top:
+				"vec2 coords;",
+
+				"coords.y = SMAASearchYUp( edgesTex, searchTex, offset[ 1 ].xy, offset[ 2 ].z );",
+				"coords.x = offset[ 0 ].x;", // offset[1].x = texcoord.x - 0.25 * resolution.x;
+				"d.x = coords.y;",
+
+				// Fetch the top crossing edges:
+				"float e1 = texture2D( edgesTex, coords, 0.0 ).g;",
+
+				// Find the distance to the bottom:
+				"coords.y = SMAASearchYDown( edgesTex, searchTex, offset[ 1 ].zw, offset[ 2 ].w );",
+				"d.y = coords.y;",
+
+				// We want the distances to be in pixel units:
+				"d = d / resolution.y - pixcoord.y;",
+
+				// SMAAArea below needs a sqrt, as the areas texture is compressed
+				// quadratically:
+				"vec2 sqrt_d = sqrt( abs( d ) );",
+
+				// Fetch the bottom crossing edges:
+				"coords.y -= 1.0 * resolution.y;", // WebGL port note: Added
+				"float e2 = SMAASampleLevelZeroOffset( edgesTex, coords, ivec2( 0, 1 ) ).g;",
+
+				// Get the area for this direction:
+				"weights.ba = SMAAArea( areaTex, sqrt_d, e1, e2, float( subsampleIndices.x ) );",
+			"}",
+
+			"return weights;",
+		"}",
+
+		"void main() {",
+
+			"gl_FragColor = SMAABlendingWeightCalculationPS( vUv, vPixcoord, vOffset, tDiffuse, tArea, tSearch, ivec4( 0.0 ) );",
+
+		"}"
+
+	].join("\n")
+
+}, {
+
+	uniforms: {
+
+		"tDiffuse":		{ value: null },
+		"tColor":		{ value: null },
+		"resolution":	{ value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
+
+	},
+
+	vertexShader: [
+
+		"uniform vec2 resolution;",
+
+		"varying vec2 vUv;",
+		"varying vec4 vOffset[ 2 ];",
+
+		"void SMAANeighborhoodBlendingVS( vec2 texcoord ) {",
+			"vOffset[ 0 ] = texcoord.xyxy + resolution.xyxy * vec4( -1.0, 0.0, 0.0, 1.0 );", // WebGL port note: Changed sign in W component
+			"vOffset[ 1 ] = texcoord.xyxy + resolution.xyxy * vec4( 1.0, 0.0, 0.0, -1.0 );", // WebGL port note: Changed sign in W component
+		"}",
+
+		"void main() {",
+
+			"vUv = uv;",
+
+			"SMAANeighborhoodBlendingVS( vUv );",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join("\n"),
+
+	fragmentShader: [
+
+		"uniform sampler2D tDiffuse;",
+		"uniform sampler2D tColor;",
+		"uniform vec2 resolution;",
+
+		"varying vec2 vUv;",
+		"varying vec4 vOffset[ 2 ];",
+
+		"vec4 SMAANeighborhoodBlendingPS( vec2 texcoord, vec4 offset[ 2 ], sampler2D colorTex, sampler2D blendTex ) {",
+			// Fetch the blending weights for current pixel:
+			"vec4 a;",
+			"a.xz = texture2D( blendTex, texcoord ).xz;",
+			"a.y = texture2D( blendTex, offset[ 1 ].zw ).g;",
+			"a.w = texture2D( blendTex, offset[ 1 ].xy ).a;",
+
+			// Is there any blending weight with a value greater than 0.0?
+			"if ( dot(a, vec4( 1.0, 1.0, 1.0, 1.0 )) < 1e-5 ) {",
+				"return texture2D( colorTex, texcoord, 0.0 );",
+			"} else {",
+				// Up to 4 lines can be crossing a pixel (one through each edge). We
+				// favor blending by choosing the line with the maximum weight for each
+				// direction:
+				"vec2 offset;",
+				"offset.x = a.a > a.b ? a.a : -a.b;", // left vs. right
+				"offset.y = a.g > a.r ? -a.g : a.r;", // top vs. bottom // WebGL port note: Changed signs
+
+				// Then we go in the direction that has the maximum weight:
+				"if ( abs( offset.x ) > abs( offset.y )) {", // horizontal vs. vertical
+					"offset.y = 0.0;",
+				"} else {",
+					"offset.x = 0.0;",
+				"}",
+
+				// Fetch the opposite color and lerp by hand:
+				"vec4 C = texture2D( colorTex, texcoord, 0.0 );",
+				"texcoord += sign( offset ) * resolution;",
+				"vec4 Cop = texture2D( colorTex, texcoord, 0.0 );",
+				"float s = abs( offset.x ) > abs( offset.y ) ? abs( offset.x ) : abs( offset.y );",
+
+				// WebGL port note: Added gamma correction
+				"C.xyz = pow(C.xyz, vec3(2.2));",
+				"Cop.xyz = pow(Cop.xyz, vec3(2.2));",
+				"vec4 mixed = mix(C, Cop, s);",
+				"mixed.xyz = pow(mixed.xyz, vec3(1.0 / 2.2));",
+
+				"return mixed;",
+			"}",
+		"}",
+
+		"void main() {",
+
+			"gl_FragColor = SMAANeighborhoodBlendingPS( vUv, vOffset, tColor, tDiffuse );",
+
+		"}"
+
+	].join("\n")
+
+} ];

文件差異過大導致無法顯示
+ 0 - 19
examples/models/animated/horse.js


文件差異過大導致無法顯示
+ 0 - 36
examples/models/skinned/marine/m4.js


+ 125 - 125
examples/obj/camaro/CamaroNoUv_bin.js

@@ -1,125 +1,125 @@
-{
-
-    "metadata" :
-    {
-        "formatVersion" : 3.1,
-        "sourceFile"    : "CamaroNoUv.obj",
-        "generatedBy"   : "OBJConverter",
-        "vertices"      : 53139,
-        "faces"         : 93251,
-        "normals"       : 46141,
-        "uvs"           : 0,
-        "materials"     : 9
-    },
-
-    "materials": [	{
-	"DbgColor" : 15658734,
-	"DbgIndex" : 0,
-	"DbgName" : "Body",
-	"colorDiffuse" : [0.227, 0.408, 0.463],
-	"colorSpecular" : [2.0, 2.0, 2.0],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 782.352941,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 15597568,
-	"DbgIndex" : 1,
-	"DbgName" : "mirror",
-	"colorDiffuse" : [0.3, 0.3, 0.3],
-	"colorSpecular" : [2.0, 2.0, 2.0],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 782.352941,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 60928,
-	"DbgIndex" : 2,
-	"DbgName" : "black",
-	"colorDiffuse" : [0.102, 0.102, 0.102],
-	"colorSpecular" : [0.2, 0.2, 0.2],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 7.843137,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 238,
-	"DbgIndex" : 3,
-	"DbgName" : "mizo",
-	"colorDiffuse" : [0.0, 0.0, 0.0],
-	"colorSpecular" : [0.0, 0.0, 0.0],
-	"illumination" : 1,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 0.0,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 15658496,
-	"DbgIndex" : 4,
-	"DbgName" : "glass",
-	"colorDiffuse" : [0.2, 0.31, 0.306],
-	"colorSpecular" : [2.0, 2.0, 2.0],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 782.352941,
-	"opacity" : 0.34
-	},
-
-	{
-	"DbgColor" : 61166,
-	"DbgIndex" : 5,
-	"DbgName" : "Interieur",
-	"colorDiffuse" : [0.102, 0.102, 0.102],
-	"colorSpecular" : [0.44, 0.44, 0.44],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 15597806,
-	"DbgIndex" : 6,
-	"DbgName" : "tire",
-	"colorDiffuse" : [0.271, 0.271, 0.263],
-	"colorSpecular" : [0.1, 0.1, 0.1],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 17.647059,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 3744854,
-	"DbgIndex" : 7,
-	"DbgName" : "tireling",
-	"colorDiffuse" : [0.5, 0.5, 0.5],
-	"colorSpecular" : [0.2, 0.2, 0.2],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 17.647059,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 4614226,
-	"DbgIndex" : 8,
-	"DbgName" : "black2",
-	"colorDiffuse" : [0.0, 0.0, 0.0],
-	"colorSpecular" : [0.0, 0.0, 0.0],
-	"illumination" : 1,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 0.0,
-	"opacity" : 1.0
-	}],
-
-    "buffers": "CamaroNoUv_bin.bin"
-
-}
+{
+
+    "metadata" :
+    {
+        "formatVersion" : 3.1,
+        "sourceFile"    : "CamaroNoUv.obj",
+        "generatedBy"   : "OBJConverter",
+        "vertices"      : 53139,
+        "faces"         : 93251,
+        "normals"       : 46141,
+        "uvs"           : 0,
+        "materials"     : 9
+    },
+
+    "materials": [	{
+	"DbgColor" : 15658734,
+	"DbgIndex" : 0,
+	"DbgName" : "Body",
+	"colorDiffuse" : [0.227, 0.408, 0.463],
+	"colorSpecular" : [2.0, 2.0, 2.0],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 782.352941,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 15597568,
+	"DbgIndex" : 1,
+	"DbgName" : "mirror",
+	"colorDiffuse" : [0.3, 0.3, 0.3],
+	"colorSpecular" : [2.0, 2.0, 2.0],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 782.352941,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 60928,
+	"DbgIndex" : 2,
+	"DbgName" : "black",
+	"colorDiffuse" : [0.102, 0.102, 0.102],
+	"colorSpecular" : [0.2, 0.2, 0.2],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 7.843137,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 238,
+	"DbgIndex" : 3,
+	"DbgName" : "mizo",
+	"colorDiffuse" : [0.0, 0.0, 0.0],
+	"colorSpecular" : [0.0, 0.0, 0.0],
+	"illumination" : 1,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 0.0,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 15658496,
+	"DbgIndex" : 4,
+	"DbgName" : "glass",
+	"colorDiffuse" : [0.2, 0.31, 0.306],
+	"colorSpecular" : [2.0, 2.0, 2.0],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 782.352941,
+	"opacity" : 0.34
+	},
+
+	{
+	"DbgColor" : 61166,
+	"DbgIndex" : 5,
+	"DbgName" : "Interieur",
+	"colorDiffuse" : [0.102, 0.102, 0.102],
+	"colorSpecular" : [0.44, 0.44, 0.44],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 15597806,
+	"DbgIndex" : 6,
+	"DbgName" : "tire",
+	"colorDiffuse" : [0.271, 0.271, 0.263],
+	"colorSpecular" : [0.1, 0.1, 0.1],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 17.647059,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 3744854,
+	"DbgIndex" : 7,
+	"DbgName" : "tireling",
+	"colorDiffuse" : [0.5, 0.5, 0.5],
+	"colorSpecular" : [0.2, 0.2, 0.2],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 17.647059,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 4614226,
+	"DbgIndex" : 8,
+	"DbgName" : "black2",
+	"colorDiffuse" : [0.0, 0.0, 0.0],
+	"colorSpecular" : [0.0, 0.0, 0.0],
+	"illumination" : 1,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 0.0,
+	"opacity" : 1.0
+	}],
+
+    "buffers": "CamaroNoUv_bin.bin"
+
+}

+ 296 - 296
examples/obj/f50/F50NoUv_bin.js

@@ -1,296 +1,296 @@
-{
-
-    "metadata" :
-    {
-        "formatVersion" : 3.1,
-        "sourceFile"    : "F50NoUv.obj",
-        "generatedBy"   : "OBJConverter",
-        "vertices"      : 67006,
-        "faces"         : 97474,
-        "normals"       : 53988,
-        "uvs"           : 0,
-        "materials"     : 25
-    },
-
-    "materials": [	{
-	"DbgColor" : 15658734,
-	"DbgIndex" : 0,
-	"DbgName" : "F50NEGRO",
-	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 15597568,
-	"DbgIndex" : 1,
-	"DbgName" : "F50CROMO",
-	"colorDiffuse" : [0.1004, 0.10352, 0.06272],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 17.647059
-	},
-
-	{
-	"DbgColor" : 60928,
-	"DbgIndex" : 2,
-	"DbgName" : "F50CRIST",
-	"colorDiffuse" : [0.0408, 0.15688, 0.00312],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216,
-	"opacity" : 0.6
-	},
-
-	{
-	"DbgColor" : 238,
-	"DbgIndex" : 3,
-	"DbgName" : "F50ROJO",
-	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 15658496,
-	"DbgIndex" : 4,
-	"DbgName" : "F50NEGRO1",
-	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 61166,
-	"DbgIndex" : 5,
-	"DbgName" : "F50ALFO",
-	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 15597806,
-	"DbgIndex" : 6,
-	"DbgName" : "F50REJIL",
-	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 13579988,
-	"DbgIndex" : 7,
-	"DbgName" : "F50RIGHT",
-	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 5708456,
-	"DbgIndex" : 8,
-	"DbgName" : "F50CAPO1",
-	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 16326172,
-	"DbgIndex" : 9,
-	"DbgName" : "F50CAPO",
-	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 5647119,
-	"DbgIndex" : 10,
-	"DbgName" : "F50LEFT",
-	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 1556016,
-	"DbgIndex" : 11,
-	"DbgName" : "F50MOTOR",
-	"colorDiffuse" : [0.1004, 0.10352, 0.06272],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 17.647059
-	},
-
-	{
-	"DbgColor" : 2608512,
-	"DbgIndex" : 12,
-	"DbgName" : "F50GOOD",
-	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 17.647059
-	},
-
-	{
-	"DbgColor" : 13541364,
-	"DbgIndex" : 13,
-	"DbgName" : "F50RUEDA",
-	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 17.647059
-	},
-
-	{
-	"DbgColor" : 16059369,
-	"DbgIndex" : 14,
-	"DbgName" : "F50SUELO",
-	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 109031,
-	"DbgIndex" : 15,
-	"DbgName" : "F50DISCO",
-	"colorDiffuse" : [0.41096, 0.3608, 0.25728],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 17.647059
-	},
-
-	{
-	"DbgColor" : 10128841,
-	"DbgIndex" : 16,
-	"DbgName" : "F50FARYE",
-	"colorDiffuse" : [0.79056, 0.41096, 0.00624],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216,
-	"opacity" : 0.4
-	},
-
-	{
-	"DbgColor" : 6350752,
-	"DbgIndex" : 17,
-	"DbgName" : "F5OLOGO",
-	"colorDiffuse" : [0.41096, 0.3608, 0.25728],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 17.647059
-	},
-
-	{
-	"DbgColor" : 9261704,
-	"DbgIndex" : 18,
-	"DbgName" : "F50FARED",
-	"colorDiffuse" : [0.63056, 0.07528, 0.05016],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216,
-	"opacity" : 0.3
-	},
-
-	{
-	"DbgColor" : 1622631,
-	"DbgIndex" : 19,
-	"DbgName" : "F50FARWD",
-	"colorDiffuse" : [0.40472, 0.40472, 0.40472],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216,
-	"opacity" : 0.3
-	},
-
-	{
-	"DbgColor" : 12242867,
-	"DbgIndex" : 20,
-	"DbgName" : "F50ASIEN",
-	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 13519402,
-	"DbgIndex" : 21,
-	"DbgName" : "F50MATR",
-	"colorDiffuse" : [0.5992, 0.58984, 0.56472],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 8996413,
-	"DbgIndex" : 22,
-	"DbgName" : "F50AIRE",
-	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 11712834,
-	"DbgIndex" : 23,
-	"DbgName" : "F50TOPAL",
-	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	},
-
-	{
-	"DbgColor" : 14218595,
-	"DbgIndex" : 24,
-	"DbgName" : "F50TOPAL1",
-	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
-	"colorSpecular" : [0.025, 0.025, 0.025],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 98.039216
-	}],
-
-    "buffers": "F50NoUv_bin.bin"
-
-}
+{
+
+    "metadata" :
+    {
+        "formatVersion" : 3.1,
+        "sourceFile"    : "F50NoUv.obj",
+        "generatedBy"   : "OBJConverter",
+        "vertices"      : 67006,
+        "faces"         : 97474,
+        "normals"       : 53988,
+        "uvs"           : 0,
+        "materials"     : 25
+    },
+
+    "materials": [	{
+	"DbgColor" : 15658734,
+	"DbgIndex" : 0,
+	"DbgName" : "F50NEGRO",
+	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 15597568,
+	"DbgIndex" : 1,
+	"DbgName" : "F50CROMO",
+	"colorDiffuse" : [0.1004, 0.10352, 0.06272],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 17.647059
+	},
+
+	{
+	"DbgColor" : 60928,
+	"DbgIndex" : 2,
+	"DbgName" : "F50CRIST",
+	"colorDiffuse" : [0.0408, 0.15688, 0.00312],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216,
+	"opacity" : 0.6
+	},
+
+	{
+	"DbgColor" : 238,
+	"DbgIndex" : 3,
+	"DbgName" : "F50ROJO",
+	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 15658496,
+	"DbgIndex" : 4,
+	"DbgName" : "F50NEGRO1",
+	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 61166,
+	"DbgIndex" : 5,
+	"DbgName" : "F50ALFO",
+	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 15597806,
+	"DbgIndex" : 6,
+	"DbgName" : "F50REJIL",
+	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 13579988,
+	"DbgIndex" : 7,
+	"DbgName" : "F50RIGHT",
+	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 5708456,
+	"DbgIndex" : 8,
+	"DbgName" : "F50CAPO1",
+	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 16326172,
+	"DbgIndex" : 9,
+	"DbgName" : "F50CAPO",
+	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 5647119,
+	"DbgIndex" : 10,
+	"DbgName" : "F50LEFT",
+	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 1556016,
+	"DbgIndex" : 11,
+	"DbgName" : "F50MOTOR",
+	"colorDiffuse" : [0.1004, 0.10352, 0.06272],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 17.647059
+	},
+
+	{
+	"DbgColor" : 2608512,
+	"DbgIndex" : 12,
+	"DbgName" : "F50GOOD",
+	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 17.647059
+	},
+
+	{
+	"DbgColor" : 13541364,
+	"DbgIndex" : 13,
+	"DbgName" : "F50RUEDA",
+	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 17.647059
+	},
+
+	{
+	"DbgColor" : 16059369,
+	"DbgIndex" : 14,
+	"DbgName" : "F50SUELO",
+	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 109031,
+	"DbgIndex" : 15,
+	"DbgName" : "F50DISCO",
+	"colorDiffuse" : [0.41096, 0.3608, 0.25728],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 17.647059
+	},
+
+	{
+	"DbgColor" : 10128841,
+	"DbgIndex" : 16,
+	"DbgName" : "F50FARYE",
+	"colorDiffuse" : [0.79056, 0.41096, 0.00624],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216,
+	"opacity" : 0.4
+	},
+
+	{
+	"DbgColor" : 6350752,
+	"DbgIndex" : 17,
+	"DbgName" : "F5OLOGO",
+	"colorDiffuse" : [0.41096, 0.3608, 0.25728],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 17.647059
+	},
+
+	{
+	"DbgColor" : 9261704,
+	"DbgIndex" : 18,
+	"DbgName" : "F50FARED",
+	"colorDiffuse" : [0.63056, 0.07528, 0.05016],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216,
+	"opacity" : 0.3
+	},
+
+	{
+	"DbgColor" : 1622631,
+	"DbgIndex" : 19,
+	"DbgName" : "F50FARWD",
+	"colorDiffuse" : [0.40472, 0.40472, 0.40472],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216,
+	"opacity" : 0.3
+	},
+
+	{
+	"DbgColor" : 12242867,
+	"DbgIndex" : 20,
+	"DbgName" : "F50ASIEN",
+	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 13519402,
+	"DbgIndex" : 21,
+	"DbgName" : "F50MATR",
+	"colorDiffuse" : [0.5992, 0.58984, 0.56472],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 8996413,
+	"DbgIndex" : 22,
+	"DbgName" : "F50AIRE",
+	"colorDiffuse" : [0.15376, 0.10984, 0.03768],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 11712834,
+	"DbgIndex" : 23,
+	"DbgName" : "F50TOPAL",
+	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	},
+
+	{
+	"DbgColor" : 14218595,
+	"DbgIndex" : 24,
+	"DbgName" : "F50TOPAL1",
+	"colorDiffuse" : [0.5616, 0.04392, 0.06904],
+	"colorSpecular" : [0.025, 0.025, 0.025],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 98.039216
+	}],
+
+    "buffers": "F50NoUv_bin.bin"
+
+}

+ 95 - 95
examples/obj/female02/Female02_bin.js

@@ -1,95 +1,95 @@
-{
-
-    "metadata" :
-    {
-        "formatVersion" : 3.1,
-        "sourceFile"    : "female02.obj",
-        "generatedBy"   : "OBJConverter",
-        "vertices"      : 3274,
-        "faces"         : 6233,
-        "normals"       : 3292,
-        "uvs"           : 4935,
-        "materials"     : 6
-    },
-
-    "materials": [	{
-	"DbgColor" : 15658734,
-	"DbgIndex" : 0,
-	"DbgName" : "_03_-_Default1noCulli__03_-_Default1noCulli",
-	"colorDiffuse" : [0.64, 0.64, 0.64],
-	"colorSpecular" : [0.165, 0.165, 0.165],
-	"illumination" : 2,
-	"mapDiffuse" : "03_-_Default1noCulling.JPG",
-	"opticalDensity" : 1.0,
-	"specularCoef" : 154.901961,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 15597568,
-	"DbgIndex" : 1,
-	"DbgName" : "_02_-_Default1noCulli__02_-_Default1noCulli",
-	"colorDiffuse" : [0.64, 0.64, 0.64],
-	"colorSpecular" : [0.165, 0.165, 0.165],
-	"illumination" : 2,
-	"mapDiffuse" : "02_-_Default1noCulling.JPG",
-	"opticalDensity" : 1.0,
-	"specularCoef" : 154.901961,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 60928,
-	"DbgIndex" : 2,
-	"DbgName" : "FrontColorNoCullingID__02_-_Default1noCulli",
-	"colorDiffuse" : [0.8, 0.8, 0.8],
-	"colorSpecular" : [0.165, 0.165, 0.165],
-	"illumination" : 2,
-	"mapDiffuse" : "02_-_Default1noCulling.JPG",
-	"opticalDensity" : 1.0,
-	"specularCoef" : 154.901961,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 238,
-	"DbgIndex" : 3,
-	"DbgName" : "FrontColorNoCullingID__03_-_Default1noCulli",
-	"colorDiffuse" : [0.8, 0.8, 0.8],
-	"colorSpecular" : [0.165, 0.165, 0.165],
-	"illumination" : 2,
-	"mapDiffuse" : "03_-_Default1noCulling.JPG",
-	"opticalDensity" : 1.0,
-	"specularCoef" : 154.901961,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 15658496,
-	"DbgIndex" : 4,
-	"DbgName" : "_01_-_Default1noCulli__01_-_Default1noCulli",
-	"colorDiffuse" : [0.64, 0.64, 0.64],
-	"colorSpecular" : [0.165, 0.165, 0.165],
-	"illumination" : 2,
-	"mapDiffuse" : "01_-_Default1noCulling.JPG",
-	"opticalDensity" : 1.0,
-	"specularCoef" : 154.901961,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 61166,
-	"DbgIndex" : 5,
-	"DbgName" : "FrontColorNoCullingID__01_-_Default1noCulli",
-	"colorDiffuse" : [0.8, 0.8, 0.8],
-	"colorSpecular" : [0.165, 0.165, 0.165],
-	"illumination" : 2,
-	"mapDiffuse" : "01_-_Default1noCulling.JPG",
-	"opticalDensity" : 1.0,
-	"specularCoef" : 154.901961,
-	"opacity" : 1.0
-	}],
-
-    "buffers": "Female02_bin.bin"
-
-}
+{
+
+    "metadata" :
+    {
+        "formatVersion" : 3.1,
+        "sourceFile"    : "female02.obj",
+        "generatedBy"   : "OBJConverter",
+        "vertices"      : 3274,
+        "faces"         : 6233,
+        "normals"       : 3292,
+        "uvs"           : 4935,
+        "materials"     : 6
+    },
+
+    "materials": [	{
+	"DbgColor" : 15658734,
+	"DbgIndex" : 0,
+	"DbgName" : "_03_-_Default1noCulli__03_-_Default1noCulli",
+	"colorDiffuse" : [0.64, 0.64, 0.64],
+	"colorSpecular" : [0.165, 0.165, 0.165],
+	"illumination" : 2,
+	"mapDiffuse" : "03_-_Default1noCulling.JPG",
+	"opticalDensity" : 1.0,
+	"specularCoef" : 154.901961,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 15597568,
+	"DbgIndex" : 1,
+	"DbgName" : "_02_-_Default1noCulli__02_-_Default1noCulli",
+	"colorDiffuse" : [0.64, 0.64, 0.64],
+	"colorSpecular" : [0.165, 0.165, 0.165],
+	"illumination" : 2,
+	"mapDiffuse" : "02_-_Default1noCulling.JPG",
+	"opticalDensity" : 1.0,
+	"specularCoef" : 154.901961,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 60928,
+	"DbgIndex" : 2,
+	"DbgName" : "FrontColorNoCullingID__02_-_Default1noCulli",
+	"colorDiffuse" : [0.8, 0.8, 0.8],
+	"colorSpecular" : [0.165, 0.165, 0.165],
+	"illumination" : 2,
+	"mapDiffuse" : "02_-_Default1noCulling.JPG",
+	"opticalDensity" : 1.0,
+	"specularCoef" : 154.901961,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 238,
+	"DbgIndex" : 3,
+	"DbgName" : "FrontColorNoCullingID__03_-_Default1noCulli",
+	"colorDiffuse" : [0.8, 0.8, 0.8],
+	"colorSpecular" : [0.165, 0.165, 0.165],
+	"illumination" : 2,
+	"mapDiffuse" : "03_-_Default1noCulling.JPG",
+	"opticalDensity" : 1.0,
+	"specularCoef" : 154.901961,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 15658496,
+	"DbgIndex" : 4,
+	"DbgName" : "_01_-_Default1noCulli__01_-_Default1noCulli",
+	"colorDiffuse" : [0.64, 0.64, 0.64],
+	"colorSpecular" : [0.165, 0.165, 0.165],
+	"illumination" : 2,
+	"mapDiffuse" : "01_-_Default1noCulling.JPG",
+	"opticalDensity" : 1.0,
+	"specularCoef" : 154.901961,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 61166,
+	"DbgIndex" : 5,
+	"DbgName" : "FrontColorNoCullingID__01_-_Default1noCulli",
+	"colorDiffuse" : [0.8, 0.8, 0.8],
+	"colorSpecular" : [0.165, 0.165, 0.165],
+	"illumination" : 2,
+	"mapDiffuse" : "01_-_Default1noCulling.JPG",
+	"opticalDensity" : 1.0,
+	"specularCoef" : 154.901961,
+	"opacity" : 1.0
+	}],
+
+    "buffers": "Female02_bin.bin"
+
+}

文件差異過大導致無法顯示
+ 0 - 95
examples/obj/female02/Female02_slim.js


+ 94 - 94
examples/obj/gallardo/GallardoNoUv_bin.js

@@ -1,94 +1,94 @@
-{
-
-    "metadata" :
-    {
-        "formatVersion" : 3.1,
-        "sourceFile"    : "GallardoNoUv.obj",
-        "generatedBy"   : "OBJConverter",
-        "vertices"      : 77815,
-        "faces"         : 75573,
-        "normals"       : 55191,
-        "uvs"           : 0,
-        "materials"     : 7
-    },
-
-    "materials": [	{
-	"DbgColor" : 15658734,
-	"DbgIndex" : 0,
-	"DbgName" : "wire_255255255",
-	"colorDiffuse" : [0.8, 0.8, 0.8],
-	"colorSpecular" : [0.175, 0.175, 0.175],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 29.411765
-	},
-
-	{
-	"DbgColor" : 15597568,
-	"DbgIndex" : 1,
-	"DbgName" : "wire_115115115",
-	"colorDiffuse" : [0.3608, 0.3608, 0.3608],
-	"colorSpecular" : [0.175, 0.175, 0.175],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 29.411765
-	},
-
-	{
-	"DbgColor" : 60928,
-	"DbgIndex" : 2,
-	"DbgName" : "03___Default",
-	"colorDiffuse" : [0.13176, 0.13176, 0.13176],
-	"colorSpecular" : [0.0, 0.0, 0.0],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 7.843137
-	},
-
-	{
-	"DbgColor" : 238,
-	"DbgIndex" : 3,
-	"DbgName" : "02___Default",
-	"colorDiffuse" : [0.8, 0.0, 0.0],
-	"colorSpecular" : [0.6525, 0.6525, 0.6525],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 25.490196
-	},
-
-	{
-	"DbgColor" : 15658496,
-	"DbgIndex" : 4,
-	"DbgName" : "wire_255000000",
-	"colorDiffuse" : [0.8, 0.0, 0.0],
-	"colorSpecular" : [0.175, 0.175, 0.175],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 29.411765
-	},
-
-	{
-	"DbgColor" : 61166,
-	"DbgIndex" : 5,
-	"DbgName" : "wire_252252000",
-	"colorDiffuse" : [0.79056, 0.79056, 0.0],
-	"colorSpecular" : [0.175, 0.175, 0.175],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 29.411765
-	},
-
-	{
-	"DbgColor" : 15597806,
-	"DbgIndex" : 6,
-	"DbgName" : "wire_132132132",
-	"colorDiffuse" : [0.41408, 0.41408, 0.41408],
-	"colorSpecular" : [0.175, 0.175, 0.175],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 29.411765
-	}],
-
-    "buffers": "GallardoNoUv_bin.bin"
-
-}
+{
+
+    "metadata" :
+    {
+        "formatVersion" : 3.1,
+        "sourceFile"    : "GallardoNoUv.obj",
+        "generatedBy"   : "OBJConverter",
+        "vertices"      : 77815,
+        "faces"         : 75573,
+        "normals"       : 55191,
+        "uvs"           : 0,
+        "materials"     : 7
+    },
+
+    "materials": [	{
+	"DbgColor" : 15658734,
+	"DbgIndex" : 0,
+	"DbgName" : "wire_255255255",
+	"colorDiffuse" : [0.8, 0.8, 0.8],
+	"colorSpecular" : [0.175, 0.175, 0.175],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 29.411765
+	},
+
+	{
+	"DbgColor" : 15597568,
+	"DbgIndex" : 1,
+	"DbgName" : "wire_115115115",
+	"colorDiffuse" : [0.3608, 0.3608, 0.3608],
+	"colorSpecular" : [0.175, 0.175, 0.175],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 29.411765
+	},
+
+	{
+	"DbgColor" : 60928,
+	"DbgIndex" : 2,
+	"DbgName" : "03___Default",
+	"colorDiffuse" : [0.13176, 0.13176, 0.13176],
+	"colorSpecular" : [0.0, 0.0, 0.0],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 7.843137
+	},
+
+	{
+	"DbgColor" : 238,
+	"DbgIndex" : 3,
+	"DbgName" : "02___Default",
+	"colorDiffuse" : [0.8, 0.0, 0.0],
+	"colorSpecular" : [0.6525, 0.6525, 0.6525],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 25.490196
+	},
+
+	{
+	"DbgColor" : 15658496,
+	"DbgIndex" : 4,
+	"DbgName" : "wire_255000000",
+	"colorDiffuse" : [0.8, 0.0, 0.0],
+	"colorSpecular" : [0.175, 0.175, 0.175],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 29.411765
+	},
+
+	{
+	"DbgColor" : 61166,
+	"DbgIndex" : 5,
+	"DbgName" : "wire_252252000",
+	"colorDiffuse" : [0.79056, 0.79056, 0.0],
+	"colorSpecular" : [0.175, 0.175, 0.175],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 29.411765
+	},
+
+	{
+	"DbgColor" : 15597806,
+	"DbgIndex" : 6,
+	"DbgName" : "wire_132132132",
+	"colorDiffuse" : [0.41408, 0.41408, 0.41408],
+	"colorSpecular" : [0.175, 0.175, 0.175],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 29.411765
+	}],
+
+    "buffers": "GallardoNoUv_bin.bin"
+
+}

+ 82 - 82
examples/obj/male02/Male02_bin.js

@@ -1,82 +1,82 @@
-{
-
-    "metadata" :
-    {
-        "formatVersion" : 3.1,
-        "sourceFile"    : "male02.obj",
-        "generatedBy"   : "OBJConverter",
-        "vertices"      : 2746,
-        "faces"         : 5004,
-        "normals"       : 2769,
-        "uvs"           : 3275,
-        "materials"     : 5
-    },
-
-    "materials": [	{
-	"DbgColor" : 15658734,
-	"DbgIndex" : 0,
-	"DbgName" : "male-02-1noCullingID_male-02-1noCulling.JP",
-	"colorDiffuse" : [0.64, 0.64, 0.64],
-	"colorSpecular" : [0.165, 0.165, 0.165],
-	"illumination" : 2,
-	"mapDiffuse" : "male-02-1noCulling.JPG",
-	"opticalDensity" : 1.0,
-	"specularCoef" : 154.901961,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 15597568,
-	"DbgIndex" : 1,
-	"DbgName" : "orig_02_-_Defaul1noCu_orig_02_-_Defaul1noCu",
-	"colorDiffuse" : [0.64, 0.64, 0.64],
-	"colorSpecular" : [0.165, 0.165, 0.165],
-	"illumination" : 2,
-	"mapDiffuse" : "orig_02_-_Defaul1noCulling.JPG",
-	"opticalDensity" : 1.0,
-	"specularCoef" : 154.901961,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 60928,
-	"DbgIndex" : 2,
-	"DbgName" : "FrontColorNoCullingID_orig_02_-_Defaul1noCu",
-	"colorDiffuse" : [0.8, 0.8, 0.8],
-	"colorSpecular" : [0.165, 0.165, 0.165],
-	"illumination" : 2,
-	"mapDiffuse" : "orig_02_-_Defaul1noCulling.JPG",
-	"opticalDensity" : 1.0,
-	"specularCoef" : 154.901961,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 238,
-	"DbgIndex" : 3,
-	"DbgName" : "_01_-_Default1noCulli__01_-_Default1noCulli",
-	"colorDiffuse" : [0.64, 0.64, 0.64],
-	"colorSpecular" : [0.165, 0.165, 0.165],
-	"illumination" : 2,
-	"mapDiffuse" : "01_-_Default1noCulling.JPG",
-	"opticalDensity" : 1.0,
-	"specularCoef" : 154.901961,
-	"opacity" : 1.0
-	},
-
-	{
-	"DbgColor" : 15658496,
-	"DbgIndex" : 4,
-	"DbgName" : "FrontColorNoCullingID_male-02-1noCulling.JP",
-	"colorDiffuse" : [0.8, 0.8, 0.8],
-	"colorSpecular" : [0.165, 0.165, 0.165],
-	"illumination" : 2,
-	"mapDiffuse" : "male-02-1noCulling.JPG",
-	"opticalDensity" : 1.0,
-	"specularCoef" : 154.901961,
-	"opacity" : 1.0
-	}],
-
-    "buffers": "Male02_bin.bin"
-
-}
+{
+
+    "metadata" :
+    {
+        "formatVersion" : 3.1,
+        "sourceFile"    : "male02.obj",
+        "generatedBy"   : "OBJConverter",
+        "vertices"      : 2746,
+        "faces"         : 5004,
+        "normals"       : 2769,
+        "uvs"           : 3275,
+        "materials"     : 5
+    },
+
+    "materials": [	{
+	"DbgColor" : 15658734,
+	"DbgIndex" : 0,
+	"DbgName" : "male-02-1noCullingID_male-02-1noCulling.JP",
+	"colorDiffuse" : [0.64, 0.64, 0.64],
+	"colorSpecular" : [0.165, 0.165, 0.165],
+	"illumination" : 2,
+	"mapDiffuse" : "male-02-1noCulling.JPG",
+	"opticalDensity" : 1.0,
+	"specularCoef" : 154.901961,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 15597568,
+	"DbgIndex" : 1,
+	"DbgName" : "orig_02_-_Defaul1noCu_orig_02_-_Defaul1noCu",
+	"colorDiffuse" : [0.64, 0.64, 0.64],
+	"colorSpecular" : [0.165, 0.165, 0.165],
+	"illumination" : 2,
+	"mapDiffuse" : "orig_02_-_Defaul1noCulling.JPG",
+	"opticalDensity" : 1.0,
+	"specularCoef" : 154.901961,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 60928,
+	"DbgIndex" : 2,
+	"DbgName" : "FrontColorNoCullingID_orig_02_-_Defaul1noCu",
+	"colorDiffuse" : [0.8, 0.8, 0.8],
+	"colorSpecular" : [0.165, 0.165, 0.165],
+	"illumination" : 2,
+	"mapDiffuse" : "orig_02_-_Defaul1noCulling.JPG",
+	"opticalDensity" : 1.0,
+	"specularCoef" : 154.901961,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 238,
+	"DbgIndex" : 3,
+	"DbgName" : "_01_-_Default1noCulli__01_-_Default1noCulli",
+	"colorDiffuse" : [0.64, 0.64, 0.64],
+	"colorSpecular" : [0.165, 0.165, 0.165],
+	"illumination" : 2,
+	"mapDiffuse" : "01_-_Default1noCulling.JPG",
+	"opticalDensity" : 1.0,
+	"specularCoef" : 154.901961,
+	"opacity" : 1.0
+	},
+
+	{
+	"DbgColor" : 15658496,
+	"DbgIndex" : 4,
+	"DbgName" : "FrontColorNoCullingID_male-02-1noCulling.JP",
+	"colorDiffuse" : [0.8, 0.8, 0.8],
+	"colorSpecular" : [0.165, 0.165, 0.165],
+	"illumination" : 2,
+	"mapDiffuse" : "male-02-1noCulling.JPG",
+	"opticalDensity" : 1.0,
+	"specularCoef" : 154.901961,
+	"opacity" : 1.0
+	}],
+
+    "buffers": "Male02_bin.bin"
+
+}

文件差異過大導致無法顯示
+ 0 - 82
examples/obj/male02/Male02_slim.js


+ 108 - 108
examples/obj/veyron/VeyronNoUv_bin.js

@@ -1,108 +1,108 @@
-{
-
-    "metadata" :
-    {
-        "formatVersion" : 3.1,
-        "sourceFile"    : "VeyronNoUv.obj",
-        "generatedBy"   : "OBJConverter",
-        "vertices"      : 36210,
-        "faces"         : 33034,
-        "normals"       : 31827,
-        "uvs"           : 0,
-        "materials"     : 8
-    },
-
-    "materials": [	{
-	"DbgColor" : 15658734,
-	"DbgIndex" : 0,
-	"DbgName" : "08___Default",
-	"colorDiffuse" : [0.18824, 0.18824, 0.18824],
-	"colorSpecular" : [0.14825, 0.14825, 0.14825],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 17.647059
-	},
-
-	{
-	"DbgColor" : 15597568,
-	"DbgIndex" : 1,
-	"DbgName" : "03___Default",
-	"colorDiffuse" : [0.52704, 0.52704, 0.52704],
-	"colorSpecular" : [0.6, 0.6, 0.6],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 37.254902
-	},
-
-	{
-	"DbgColor" : 60928,
-	"DbgIndex" : 2,
-	"DbgName" : "ColorBlack",
-	"colorDiffuse" : [0.21648, 0.21648, 0.21648],
-	"colorSpecular" : [0.75, 0.75, 0.75],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 56.862745
-	},
-
-	{
-	"DbgColor" : 238,
-	"DbgIndex" : 3,
-	"DbgName" : "04___Default",
-	"colorDiffuse" : [0.54272, 0.57408, 0.58984],
-	"colorSpecular" : [0.75, 0.75, 0.75],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 66.666667,
-	"opacity" : 0.5
-	},
-
-	{
-	"DbgColor" : 15658496,
-	"DbgIndex" : 4,
-	"DbgName" : "02___Default",
-	"colorDiffuse" : [0.48312, 0.0, 0.0],
-	"colorSpecular" : [0.75, 0.75, 0.75],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 56.862745
-	},
-
-	{
-	"DbgColor" : 61166,
-	"DbgIndex" : 5,
-	"DbgName" : "Aluminium",
-	"colorDiffuse" : [0.47056, 0.47056, 0.47056],
-	"colorSpecular" : [0.4, 0.4, 0.4],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 17.647059
-	},
-
-	{
-	"DbgColor" : 15597806,
-	"DbgIndex" : 6,
-	"DbgName" : "glassRed",
-	"colorDiffuse" : [0.57408, 0.0, 0.0],
-	"colorSpecular" : [0.75, 0.75, 0.75],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 66.666667,
-	"opacity" : 0.5
-	},
-
-	{
-	"DbgColor" : 419610,
-	"DbgIndex" : 7,
-	"DbgName" : "glassOranje",
-	"colorDiffuse" : [0.8, 0.49568, 0.1004],
-	"colorSpecular" : [0.75, 0.75, 0.75],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 66.666667,
-	"opacity" : 0.5
-	}],
-
-    "buffers": "VeyronNoUv_bin.bin"
-
-}
+{
+
+    "metadata" :
+    {
+        "formatVersion" : 3.1,
+        "sourceFile"    : "VeyronNoUv.obj",
+        "generatedBy"   : "OBJConverter",
+        "vertices"      : 36210,
+        "faces"         : 33034,
+        "normals"       : 31827,
+        "uvs"           : 0,
+        "materials"     : 8
+    },
+
+    "materials": [	{
+	"DbgColor" : 15658734,
+	"DbgIndex" : 0,
+	"DbgName" : "08___Default",
+	"colorDiffuse" : [0.18824, 0.18824, 0.18824],
+	"colorSpecular" : [0.14825, 0.14825, 0.14825],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 17.647059
+	},
+
+	{
+	"DbgColor" : 15597568,
+	"DbgIndex" : 1,
+	"DbgName" : "03___Default",
+	"colorDiffuse" : [0.52704, 0.52704, 0.52704],
+	"colorSpecular" : [0.6, 0.6, 0.6],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 37.254902
+	},
+
+	{
+	"DbgColor" : 60928,
+	"DbgIndex" : 2,
+	"DbgName" : "ColorBlack",
+	"colorDiffuse" : [0.21648, 0.21648, 0.21648],
+	"colorSpecular" : [0.75, 0.75, 0.75],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 56.862745
+	},
+
+	{
+	"DbgColor" : 238,
+	"DbgIndex" : 3,
+	"DbgName" : "04___Default",
+	"colorDiffuse" : [0.54272, 0.57408, 0.58984],
+	"colorSpecular" : [0.75, 0.75, 0.75],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 66.666667,
+	"opacity" : 0.5
+	},
+
+	{
+	"DbgColor" : 15658496,
+	"DbgIndex" : 4,
+	"DbgName" : "02___Default",
+	"colorDiffuse" : [0.48312, 0.0, 0.0],
+	"colorSpecular" : [0.75, 0.75, 0.75],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 56.862745
+	},
+
+	{
+	"DbgColor" : 61166,
+	"DbgIndex" : 5,
+	"DbgName" : "Aluminium",
+	"colorDiffuse" : [0.47056, 0.47056, 0.47056],
+	"colorSpecular" : [0.4, 0.4, 0.4],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 17.647059
+	},
+
+	{
+	"DbgColor" : 15597806,
+	"DbgIndex" : 6,
+	"DbgName" : "glassRed",
+	"colorDiffuse" : [0.57408, 0.0, 0.0],
+	"colorSpecular" : [0.75, 0.75, 0.75],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 66.666667,
+	"opacity" : 0.5
+	},
+
+	{
+	"DbgColor" : 419610,
+	"DbgIndex" : 7,
+	"DbgName" : "glassOranje",
+	"colorDiffuse" : [0.8, 0.49568, 0.1004],
+	"colorSpecular" : [0.75, 0.75, 0.75],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 66.666667,
+	"opacity" : 0.5
+	}],
+
+    "buffers": "VeyronNoUv_bin.bin"
+
+}

+ 29 - 29
examples/obj/walt/WaltHead_bin.js

@@ -1,29 +1,29 @@
-{
-
-    "metadata" :
-    {
-        "formatVersion" : 3.1,
-        "sourceFile"    : "WaltHead.obj",
-        "generatedBy"   : "OBJConverter",
-        "vertices"      : 8146,
-        "faces"         : 16160,
-        "normals"       : 8146,
-        "uvs"           : 0,
-        "materials"     : 1
-    },
-
-    "materials": [	{
-	"DbgColor" : 15658734,
-	"DbgIndex" : 0,
-	"DbgName" : "lambert2SG.001",
-	"colorDiffuse" : [0.64, 0.64, 0.64],
-	"colorSpecular" : [0.25, 0.25, 0.25],
-	"illumination" : 2,
-	"opticalDensity" : 1.0,
-	"specularCoef" : 92.156863,
-	"opacity" : 1.0
-	}],
-
-    "buffers": "WaltHead_bin.bin"
-
-}
+{
+
+    "metadata" :
+    {
+        "formatVersion" : 3.1,
+        "sourceFile"    : "WaltHead.obj",
+        "generatedBy"   : "OBJConverter",
+        "vertices"      : 8146,
+        "faces"         : 16160,
+        "normals"       : 8146,
+        "uvs"           : 0,
+        "materials"     : 1
+    },
+
+    "materials": [	{
+	"DbgColor" : 15658734,
+	"DbgIndex" : 0,
+	"DbgName" : "lambert2SG.001",
+	"colorDiffuse" : [0.64, 0.64, 0.64],
+	"colorSpecular" : [0.25, 0.25, 0.25],
+	"illumination" : 2,
+	"opticalDensity" : 1.0,
+	"specularCoef" : 92.156863,
+	"opacity" : 1.0
+	}],
+
+    "buffers": "WaltHead_bin.bin"
+
+}

文件差異過大導致無法顯示
+ 0 - 29
examples/obj/walt/WaltHead_slim.js


+ 1 - 1
test/unit/editor/Sidebar.Geometry.LatheGeometry.tests.js

@@ -1,4 +1,4 @@
-/**
+/**
  * @author TristanVALCKE / https://github.com/Itee
  */
 /* global QUnit */

+ 456 - 456
test/unit/src/animation/AnimationAction.tests.js

@@ -1,456 +1,456 @@
-/**
- * @author TristanVALCKE / https://github.com/Itee
- */
-/* global QUnit */
-
-import { AnimationAction } from '../../../../src/animation/AnimationAction';
-import { AnimationMixer } from '../../../../src/animation/AnimationMixer';
-import { AnimationClip } from '../../../../src/animation/AnimationClip';
-import { NumberKeyframeTrack } from '../../../../src/animation/tracks/NumberKeyframeTrack';
-import { Object3D } from '../../../../src/core/Object3D';
-import { LoopOnce, LoopRepeat, LoopPingPong } from '../../../../src/constants';
-
-
-function createAnimation(){
-
-	var root = new Object3D();
-	var mixer = new AnimationMixer(root);
-	var track = new NumberKeyframeTrack( ".rotation[x]", [ 0, 1000 ], [ 0, 360 ] );
-	var clip = new AnimationClip( "clip1", 1000, [track] );
-
-	var animationAction = mixer.clipAction( clip );
-	return {
-		root: root,
-		mixer: mixer,
-		track: track,
-		clip: clip,
-		animationAction: animationAction
-	};
-
-}
-
-function createTwoAnimations(){
-
-	var root = new Object3D();
-	var mixer = new AnimationMixer(root);
-	var track = new NumberKeyframeTrack( ".rotation[x]", [ 0, 1000 ], [ 0, 360 ] );
-	var clip = new AnimationClip( "clip1", 1000, [track] );
-	var animationAction = mixer.clipAction( clip );
-
-	var track2 = new NumberKeyframeTrack( ".rotation[y]", [ 0, 1000 ], [ 0, 360 ] );
-	var clip2 = new AnimationClip( "clip2", 1000, [track] );
-	var animationAction2 = mixer.clipAction( clip2 );
-
-	return {
-		root: root,
-		mixer: mixer,
-		track: track,
-		clip: clip,
-		animationAction: animationAction,
-		track2: track2,
-		clip2: clip2,
-		animationAction2: animationAction2
-	};
-
-}
-
-
-export default QUnit.module( 'Animation', () => {
-
-	QUnit.module( 'AnimationAction', () => {
-
-		// INSTANCING
-		QUnit.test( "Instancing", ( assert ) => {
-
-			var mixer = new AnimationMixer();
-			var clip = new AnimationClip( "nonname", - 1, [] );
-
-			var animationAction = new AnimationAction( mixer, clip );
-			assert.ok( animationAction, "animationAction instanciated" );
-
-		} );
-
-		// PUBLIC STUFF
-		QUnit.test( "play", ( assert ) => {
-
-			var {mixer,animationAction} = createAnimation();
-			var animationAction2 = animationAction.play();
-			assert.equal( animationAction, animationAction2, "AnimationAction.play can be chained." );
-
-			var UserException = function () {
-
-				this.message = "AnimationMixer must activate AnimationAction on play.";
-
-			};
-			mixer._activateAction = function ( action ) {
-
-				if ( action === animationAction ) {
-
-					throw new UserException();
-
-				}
-
-			};
-			assert.throws( () => {
-
-				animationAction.play();
-
-			}, new UserException() );
-
-		} );
-
-		QUnit.test( "stop", ( assert ) => {
-
-			var {mixer,animationAction} = createAnimation();
-			var animationAction2 = animationAction.stop();
-			assert.equal( animationAction, animationAction2, "AnimationAction.stop can be chained." );
-
-			var UserException = function () {
-
-				this.message = "AnimationMixer must deactivate AnimationAction on stop.";
-
-			};
-			mixer._deactivateAction = function ( action ) {
-
-				if ( action === animationAction ) {
-
-					throw new UserException();
-
-				}
-
-			};
-			assert.throws( () => {
-
-				animationAction.stop();
-
-			}, new UserException() );
-
-		} );
-
-		QUnit.test( "reset", ( assert ) => {
-
-			var {mixer,animationAction} = createAnimation();
-			var animationAction2 = animationAction.stop();
-			assert.equal( animationAction, animationAction2, "AnimationAction.reset can be chained." );
-			assert.equal( animationAction2.paused, false, "AnimationAction.reset() sets paused false" );
-			assert.equal( animationAction2.enabled, true, "AnimationAction.reset() sets enabled true" );
-			assert.equal( animationAction2.time, 0, "AnimationAction.reset() resets time." );
-			assert.equal( animationAction2._loopCount, - 1, "AnimationAction.reset() resets loopcount." );
-			assert.equal( animationAction2._startTime, null, "AnimationAction.reset() removes starttime." );
-
-		} );
-
-		QUnit.test( "isRunning", ( assert ) => {
-
-			var {mixer,animationAction} = createAnimation();
-			assert.notOk( animationAction.isRunning(), "When an animation is just made, it is not running." );
-			animationAction.play();
-			assert.ok( animationAction.isRunning(), "When an animation is started, it is running." );
-			animationAction.stop();
-			assert.notOk( animationAction.isRunning(), "When an animation is stopped, it is not running." );
-			animationAction.play();
-			animationAction.paused = true;
-			assert.notOk( animationAction.isRunning(), "When an animation is paused, it is not running." );
-			animationAction.paused = false;
-			animationAction.enabled = false;
-			assert.notOk( animationAction.isRunning(), "When an animation is not enabled, it is not running." );
-			animationAction.enabled = true;
-			assert.ok( animationAction.isRunning(), "When an animation is enabled, it is running." );
-
-		} );
-
-		QUnit.test( "isScheduled", ( assert ) => {
-
-			var {mixer,animationAction} = createAnimation();
-			assert.notOk( animationAction.isScheduled(), "When an animation is just made, it is not scheduled." );
-			animationAction.play();
-			assert.ok( animationAction.isScheduled(), "When an animation is started, it is scheduled." );
-			mixer.update(1);
-			assert.ok( animationAction.isScheduled(), "When an animation is updated, it is scheduled." );
-			animationAction.stop();
-			assert.notOk( animationAction.isScheduled(), "When an animation is stopped, it isn't scheduled anymore." );
-
-		   
-		} );
-
-		QUnit.test( "startAt", ( assert ) => {
-
-			var {mixer,animationAction} = createAnimation();
-			animationAction.startAt(2);
-			animationAction.play();
-			assert.notOk( animationAction.isRunning(), "When an animation is started at a specific time, it is not running." );
-			assert.ok( animationAction.isScheduled(), "When an animation is started at a specific time, it is scheduled." );
-			mixer.update(1);
-			assert.notOk( animationAction.isRunning(), "When an animation is started at a specific time and the interval is not passed, it is not running." );
-			assert.ok( animationAction.isScheduled(), "When an animation is started at a specific time and the interval is not passed, it is scheduled." );
-			mixer.update(1);
-			assert.ok( animationAction.isRunning(), "When an animation is started at a specific time and the interval is passed, it is running." );
-			assert.ok( animationAction.isScheduled(), "When an animation is started at a specific time and the interval is passed, it is scheduled." );
-			animationAction.stop();
-			assert.notOk( animationAction.isRunning(), "When an animation is stopped, it is not running." );
-			assert.notOk( animationAction.isScheduled(), "When an animation is stopped, it is not scheduled." );
-			
-
-		} );
-
-		QUnit.test( "setLoop LoopOnce", ( assert ) => {
-
-			var {mixer,animationAction} = createAnimation();
-			animationAction.setLoop(LoopOnce);
-			animationAction.play();
-			assert.ok( animationAction.isRunning(), "When an animation is started, it is running." );
-			mixer.update(500);
-			assert.ok( animationAction.isRunning(), "When an animation is in the first loop, it is running." );
-			mixer.update(500);
-			assert.notOk( animationAction.isRunning(), "When an animation is ended, it is not running." );
-			mixer.update(500);
-			assert.notOk( animationAction.isRunning(), "When an animation is ended, it is not running." );
-	
-		} );
-	
-		QUnit.test( "setLoop LoopRepeat", ( assert ) => {
-	
-			var { root, mixer,animationAction } = createAnimation();
-			animationAction.setLoop(LoopRepeat,3);
-			animationAction.play();
-			assert.ok( animationAction.isRunning(), "When an animation is started, it is running." );
-			mixer.update(750);
-			assert.equal( root.rotation.x, 270 , "When an animation is 3/4 in the first loop, it has changed to 3/4 when LoopRepeat." );
-			assert.ok( animationAction.isRunning(), "When an animation is in the first loop, it is running." );
-			mixer.update(1000);
-			assert.equal( root.rotation.x, 270 , "When an animation is 3/4 in the second loop, it has changed to 3/4 when LoopRepeat." );
-			assert.ok( animationAction.isRunning(), "When an animation is in second loop when in looprepeat 3 times, it is running." );
-			mixer.update(1000);
-			assert.equal( root.rotation.x, 270 , "When an animation is 3/4 in the third loop, it has changed to 3/4 when LoopRepeat." );
-			assert.ok( animationAction.isRunning(), "When an animation is in third loop when in looprepeat 3 times, it is running." );
-			mixer.update(1000);
-			assert.equal( root.rotation.x, 270 , "When an animation is 3/4 in the fourth loop, it has changed to 3/4 when LoopRepeat." );
-			assert.ok( animationAction.isRunning(), "When an animation is in fourth loop when in looprepeat 3 times, it is running." );
-			mixer.update(1000);
-			assert.equal( root.rotation.x, 0 , "When an animation ended his third loop when in looprepeat 3 times, it stays on the end result." );
-			assert.notOk( animationAction.isRunning(), "When an animation ended his third loop when in looprepeat 3 times, it stays not running anymore." );
-			
-		} );
-
-		QUnit.test( "setLoop LoopPingPong", ( assert ) => {
-	
-			var {root, mixer,animationAction} = createAnimation();
-			animationAction.setLoop(LoopPingPong,3);
-			animationAction.play();
-			assert.ok( animationAction.isRunning(), "When an animation is started, it is running." );
-			mixer.update(750);
-			assert.equal( root.rotation.x, 270 , "When an animation is 3/4 in the first loop, it has changed to 3/4 when LoopPingPong." );
-			assert.ok( animationAction.isRunning(), "When an animation is in the first loop, it is running." );
-			mixer.update(1000);
-			assert.equal( root.rotation.x, 90 ,  "When an animation is 3/4 in the second loop, it has changed to 1/4 when LoopPingPong." );
-			assert.ok( animationAction.isRunning(), "When an animation is in second loop when in looprepeat 3 times, it is running." );
-			mixer.update(1000);
-			assert.equal( root.rotation.x, 270 ,  "When an animation is 3/4 in the third loop, it has changed to 3/4 when LoopPingPong." );
-			assert.ok( animationAction.isRunning(), "When an animation is in third loop when in looprepeat 3 times, it is running." );
-			mixer.update(1000);
-			assert.equal( root.rotation.x, 90 ,  "When an animation is 3/4 in the fourth loop, it has changed to 1/4 when LoopPingPong." );
-			assert.ok( animationAction.isRunning(), "When an animation is in fourth loop when in looprepeat 3 times, it is running." );
-			mixer.update(1000);
-			assert.equal( root.rotation.x, 0 , "When an animation ended his fourth loop when in looprepeat 3 times, it stays on the end result." );
-			assert.notOk( animationAction.isRunning(), "When an animation ended his fourth loop when in looprepeat 3 times, it stays not running anymore." );
-			
-		} );
-
-		QUnit.test( "setEffectiveWeight", ( assert ) => {
-
-			var {animationAction} = createAnimation();
-			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation is created, EffectiveWeight is 1." );
-			animationAction.setEffectiveWeight(0.3);
-			assert.equal( animationAction.getEffectiveWeight(), 0.3 , "When EffectiveWeight is set to 0.3 , EffectiveWeight is 0.3." );
-
-
-			var {animationAction} = createAnimation();
-			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation is created, EffectiveWeight is 1." );
-			animationAction.enabled = false;
-			animationAction.setEffectiveWeight(0.3);
-			assert.equal( animationAction.getEffectiveWeight(), 0 , "When EffectiveWeight is set to 0.3 when disabled , EffectiveWeight is 0." );
-
-
-			var { root, mixer,animationAction } = createAnimation();
-			animationAction.setEffectiveWeight(0.5);
-			animationAction.play();
-			mixer.update(500);
-			assert.equal( root.rotation.x, 90 ,  "When an animation has weight 0.5 and runs half through the animation, it has changed to 1/4." );
-			mixer.update(1000);
-			assert.equal( root.rotation.x, 90 ,  "When an animation has weight 0.5 and runs one and half through the animation, it has changed to 1/4." );
-
-
-
-		} );
-
-		QUnit.test( "getEffectiveWeight", ( assert ) => {
-
-			var {animationAction} = createAnimation();
-			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation is created, EffectiveWeight is 1." );
-			animationAction.setEffectiveWeight(0.3);
-			assert.equal( animationAction.getEffectiveWeight(), 0.3 , "When EffectiveWeight is set to 0.3 , EffectiveWeight is 0.3." );
-
-
-			var {animationAction} = createAnimation();
-			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation is created, EffectiveWeight is 1." );
-			animationAction.enabled = false;
-			animationAction.setEffectiveWeight(0.3);
-			assert.equal( animationAction.getEffectiveWeight(), 0 , "When EffectiveWeight is set to 0.3 when disabled , EffectiveWeight is 0." );
-
-		} );
-
-		QUnit.test( "fadeIn", ( assert ) => {
-
-			var {mixer, animationAction} = createAnimation();
-			animationAction.fadeIn(1000);
-			animationAction.play();
-			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation fadeIn is started, EffectiveWeight is 1." );
-			mixer.update(250);
-			assert.equal( animationAction.getEffectiveWeight(), 0.25, "When an animation fadeIn happened 1/4, EffectiveWeight is 0.25." );
-			mixer.update(250);
-			assert.equal( animationAction.getEffectiveWeight(), 0.5, "When an animation fadeIn is halfway , EffectiveWeight is 0.5." );
-			mixer.update(250);
-			assert.equal( animationAction.getEffectiveWeight(), 0.75, "When an animation fadeIn is halfway , EffectiveWeight is 0.75." );
-			mixer.update(500);
-			assert.equal( animationAction.getEffectiveWeight(), 1, "When an animation fadeIn is ended , EffectiveWeight is 1." );
-
-		} );
-
-		QUnit.test( "fadeOut", ( assert ) => {
-
-			var {mixer, animationAction} = createAnimation();
-			animationAction.fadeOut(1000);
-			animationAction.play();
-			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation fadeOut is started, EffectiveWeight is 1." );
-			mixer.update(250);
-			assert.equal( animationAction.getEffectiveWeight(), 0.75, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
-			mixer.update(250);
-			assert.equal( animationAction.getEffectiveWeight(), 0.5, "When an animation fadeOut is halfway , EffectiveWeight is 0.5." );
-			mixer.update(250);
-			assert.equal( animationAction.getEffectiveWeight(), 0.25, "When an animation fadeOut is happened 3/4 , EffectiveWeight is 0.25." );
-			mixer.update(500);
-			assert.equal( animationAction.getEffectiveWeight(), 0, "When an animation fadeOut is ended , EffectiveWeight is 0." );
-
-		} );
-
-		QUnit.test( "crossFadeFrom", ( assert ) => {
-
-			var {mixer, animationAction, animationAction2} = createTwoAnimations();
-			animationAction.crossFadeFrom(animationAction2, 1000, false);
-			animationAction.play();
-			animationAction2.play();
-			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation crossFadeFrom is started, EffectiveWeight is 1." );
-			assert.equal( animationAction2.getEffectiveWeight(), 1 , "When an animation crossFadeFrom is started, EffectiveWeight is 1." );
-			mixer.update(250);
-			assert.equal( animationAction.getEffectiveWeight(), 0.25, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
-			assert.equal( animationAction2.getEffectiveWeight(), 0.75, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
-			mixer.update(250);
-			assert.equal( animationAction.getEffectiveWeight(), 0.5, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
-			assert.equal( animationAction2.getEffectiveWeight(), 0.5, "When an animation fadeOut is halfway , EffectiveWeight is 0.5." );
-			mixer.update(250);
-			assert.equal( animationAction.getEffectiveWeight(), 0.75, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
-			assert.equal( animationAction2.getEffectiveWeight(), 0.25, "When an animation fadeOut is happened 3/4 , EffectiveWeight is 0.25." );
-			mixer.update(500);
-			assert.equal( animationAction.getEffectiveWeight(), 1, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
-			assert.equal( animationAction2.getEffectiveWeight(), 0, "When an animation fadeOut is ended , EffectiveWeight is 0." );
-
-		} );
-
-		QUnit.test( "crossFadeTo", ( assert ) => {
-
-			var {mixer, animationAction, animationAction2} = createTwoAnimations();
-			animationAction2.crossFadeTo(animationAction, 1000, false);
-			animationAction.play();
-			animationAction2.play();
-			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation crossFadeFrom is started, EffectiveWeight is 1." );
-			assert.equal( animationAction2.getEffectiveWeight(), 1 , "When an animation crossFadeFrom is started, EffectiveWeight is 1." );
-			mixer.update(250);
-			assert.equal( animationAction.getEffectiveWeight(), 0.25, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
-			assert.equal( animationAction2.getEffectiveWeight(), 0.75, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
-			mixer.update(250);
-			assert.equal( animationAction.getEffectiveWeight(), 0.5, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
-			assert.equal( animationAction2.getEffectiveWeight(), 0.5, "When an animation fadeOut is halfway , EffectiveWeight is 0.5." );
-			mixer.update(250);
-			assert.equal( animationAction.getEffectiveWeight(), 0.75, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
-			assert.equal( animationAction2.getEffectiveWeight(), 0.25, "When an animation fadeOut is happened 3/4 , EffectiveWeight is 0.25." );
-			mixer.update(500);
-			assert.equal( animationAction.getEffectiveWeight(), 1, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
-			assert.equal( animationAction2.getEffectiveWeight(), 0, "When an animation fadeOut is ended , EffectiveWeight is 0." );
-
-		} );
-
-		QUnit.todo( "stopFading", ( assert ) => {
-
-			assert.ok( false, "everything's gonna be alright" );
-
-		} );
-
-		QUnit.todo( "setEffectiveTimeScale", ( assert ) => {
-
-			assert.ok( false, "everything's gonna be alright" );
-
-		} );
-
-		QUnit.todo( "getEffectiveTimeScale", ( assert ) => {
-
-			assert.ok( false, "everything's gonna be alright" );
-
-		} );
-
-		QUnit.todo( "setDuration", ( assert ) => {
-
-			assert.ok( false, "everything's gonna be alright" );
-
-		} );
-
-		QUnit.todo( "syncWith", ( assert ) => {
-
-			assert.ok( false, "everything's gonna be alright" );
-
-		} );
-
-		QUnit.todo( "halt", ( assert ) => {
-
-			assert.ok( false, "everything's gonna be alright" );
-
-		} );
-
-		QUnit.todo( "warp", ( assert ) => {
-
-			assert.ok( false, "everything's gonna be alright" );
-
-		} );
-
-		QUnit.todo( "stopWarping", ( assert ) => {
-
-			assert.ok( false, "everything's gonna be alright" );
-
-		} );
-
-		QUnit.test( "getMixer", ( assert ) => {
-
-			var { mixer, animationAction } = createAnimation();
-			var mixer2 = animationAction.getMixer();
-			assert.equal( mixer, mixer2, "mixer should be returned by getMixer." );
-
-		} );
-
-		QUnit.test("getClip", (assert) => {
-
-			var { clip, animationAction } = createAnimation();
-			var clip2 = animationAction.getClip();
-			assert.equal( clip, clip2, "clip should be returned by getClip." );
-
-		} );
-
-		QUnit.test( "getRoot", ( assert ) => {
-
-			var { root, animationAction } = createAnimation();
-			var root2 = animationAction.getRoot();
-			assert.equal(root, root2, "root should be returned by getRoot." );
-
-		} );
-
-	} );
-
-} );
+/**
+ * @author TristanVALCKE / https://github.com/Itee
+ */
+/* global QUnit */
+
+import { AnimationAction } from '../../../../src/animation/AnimationAction';
+import { AnimationMixer } from '../../../../src/animation/AnimationMixer';
+import { AnimationClip } from '../../../../src/animation/AnimationClip';
+import { NumberKeyframeTrack } from '../../../../src/animation/tracks/NumberKeyframeTrack';
+import { Object3D } from '../../../../src/core/Object3D';
+import { LoopOnce, LoopRepeat, LoopPingPong } from '../../../../src/constants';
+
+
+function createAnimation(){
+
+	var root = new Object3D();
+	var mixer = new AnimationMixer(root);
+	var track = new NumberKeyframeTrack( ".rotation[x]", [ 0, 1000 ], [ 0, 360 ] );
+	var clip = new AnimationClip( "clip1", 1000, [track] );
+
+	var animationAction = mixer.clipAction( clip );
+	return {
+		root: root,
+		mixer: mixer,
+		track: track,
+		clip: clip,
+		animationAction: animationAction
+	};
+
+}
+
+function createTwoAnimations(){
+
+	var root = new Object3D();
+	var mixer = new AnimationMixer(root);
+	var track = new NumberKeyframeTrack( ".rotation[x]", [ 0, 1000 ], [ 0, 360 ] );
+	var clip = new AnimationClip( "clip1", 1000, [track] );
+	var animationAction = mixer.clipAction( clip );
+
+	var track2 = new NumberKeyframeTrack( ".rotation[y]", [ 0, 1000 ], [ 0, 360 ] );
+	var clip2 = new AnimationClip( "clip2", 1000, [track] );
+	var animationAction2 = mixer.clipAction( clip2 );
+
+	return {
+		root: root,
+		mixer: mixer,
+		track: track,
+		clip: clip,
+		animationAction: animationAction,
+		track2: track2,
+		clip2: clip2,
+		animationAction2: animationAction2
+	};
+
+}
+
+
+export default QUnit.module( 'Animation', () => {
+
+	QUnit.module( 'AnimationAction', () => {
+
+		// INSTANCING
+		QUnit.test( "Instancing", ( assert ) => {
+
+			var mixer = new AnimationMixer();
+			var clip = new AnimationClip( "nonname", - 1, [] );
+
+			var animationAction = new AnimationAction( mixer, clip );
+			assert.ok( animationAction, "animationAction instanciated" );
+
+		} );
+
+		// PUBLIC STUFF
+		QUnit.test( "play", ( assert ) => {
+
+			var {mixer,animationAction} = createAnimation();
+			var animationAction2 = animationAction.play();
+			assert.equal( animationAction, animationAction2, "AnimationAction.play can be chained." );
+
+			var UserException = function () {
+
+				this.message = "AnimationMixer must activate AnimationAction on play.";
+
+			};
+			mixer._activateAction = function ( action ) {
+
+				if ( action === animationAction ) {
+
+					throw new UserException();
+
+				}
+
+			};
+			assert.throws( () => {
+
+				animationAction.play();
+
+			}, new UserException() );
+
+		} );
+
+		QUnit.test( "stop", ( assert ) => {
+
+			var {mixer,animationAction} = createAnimation();
+			var animationAction2 = animationAction.stop();
+			assert.equal( animationAction, animationAction2, "AnimationAction.stop can be chained." );
+
+			var UserException = function () {
+
+				this.message = "AnimationMixer must deactivate AnimationAction on stop.";
+
+			};
+			mixer._deactivateAction = function ( action ) {
+
+				if ( action === animationAction ) {
+
+					throw new UserException();
+
+				}
+
+			};
+			assert.throws( () => {
+
+				animationAction.stop();
+
+			}, new UserException() );
+
+		} );
+
+		QUnit.test( "reset", ( assert ) => {
+
+			var {mixer,animationAction} = createAnimation();
+			var animationAction2 = animationAction.stop();
+			assert.equal( animationAction, animationAction2, "AnimationAction.reset can be chained." );
+			assert.equal( animationAction2.paused, false, "AnimationAction.reset() sets paused false" );
+			assert.equal( animationAction2.enabled, true, "AnimationAction.reset() sets enabled true" );
+			assert.equal( animationAction2.time, 0, "AnimationAction.reset() resets time." );
+			assert.equal( animationAction2._loopCount, - 1, "AnimationAction.reset() resets loopcount." );
+			assert.equal( animationAction2._startTime, null, "AnimationAction.reset() removes starttime." );
+
+		} );
+
+		QUnit.test( "isRunning", ( assert ) => {
+
+			var {mixer,animationAction} = createAnimation();
+			assert.notOk( animationAction.isRunning(), "When an animation is just made, it is not running." );
+			animationAction.play();
+			assert.ok( animationAction.isRunning(), "When an animation is started, it is running." );
+			animationAction.stop();
+			assert.notOk( animationAction.isRunning(), "When an animation is stopped, it is not running." );
+			animationAction.play();
+			animationAction.paused = true;
+			assert.notOk( animationAction.isRunning(), "When an animation is paused, it is not running." );
+			animationAction.paused = false;
+			animationAction.enabled = false;
+			assert.notOk( animationAction.isRunning(), "When an animation is not enabled, it is not running." );
+			animationAction.enabled = true;
+			assert.ok( animationAction.isRunning(), "When an animation is enabled, it is running." );
+
+		} );
+
+		QUnit.test( "isScheduled", ( assert ) => {
+
+			var {mixer,animationAction} = createAnimation();
+			assert.notOk( animationAction.isScheduled(), "When an animation is just made, it is not scheduled." );
+			animationAction.play();
+			assert.ok( animationAction.isScheduled(), "When an animation is started, it is scheduled." );
+			mixer.update(1);
+			assert.ok( animationAction.isScheduled(), "When an animation is updated, it is scheduled." );
+			animationAction.stop();
+			assert.notOk( animationAction.isScheduled(), "When an animation is stopped, it isn't scheduled anymore." );
+
+		   
+		} );
+
+		QUnit.test( "startAt", ( assert ) => {
+
+			var {mixer,animationAction} = createAnimation();
+			animationAction.startAt(2);
+			animationAction.play();
+			assert.notOk( animationAction.isRunning(), "When an animation is started at a specific time, it is not running." );
+			assert.ok( animationAction.isScheduled(), "When an animation is started at a specific time, it is scheduled." );
+			mixer.update(1);
+			assert.notOk( animationAction.isRunning(), "When an animation is started at a specific time and the interval is not passed, it is not running." );
+			assert.ok( animationAction.isScheduled(), "When an animation is started at a specific time and the interval is not passed, it is scheduled." );
+			mixer.update(1);
+			assert.ok( animationAction.isRunning(), "When an animation is started at a specific time and the interval is passed, it is running." );
+			assert.ok( animationAction.isScheduled(), "When an animation is started at a specific time and the interval is passed, it is scheduled." );
+			animationAction.stop();
+			assert.notOk( animationAction.isRunning(), "When an animation is stopped, it is not running." );
+			assert.notOk( animationAction.isScheduled(), "When an animation is stopped, it is not scheduled." );
+			
+
+		} );
+
+		QUnit.test( "setLoop LoopOnce", ( assert ) => {
+
+			var {mixer,animationAction} = createAnimation();
+			animationAction.setLoop(LoopOnce);
+			animationAction.play();
+			assert.ok( animationAction.isRunning(), "When an animation is started, it is running." );
+			mixer.update(500);
+			assert.ok( animationAction.isRunning(), "When an animation is in the first loop, it is running." );
+			mixer.update(500);
+			assert.notOk( animationAction.isRunning(), "When an animation is ended, it is not running." );
+			mixer.update(500);
+			assert.notOk( animationAction.isRunning(), "When an animation is ended, it is not running." );
+	
+		} );
+	
+		QUnit.test( "setLoop LoopRepeat", ( assert ) => {
+	
+			var { root, mixer,animationAction } = createAnimation();
+			animationAction.setLoop(LoopRepeat,3);
+			animationAction.play();
+			assert.ok( animationAction.isRunning(), "When an animation is started, it is running." );
+			mixer.update(750);
+			assert.equal( root.rotation.x, 270 , "When an animation is 3/4 in the first loop, it has changed to 3/4 when LoopRepeat." );
+			assert.ok( animationAction.isRunning(), "When an animation is in the first loop, it is running." );
+			mixer.update(1000);
+			assert.equal( root.rotation.x, 270 , "When an animation is 3/4 in the second loop, it has changed to 3/4 when LoopRepeat." );
+			assert.ok( animationAction.isRunning(), "When an animation is in second loop when in looprepeat 3 times, it is running." );
+			mixer.update(1000);
+			assert.equal( root.rotation.x, 270 , "When an animation is 3/4 in the third loop, it has changed to 3/4 when LoopRepeat." );
+			assert.ok( animationAction.isRunning(), "When an animation is in third loop when in looprepeat 3 times, it is running." );
+			mixer.update(1000);
+			assert.equal( root.rotation.x, 270 , "When an animation is 3/4 in the fourth loop, it has changed to 3/4 when LoopRepeat." );
+			assert.ok( animationAction.isRunning(), "When an animation is in fourth loop when in looprepeat 3 times, it is running." );
+			mixer.update(1000);
+			assert.equal( root.rotation.x, 0 , "When an animation ended his third loop when in looprepeat 3 times, it stays on the end result." );
+			assert.notOk( animationAction.isRunning(), "When an animation ended his third loop when in looprepeat 3 times, it stays not running anymore." );
+			
+		} );
+
+		QUnit.test( "setLoop LoopPingPong", ( assert ) => {
+	
+			var {root, mixer,animationAction} = createAnimation();
+			animationAction.setLoop(LoopPingPong,3);
+			animationAction.play();
+			assert.ok( animationAction.isRunning(), "When an animation is started, it is running." );
+			mixer.update(750);
+			assert.equal( root.rotation.x, 270 , "When an animation is 3/4 in the first loop, it has changed to 3/4 when LoopPingPong." );
+			assert.ok( animationAction.isRunning(), "When an animation is in the first loop, it is running." );
+			mixer.update(1000);
+			assert.equal( root.rotation.x, 90 ,  "When an animation is 3/4 in the second loop, it has changed to 1/4 when LoopPingPong." );
+			assert.ok( animationAction.isRunning(), "When an animation is in second loop when in looprepeat 3 times, it is running." );
+			mixer.update(1000);
+			assert.equal( root.rotation.x, 270 ,  "When an animation is 3/4 in the third loop, it has changed to 3/4 when LoopPingPong." );
+			assert.ok( animationAction.isRunning(), "When an animation is in third loop when in looprepeat 3 times, it is running." );
+			mixer.update(1000);
+			assert.equal( root.rotation.x, 90 ,  "When an animation is 3/4 in the fourth loop, it has changed to 1/4 when LoopPingPong." );
+			assert.ok( animationAction.isRunning(), "When an animation is in fourth loop when in looprepeat 3 times, it is running." );
+			mixer.update(1000);
+			assert.equal( root.rotation.x, 0 , "When an animation ended his fourth loop when in looprepeat 3 times, it stays on the end result." );
+			assert.notOk( animationAction.isRunning(), "When an animation ended his fourth loop when in looprepeat 3 times, it stays not running anymore." );
+			
+		} );
+
+		QUnit.test( "setEffectiveWeight", ( assert ) => {
+
+			var {animationAction} = createAnimation();
+			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation is created, EffectiveWeight is 1." );
+			animationAction.setEffectiveWeight(0.3);
+			assert.equal( animationAction.getEffectiveWeight(), 0.3 , "When EffectiveWeight is set to 0.3 , EffectiveWeight is 0.3." );
+
+
+			var {animationAction} = createAnimation();
+			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation is created, EffectiveWeight is 1." );
+			animationAction.enabled = false;
+			animationAction.setEffectiveWeight(0.3);
+			assert.equal( animationAction.getEffectiveWeight(), 0 , "When EffectiveWeight is set to 0.3 when disabled , EffectiveWeight is 0." );
+
+
+			var { root, mixer,animationAction } = createAnimation();
+			animationAction.setEffectiveWeight(0.5);
+			animationAction.play();
+			mixer.update(500);
+			assert.equal( root.rotation.x, 90 ,  "When an animation has weight 0.5 and runs half through the animation, it has changed to 1/4." );
+			mixer.update(1000);
+			assert.equal( root.rotation.x, 90 ,  "When an animation has weight 0.5 and runs one and half through the animation, it has changed to 1/4." );
+
+
+
+		} );
+
+		QUnit.test( "getEffectiveWeight", ( assert ) => {
+
+			var {animationAction} = createAnimation();
+			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation is created, EffectiveWeight is 1." );
+			animationAction.setEffectiveWeight(0.3);
+			assert.equal( animationAction.getEffectiveWeight(), 0.3 , "When EffectiveWeight is set to 0.3 , EffectiveWeight is 0.3." );
+
+
+			var {animationAction} = createAnimation();
+			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation is created, EffectiveWeight is 1." );
+			animationAction.enabled = false;
+			animationAction.setEffectiveWeight(0.3);
+			assert.equal( animationAction.getEffectiveWeight(), 0 , "When EffectiveWeight is set to 0.3 when disabled , EffectiveWeight is 0." );
+
+		} );
+
+		QUnit.test( "fadeIn", ( assert ) => {
+
+			var {mixer, animationAction} = createAnimation();
+			animationAction.fadeIn(1000);
+			animationAction.play();
+			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation fadeIn is started, EffectiveWeight is 1." );
+			mixer.update(250);
+			assert.equal( animationAction.getEffectiveWeight(), 0.25, "When an animation fadeIn happened 1/4, EffectiveWeight is 0.25." );
+			mixer.update(250);
+			assert.equal( animationAction.getEffectiveWeight(), 0.5, "When an animation fadeIn is halfway , EffectiveWeight is 0.5." );
+			mixer.update(250);
+			assert.equal( animationAction.getEffectiveWeight(), 0.75, "When an animation fadeIn is halfway , EffectiveWeight is 0.75." );
+			mixer.update(500);
+			assert.equal( animationAction.getEffectiveWeight(), 1, "When an animation fadeIn is ended , EffectiveWeight is 1." );
+
+		} );
+
+		QUnit.test( "fadeOut", ( assert ) => {
+
+			var {mixer, animationAction} = createAnimation();
+			animationAction.fadeOut(1000);
+			animationAction.play();
+			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation fadeOut is started, EffectiveWeight is 1." );
+			mixer.update(250);
+			assert.equal( animationAction.getEffectiveWeight(), 0.75, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
+			mixer.update(250);
+			assert.equal( animationAction.getEffectiveWeight(), 0.5, "When an animation fadeOut is halfway , EffectiveWeight is 0.5." );
+			mixer.update(250);
+			assert.equal( animationAction.getEffectiveWeight(), 0.25, "When an animation fadeOut is happened 3/4 , EffectiveWeight is 0.25." );
+			mixer.update(500);
+			assert.equal( animationAction.getEffectiveWeight(), 0, "When an animation fadeOut is ended , EffectiveWeight is 0." );
+
+		} );
+
+		QUnit.test( "crossFadeFrom", ( assert ) => {
+
+			var {mixer, animationAction, animationAction2} = createTwoAnimations();
+			animationAction.crossFadeFrom(animationAction2, 1000, false);
+			animationAction.play();
+			animationAction2.play();
+			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation crossFadeFrom is started, EffectiveWeight is 1." );
+			assert.equal( animationAction2.getEffectiveWeight(), 1 , "When an animation crossFadeFrom is started, EffectiveWeight is 1." );
+			mixer.update(250);
+			assert.equal( animationAction.getEffectiveWeight(), 0.25, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
+			assert.equal( animationAction2.getEffectiveWeight(), 0.75, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
+			mixer.update(250);
+			assert.equal( animationAction.getEffectiveWeight(), 0.5, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
+			assert.equal( animationAction2.getEffectiveWeight(), 0.5, "When an animation fadeOut is halfway , EffectiveWeight is 0.5." );
+			mixer.update(250);
+			assert.equal( animationAction.getEffectiveWeight(), 0.75, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
+			assert.equal( animationAction2.getEffectiveWeight(), 0.25, "When an animation fadeOut is happened 3/4 , EffectiveWeight is 0.25." );
+			mixer.update(500);
+			assert.equal( animationAction.getEffectiveWeight(), 1, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
+			assert.equal( animationAction2.getEffectiveWeight(), 0, "When an animation fadeOut is ended , EffectiveWeight is 0." );
+
+		} );
+
+		QUnit.test( "crossFadeTo", ( assert ) => {
+
+			var {mixer, animationAction, animationAction2} = createTwoAnimations();
+			animationAction2.crossFadeTo(animationAction, 1000, false);
+			animationAction.play();
+			animationAction2.play();
+			assert.equal( animationAction.getEffectiveWeight(), 1 , "When an animation crossFadeFrom is started, EffectiveWeight is 1." );
+			assert.equal( animationAction2.getEffectiveWeight(), 1 , "When an animation crossFadeFrom is started, EffectiveWeight is 1." );
+			mixer.update(250);
+			assert.equal( animationAction.getEffectiveWeight(), 0.25, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
+			assert.equal( animationAction2.getEffectiveWeight(), 0.75, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
+			mixer.update(250);
+			assert.equal( animationAction.getEffectiveWeight(), 0.5, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
+			assert.equal( animationAction2.getEffectiveWeight(), 0.5, "When an animation fadeOut is halfway , EffectiveWeight is 0.5." );
+			mixer.update(250);
+			assert.equal( animationAction.getEffectiveWeight(), 0.75, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
+			assert.equal( animationAction2.getEffectiveWeight(), 0.25, "When an animation fadeOut is happened 3/4 , EffectiveWeight is 0.25." );
+			mixer.update(500);
+			assert.equal( animationAction.getEffectiveWeight(), 1, "When an animation fadeOut happened 1/4, EffectiveWeight is 0.75." );
+			assert.equal( animationAction2.getEffectiveWeight(), 0, "When an animation fadeOut is ended , EffectiveWeight is 0." );
+
+		} );
+
+		QUnit.todo( "stopFading", ( assert ) => {
+
+			assert.ok( false, "everything's gonna be alright" );
+
+		} );
+
+		QUnit.todo( "setEffectiveTimeScale", ( assert ) => {
+
+			assert.ok( false, "everything's gonna be alright" );
+
+		} );
+
+		QUnit.todo( "getEffectiveTimeScale", ( assert ) => {
+
+			assert.ok( false, "everything's gonna be alright" );
+
+		} );
+
+		QUnit.todo( "setDuration", ( assert ) => {
+
+			assert.ok( false, "everything's gonna be alright" );
+
+		} );
+
+		QUnit.todo( "syncWith", ( assert ) => {
+
+			assert.ok( false, "everything's gonna be alright" );
+
+		} );
+
+		QUnit.todo( "halt", ( assert ) => {
+
+			assert.ok( false, "everything's gonna be alright" );
+
+		} );
+
+		QUnit.todo( "warp", ( assert ) => {
+
+			assert.ok( false, "everything's gonna be alright" );
+
+		} );
+
+		QUnit.todo( "stopWarping", ( assert ) => {
+
+			assert.ok( false, "everything's gonna be alright" );
+
+		} );
+
+		QUnit.test( "getMixer", ( assert ) => {
+
+			var { mixer, animationAction } = createAnimation();
+			var mixer2 = animationAction.getMixer();
+			assert.equal( mixer, mixer2, "mixer should be returned by getMixer." );
+
+		} );
+
+		QUnit.test("getClip", (assert) => {
+
+			var { clip, animationAction } = createAnimation();
+			var clip2 = animationAction.getClip();
+			assert.equal( clip, clip2, "clip should be returned by getClip." );
+
+		} );
+
+		QUnit.test( "getRoot", ( assert ) => {
+
+			var { root, animationAction } = createAnimation();
+			var root2 = animationAction.getRoot();
+			assert.equal(root, root2, "root should be returned by getRoot." );
+
+		} );
+
+	} );
+
+} );

部分文件因文件數量過多而無法顯示