فهرست منبع

Resolve conflict

yomboprime 6 سال پیش
والد
کامیت
79349fae7c
32فایلهای تغییر یافته به همراه4692 افزوده شده و 509 حذف شده
  1. 10 0
      docs/manual/en/introduction/Import-via-modules.html
  2. 5 5
      examples/js/animation/CCDIKSolver.js
  3. 42 63
      examples/js/loaders/AWDLoader.js
  4. 576 404
      examples/js/loaders/DRACOLoader.js
  5. 1 1
      examples/js/loaders/TTFLoader.js
  6. 5 5
      examples/js/misc/CarControls.js
  7. 0 0
      examples/js/misc/ConvexObjectBreaker.js
  8. 0 0
      examples/js/misc/GPUComputationRenderer.js
  9. 12 8
      examples/js/misc/Ocean.js
  10. 10 9
      examples/js/shaders/OceanShaders.js
  11. 25 0
      examples/jsm/animation/CCDIKSolver.d.ts
  12. 472 0
      examples/jsm/animation/CCDIKSolver.js
  13. 50 0
      examples/jsm/loaders/AWDLoader.d.ts
  14. 1234 0
      examples/jsm/loaders/AWDLoader.js
  15. 21 0
      examples/jsm/loaders/DRACOLoader.d.ts
  16. 740 0
      examples/jsm/loaders/DRACOLoader.js
  17. 16 0
      examples/jsm/loaders/TTFLoader.d.ts
  18. 207 0
      examples/jsm/loaders/TTFLoader.js
  19. 314 0
      examples/jsm/misc/CarControls.js
  20. 68 0
      examples/jsm/misc/Ocean.d.ts
  21. 411 0
      examples/jsm/misc/Ocean.js
  22. 1 1
      examples/jsm/postprocessing/EffectComposer.d.ts
  23. 1 1
      examples/jsm/postprocessing/RenderPass.d.ts
  24. 75 0
      examples/jsm/shaders/OceanShaders.d.ts
  25. 377 0
      examples/jsm/shaders/OceanShaders.js
  26. 1 1
      examples/webgl_gpgpu_birds.html
  27. 1 1
      examples/webgl_gpgpu_protoplanet.html
  28. 1 1
      examples/webgl_gpgpu_water.html
  29. 6 6
      examples/webgl_materials_cars.html
  30. 1 1
      examples/webgl_physics_convex_break.html
  31. 1 1
      examples/webgl_shaders_ocean2.html
  32. 8 1
      utils/modularize.js

+ 10 - 0
docs/manual/en/introduction/Import-via-modules.html

@@ -79,6 +79,7 @@
 				<li>animation
 					<ul>
 						<li>AnimationClipCreator</li>
+						<li>CCDIKSolver</li>
 						<li>TimelinerController</li>
 					</ul>
 				</li>
@@ -161,12 +162,14 @@
 					<ul>
 						<li>3MFLoader</li>
 						<li>AMFLoader</li>
+						<li>AWDLoader</li>
 						<li>AssimpJSONLoader</li>
 						<li>AssimpLoader</li>
 						<li>BabylonLoader</li>
 						<li>BVHLoader</li>
 						<li>ColladaLoader</li>
 						<li>DDSLoader</li>
+						<li>DRACOLoader</li>
 						<li>EXRLoader</li>
 						<li>FBXLoader</li>
 						<li>GCodeLoader</li>
@@ -188,6 +191,7 @@
 						<li>SVGLoader</li>
 						<li>TDSLoader</li>
 						<li>TGALoader</li>
+						<li>TTFLoader</li>
 						<li>VRMLLoader</li>
 						<li>VTKLoader</li>
 					</ul>
@@ -201,6 +205,11 @@
 						<li>SimplexNoise</li>
 					</ul>
 				</li>
+				<li>misc
+					<ul>
+						<li>Ocean</li>
+					</ul>
+				</li>
 				<li>modifiers
 					<ul>
 						<li>ExplodeModifier</li>
@@ -299,6 +308,7 @@
 						<li>LuminosityShader</li>
 						<li>MirrorShader</li>
 						<li>NormalMapShader</li>
+						<li>OceanShaders</li>
 						<li>ParallaxShader</li>
 						<li>PixelShader</li>
 						<li>RGBShiftShader</li>

+ 5 - 5
examples/js/animation/CCDIKSolver.js

@@ -65,7 +65,7 @@ THREE.CCDIKSolver = ( function () {
 				// for reference overhead reduction in loop
 				var math = Math;
 
-				for ( var i = 0, il = iks.length; i < il; i++ ) {
+				for ( var i = 0, il = iks.length; i < il; i ++ ) {
 
 					var ik = iks[ i ];
 					var effector = bones[ ik.effector ];
@@ -78,11 +78,11 @@ THREE.CCDIKSolver = ( function () {
 					var links = ik.links;
 					var iteration = ik.iteration !== undefined ? ik.iteration : 1;
 
-					for ( var j = 0; j < iteration; j++ ) {
+					for ( var j = 0; j < iteration; j ++ ) {
 
 						var rotated = false;
 
-						for ( var k = 0, kl = links.length; k < kl; k++ ) {
+						for ( var k = 0, kl = links.length; k < kl; k ++ ) {
 
 							var link = bones[ links[ k ].index ];
 
@@ -115,9 +115,9 @@ THREE.CCDIKSolver = ( function () {
 
 								angle = 1.0;
 
-							} else if ( angle < -1.0 ) {
+							} else if ( angle < - 1.0 ) {
 
-								angle = -1.0;
+								angle = - 1.0;
 
 							}
 

+ 42 - 63
examples/js/loaders/AWDLoader.js

@@ -3,11 +3,11 @@
  * Date: 09/12/2013 17:21
  */
 
-( function () {
+THREE.AWDLoader = ( function () {
 
-	var UNCOMPRESSED = 0,
-		DEFLATE = 1,
-		LZMA = 2,
+	var //UNCOMPRESSED = 0,
+		//DEFLATE = 1,
+		//LZMA = 2,
 
 		AWD_FIELD_INT8 = 1,
 		AWD_FIELD_INT16 = 2,
@@ -18,10 +18,10 @@
 		AWD_FIELD_FLOAT32 = 7,
 		AWD_FIELD_FLOAT64 = 8,
 		AWD_FIELD_BOOL = 21,
-		AWD_FIELD_COLOR = 22,
+		//AWD_FIELD_COLOR = 22,
 		AWD_FIELD_BADDR = 23,
-		AWD_FIELD_STRING = 31,
-		AWD_FIELD_BYTEARRAY = 32,
+		//AWD_FIELD_STRING = 31,
+		//AWD_FIELD_BYTEARRAY = 32,
 		AWD_FIELD_VECTOR2x1 = 41,
 		AWD_FIELD_VECTOR3x1 = 42,
 		AWD_FIELD_VECTOR4x1 = 43,
@@ -31,15 +31,15 @@
 		AWD_FIELD_MTX4x4 = 47,
 
 		BOOL = 21,
-		COLOR = 22,
+		//COLOR = 22,
 		BADDR = 23,
 
-		INT8 = 1,
-		INT16 = 2,
-		INT32 = 3,
+		//INT8 = 1,
+		//INT16 = 2,
+		//INT32 = 3,
 		UINT8 = 4,
 		UINT16 = 5,
-		UINT32 = 6,
+		//UINT32 = 6,
 		FLOAT32 = 7,
 		FLOAT64 = 8;
 
@@ -76,7 +76,7 @@
 		}
 	};
 
-	THREE.AWDLoader = function ( manager ) {
+	var AWDLoader = function ( manager ) {
 
 		this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 
@@ -104,9 +104,9 @@
 
 	};
 
-	THREE.AWDLoader.prototype = {
+	AWDLoader.prototype = {
 
-		constructor: THREE.AWDLoader,
+		constructor: AWDLoader,
 
 		load: function ( url, onLoad, onProgress, onError ) {
 
@@ -178,57 +178,49 @@
 			switch ( type ) {
 
 				case 1:
-					assetData = this.parseMeshData( len );
+					assetData = this.parseMeshData();
 					break;
 
 				case 22:
-					assetData = this.parseContainer( len );
+					assetData = this.parseContainer();
 					break;
 
 				case 23:
-					assetData = this.parseMeshInstance( len );
+					assetData = this.parseMeshInstance();
 					break;
 
 				case 81:
-					assetData = this.parseMaterial( len );
+					assetData = this.parseMaterial();
 					break;
 
 				case 82:
-					assetData = this.parseTexture( len );
+					assetData = this.parseTexture();
 					break;
 
 				case 101:
-					assetData = this.parseSkeleton( len );
+					assetData = this.parseSkeleton();
 					break;
 
-	    //  case 111:
-	    //    assetData = this.parseMeshPoseAnimation(len, true);
-	    //    break;
-
 				case 112:
-					assetData = this.parseMeshPoseAnimation( len, false );
+					assetData = this.parseMeshPoseAnimation( false );
 					break;
 
 				case 113:
-					assetData = this.parseVertexAnimationSet( len );
+					assetData = this.parseVertexAnimationSet();
 					break;
 
 				case 102:
-					assetData = this.parseSkeletonPose( len );
+					assetData = this.parseSkeletonPose();
 					break;
 
 				case 103:
-					assetData = this.parseSkeletonAnimation( len );
+					assetData = this.parseSkeletonAnimation();
 					break;
 
 				case 122:
-					assetData = this.parseAnimatorSet( len );
+					assetData = this.parseAnimatorSet();
 					break;
 
-				// case 121:
-				//  assetData = parseUVAnimation(len);
-				//  break;
-
 				default:
 					//debug('Ignoring block!',type, len);
 					this._ptr += len;
@@ -279,7 +271,7 @@
 
 		},
 
-		parseContainer: function ( len ) {
+		parseContainer: function () {
 
 			var parent,
 				ctr = new THREE.Object3D(),
@@ -305,7 +297,7 @@
 
 		},
 
-		parseMeshInstance: function ( len ) {
+		parseMeshInstance: function () {
 
 			var name,
 				mesh, geometries, meshLen, meshes,
@@ -379,14 +371,13 @@
 
 		},
 
-		parseMaterial: function ( len ) {
+		parseMaterial: function () {
 
 			var name,
 				type,
 				props,
 				mat,
 				attributes,
-				finalize,
 				num_methods,
 				methods_parsed;
 
@@ -449,7 +440,7 @@
 
 		},
 
-		parseTexture: function ( len ) {
+		parseTexture: function () {
 
 			var name = this.readUTF(),
 				type = this.readU8(),
@@ -493,7 +484,7 @@
 
 		},
 
-		parseSkeleton: function ( len ) {
+		parseSkeleton: function () {
 
 			// Array<Bone>
 			var name = this.readUTF(),
@@ -534,7 +525,7 @@
 
 		},
 
-		parseSkeletonPose: function ( blockID ) {
+		parseSkeletonPose: function () {
 
 			var name = this.readUTF();
 
@@ -549,8 +540,6 @@
 
 			while ( joints_parsed < num_joints ) {
 
-				var joint_pose;
-
 				var has_transform; //:uint;
 				var mtx_data;
 
@@ -577,7 +566,7 @@
 
 		},
 
-		parseSkeletonAnimation: function ( blockID ) {
+		parseSkeletonAnimation: function () {
 
 			var frame_dur;
 			var pose_addr;
@@ -591,7 +580,6 @@
 			this.parseProperties( null );
 
 			var frames_parsed = 0;
-			var returnedArray;
 
 			// debug( 'parse Skeleton Animation. frames : ' + num_frames);
 
@@ -623,7 +611,7 @@
 
 		},
 
-		parseVertexAnimationSet: function ( len ) {
+		parseVertexAnimationSet: function () {
 
 			var poseBlockAdress,
 				name = this.readUTF(),
@@ -647,14 +635,11 @@
 
 		},
 
-		parseAnimatorSet: function ( len ) {
-
-			var targetMesh;
+		parseAnimatorSet: function () {
 
 			var animSetBlockAdress; //:int
 
 			var targetAnimationSet; //:AnimationSetBase;
-			var outputString = ""; //:String = "";
 			var name = this.readUTF();
 			var type = this.readU16();
 
@@ -673,13 +658,12 @@
 			this.parseUserAttributes();
 			this.parseUserAttributes();
 
-			var returnedArray;
 			var targetMeshes = []; //:Vector.<Mesh> = new Vector.<Mesh>;
 
 			for ( i = 0; i < meshAdresses.length; i ++ ) {
 
-				//      returnedArray = getAssetByID(meshAdresses[i], [AssetType.MESH]);
-				//      if (returnedArray[0])
+				//			returnedArray = getAssetByID(meshAdresses[i], [AssetType.MESH]);
+				//			if (returnedArray[0])
 				targetMeshes.push( this._blocks[ meshAdresses[ i ] ].data );
 
 			}
@@ -711,14 +695,13 @@
 
 		},
 
-		parseMeshData: function ( len ) {
+		parseMeshData: function () {
 
 			var name = this.readUTF(),
 				num_subs = this.readU16(),
 				geom,
 				subs_parsed = 0,
 				buffer,
-				skinW, skinI,
 				geometries = [];
 
 			// Ignore for now
@@ -848,29 +831,23 @@
 
 		},
 
-		parseMeshPoseAnimation: function ( len, poseOnly ) {
+		parseMeshPoseAnimation: function ( poseOnly ) {
 
 			var num_frames = 1,
 				num_submeshes,
 				frames_parsed,
 				subMeshParsed,
-				frame_dur,
-				x, y, z,
 
 				str_len,
 				str_end,
 				geom,
-				subGeom,
 				idx = 0,
 				clip = {},
-				indices,
-				verts,
 				num_Streams,
 				streamsParsed,
 				streamtypes = [],
 
 				props,
-				thisGeo,
 				name = this.readUTF(),
 				geoAdress = this.readU32();
 
@@ -912,7 +889,7 @@
 
 			while ( frames_parsed < num_frames ) {
 
-				frame_dur = this.readU16();
+				this.readU16();
 				subMeshParsed = 0;
 
 				while ( subMeshParsed < num_submeshes ) {
@@ -1236,4 +1213,6 @@
 
 	};
 
+	return AWDLoader;
+
 } )();

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 576 - 404
examples/js/loaders/DRACOLoader.js


+ 1 - 1
examples/js/loaders/TTFLoader.js

@@ -68,7 +68,7 @@ THREE.TTFLoader.prototype = {
 
 					}
 
-					glyph.path.commands.forEach( function ( command, i ) {
+					glyph.path.commands.forEach( function ( command ) {
 
 						if ( command.type.toLowerCase() === 'c' ) {
 

+ 5 - 5
examples/js/Car.js → examples/js/misc/CarControls.js

@@ -9,7 +9,7 @@
  *
  */
 
-THREE.Car = ( function ( ) {
+THREE.CarControls = ( function ( ) {
 
 	// private variables
 	var steeringWheelSpeed = 1.5;
@@ -51,7 +51,7 @@ THREE.Car = ( function ( ) {
 
 	};
 
-	function Car( maxSpeed, acceleration, brakePower, turningRadius, keys ) {
+	function CarControls( maxSpeed, acceleration, brakePower, turningRadius, keys ) {
 
 		this.enabled = true;
 
@@ -96,9 +96,9 @@ THREE.Car = ( function ( ) {
 
 	}
 
-	Car.prototype = {
+	CarControls.prototype = {
 
-		constructor: Car,
+		constructor: CarControls,
 
 		onKeyDown: function ( event ) {
 
@@ -300,6 +300,6 @@ THREE.Car = ( function ( ) {
 
 	}
 
-	return Car;
+	return CarControls;
 
 } )();

+ 0 - 0
examples/js/ConvexObjectBreaker.js → examples/js/misc/ConvexObjectBreaker.js


+ 0 - 0
examples/js/GPUComputationRenderer.js → examples/js/misc/GPUComputationRenderer.js


+ 12 - 8
examples/js/Ocean.js → examples/js/misc/Ocean.js

@@ -1,3 +1,7 @@
+/*
+	three.js Ocean
+*/
+
 THREE.Ocean = function ( renderer, camera, scene, options ) {
 
 	// flag used to trigger parameter changes
@@ -87,10 +91,10 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	////////////////////////////////////////
 
 	// 0 - The vertex shader used in all of the simulation steps
-	var fullscreeenVertexShader = THREE.ShaderLib[ "ocean_sim_vertex" ];
+	var fullscreeenVertexShader = THREE.OceanShaders[ "ocean_sim_vertex" ];
 
 	// 1 - Horizontal wave vertices used for FFT
-	var oceanHorizontalShader = THREE.ShaderLib[ "ocean_subtransform" ];
+	var oceanHorizontalShader = THREE.OceanShaders[ "ocean_subtransform" ];
 	var oceanHorizontalUniforms = THREE.UniformsUtils.clone( oceanHorizontalShader.uniforms );
 	this.materialOceanHorizontal = new THREE.ShaderMaterial( {
 		uniforms: oceanHorizontalUniforms,
@@ -103,7 +107,7 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	this.materialOceanHorizontal.depthTest = false;
 
 	// 2 - Vertical wave vertices used for FFT
-	var oceanVerticalShader = THREE.ShaderLib[ "ocean_subtransform" ];
+	var oceanVerticalShader = THREE.OceanShaders[ "ocean_subtransform" ];
 	var oceanVerticalUniforms = THREE.UniformsUtils.clone( oceanVerticalShader.uniforms );
 	this.materialOceanVertical = new THREE.ShaderMaterial( {
 		uniforms: oceanVerticalUniforms,
@@ -116,7 +120,7 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	this.materialOceanVertical.depthTest = false;
 
 	// 3 - Initial spectrum used to generate height map
-	var initialSpectrumShader = THREE.ShaderLib[ "ocean_initial_spectrum" ];
+	var initialSpectrumShader = THREE.OceanShaders[ "ocean_initial_spectrum" ];
 	var initialSpectrumUniforms = THREE.UniformsUtils.clone( initialSpectrumShader.uniforms );
 	this.materialInitialSpectrum = new THREE.ShaderMaterial( {
 		uniforms: initialSpectrumUniforms,
@@ -128,7 +132,7 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	this.materialInitialSpectrum.depthTest = false;
 
 	// 4 - Phases used to animate heightmap
-	var phaseShader = THREE.ShaderLib[ "ocean_phase" ];
+	var phaseShader = THREE.OceanShaders[ "ocean_phase" ];
 	var phaseUniforms = THREE.UniformsUtils.clone( phaseShader.uniforms );
 	this.materialPhase = new THREE.ShaderMaterial( {
 		uniforms: phaseUniforms,
@@ -139,7 +143,7 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	this.materialPhase.depthTest = false;
 
 	// 5 - Shader used to update spectrum
-	var spectrumShader = THREE.ShaderLib[ "ocean_spectrum" ];
+	var spectrumShader = THREE.OceanShaders[ "ocean_spectrum" ];
 	var spectrumUniforms = THREE.UniformsUtils.clone( spectrumShader.uniforms );
 	this.materialSpectrum = new THREE.ShaderMaterial( {
 		uniforms: spectrumUniforms,
@@ -151,7 +155,7 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	this.materialSpectrum.depthTest = false;
 
 	// 6 - Shader used to update spectrum normals
-	var normalShader = THREE.ShaderLib[ "ocean_normals" ];
+	var normalShader = THREE.OceanShaders[ "ocean_normals" ];
 	var normalUniforms = THREE.UniformsUtils.clone( normalShader.uniforms );
 	this.materialNormal = new THREE.ShaderMaterial( {
 		uniforms: normalUniforms,
@@ -163,7 +167,7 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	this.materialNormal.depthTest = false;
 
 	// 7 - Shader used to update normals
-	var oceanShader = THREE.ShaderLib[ "ocean_main" ];
+	var oceanShader = THREE.OceanShaders[ "ocean_main" ];
 	var oceanUniforms = THREE.UniformsUtils.clone( oceanShader.uniforms );
 	this.materialOcean = new THREE.ShaderMaterial( {
 		uniforms: oceanUniforms,

+ 10 - 9
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
@@ -20,9 +20,10 @@
 
 // -- Rendering Shader
 // [7] ocean_main               -> Vertex and Fragment shader used to create the final render
+*/
 
-
-THREE.ShaderLib[ 'ocean_sim_vertex' ] = {
+THREE.OceanShaders = {}
+THREE.OceanShaders[ 'ocean_sim_vertex' ] = {
 	vertexShader: [
 		'varying vec2 vUV;',
 
@@ -32,7 +33,7 @@ THREE.ShaderLib[ 'ocean_sim_vertex' ] = {
 		'}'
 	].join( '\n' )
 };
-THREE.ShaderLib[ 'ocean_subtransform' ] = {
+THREE.OceanShaders[ 'ocean_subtransform' ] = {
 	uniforms: {
 		"u_input": { value: null },
 		"u_transformSize": { value: 512.0 },
@@ -82,7 +83,7 @@ THREE.ShaderLib[ 'ocean_subtransform' ] = {
 		'}'
 	].join( '\n' )
 };
-THREE.ShaderLib[ 'ocean_initial_spectrum' ] = {
+THREE.OceanShaders[ 'ocean_initial_spectrum' ] = {
 	uniforms: {
 		"u_wind": { value: new THREE.Vector2( 10.0, 10.0 ) },
 		"u_resolution": { value: 512.0 },
@@ -163,7 +164,7 @@ THREE.ShaderLib[ 'ocean_initial_spectrum' ] = {
 		'}'
 	].join( '\n' )
 };
-THREE.ShaderLib[ 'ocean_phase' ] = {
+THREE.OceanShaders[ 'ocean_phase' ] = {
 	uniforms: {
 		"u_phases": { value: null },
 		"u_deltaTime": { value: null },
@@ -203,7 +204,7 @@ THREE.ShaderLib[ 'ocean_phase' ] = {
 		'}'
 	].join( '\n' )
 };
-THREE.ShaderLib[ 'ocean_spectrum' ] = {
+THREE.OceanShaders[ 'ocean_spectrum' ] = {
 	uniforms: {
 		"u_size": { value: null },
 		"u_resolution": { value: null },
@@ -267,7 +268,7 @@ THREE.ShaderLib[ 'ocean_spectrum' ] = {
 		'}'
 	].join( '\n' )
 };
-THREE.ShaderLib[ 'ocean_normals' ] = {
+THREE.OceanShaders[ 'ocean_normals' ] = {
 	uniforms: {
 		"u_displacementMap": { value: null },
 		"u_resolution": { value: null },
@@ -301,7 +302,7 @@ THREE.ShaderLib[ 'ocean_normals' ] = {
 		'}'
 	].join( '\n' )
 };
-THREE.ShaderLib[ 'ocean_main' ] = {
+THREE.OceanShaders[ 'ocean_main' ] = {
 	uniforms: {
 		"u_displacementMap": { value: null },
 		"u_normalMap": { value: null },

+ 25 - 0
examples/jsm/animation/CCDIKSolver.d.ts

@@ -0,0 +1,25 @@
+import {
+  SkinnedMesh,
+} from '../../../src/Three';
+
+export interface IKS {
+  effector: number;
+  iteration: number;
+  links: {
+    enabled: boolean;
+    index: number;
+  }
+  maxAngle: number;
+  target: number;
+}
+
+export class CCDIKSolver {
+  constructor(mesh: SkinnedMesh, iks: IKS[]);
+
+  update(): this;
+  createHelper(): CCDIKHelper;
+}
+
+export class CCDIKHelper {
+  constructor(mesh: SkinnedMesh, iks: IKS[]);
+}

+ 472 - 0
examples/jsm/animation/CCDIKSolver.js

@@ -0,0 +1,472 @@
+/**
+ * @author takahiro / https://github.com/takahirox
+ *
+ * CCD Algorithm
+ *  - https://sites.google.com/site/auraliusproject/ccd-algorithm
+ *
+ * // ik parameter example
+ * //
+ * // target, effector, index in links are bone index in skeleton.bones.
+ * // the bones relation should be
+ * // <-- parent                                  child -->
+ * // links[ n ], links[ n - 1 ], ..., links[ 0 ], effector
+ * iks = [ {
+ *	target: 1,
+ *	effector: 2,
+ *	links: [ { index: 5, limitation: new Vector3( 1, 0, 0 ) }, { index: 4, enabled: false }, { index : 3 } ],
+ *	iteration: 10,
+ *	minAngle: 0.0,
+ *	maxAngle: 1.0,
+ * } ];
+ */
+
+import {
+	BufferAttribute,
+	BufferGeometry,
+	Color,
+	Line,
+	LineBasicMaterial,
+	Matrix4,
+	Mesh,
+	MeshBasicMaterial,
+	Object3D,
+	Quaternion,
+	SphereBufferGeometry,
+	Vector3
+} from "../../../build/three.module.js";
+
+var CCDIKSolver = ( function () {
+
+	/**
+	 * @param {THREE.SkinnedMesh} mesh
+	 * @param {Array<Object>} iks
+	 */
+	function CCDIKSolver( mesh, iks ) {
+
+		this.mesh = mesh;
+		this.iks = iks || [];
+
+		this._valid();
+
+	}
+
+	CCDIKSolver.prototype = {
+
+		constructor: CCDIKSolver,
+
+		/**
+		 * Update IK bones.
+		 *
+		 * @return {CCDIKSolver}
+		 */
+		update: function () {
+
+			var q = new Quaternion();
+			var targetPos = new Vector3();
+			var targetVec = new Vector3();
+			var effectorPos = new Vector3();
+			var effectorVec = new Vector3();
+			var linkPos = new Vector3();
+			var invLinkQ = new Quaternion();
+			var linkScale = new Vector3();
+			var axis = new Vector3();
+			var vector = new Vector3();
+
+			return function update() {
+
+				var bones = this.mesh.skeleton.bones;
+				var iks = this.iks;
+
+				// for reference overhead reduction in loop
+				var math = Math;
+
+				for ( var i = 0, il = iks.length; i < il; i ++ ) {
+
+					var ik = iks[ i ];
+					var effector = bones[ ik.effector ];
+					var target = bones[ ik.target ];
+
+					// don't use getWorldPosition() here for the performance
+					// because it calls updateMatrixWorld( true ) inside.
+					targetPos.setFromMatrixPosition( target.matrixWorld );
+
+					var links = ik.links;
+					var iteration = ik.iteration !== undefined ? ik.iteration : 1;
+
+					for ( var j = 0; j < iteration; j ++ ) {
+
+						var rotated = false;
+
+						for ( var k = 0, kl = links.length; k < kl; k ++ ) {
+
+							var link = bones[ links[ k ].index ];
+
+							// skip this link and following links.
+							// this skip is used for MMD performance optimization.
+							if ( links[ k ].enabled === false ) break;
+
+							var limitation = links[ k ].limitation;
+							var rotationMin = links[ k ].rotationMin;
+							var rotationMax = links[ k ].rotationMax;
+
+							// don't use getWorldPosition/Quaternion() here for the performance
+							// because they call updateMatrixWorld( true ) inside.
+							link.matrixWorld.decompose( linkPos, invLinkQ, linkScale );
+							invLinkQ.inverse();
+							effectorPos.setFromMatrixPosition( effector.matrixWorld );
+
+							// work in link world
+							effectorVec.subVectors( effectorPos, linkPos );
+							effectorVec.applyQuaternion( invLinkQ );
+							effectorVec.normalize();
+
+							targetVec.subVectors( targetPos, linkPos );
+							targetVec.applyQuaternion( invLinkQ );
+							targetVec.normalize();
+
+							var angle = targetVec.dot( effectorVec );
+
+							if ( angle > 1.0 ) {
+
+								angle = 1.0;
+
+							} else if ( angle < - 1.0 ) {
+
+								angle = - 1.0;
+
+							}
+
+							angle = math.acos( angle );
+
+							// skip if changing angle is too small to prevent vibration of bone
+							// Refer to http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mmd.three.js
+							if ( angle < 1e-5 ) continue;
+
+							if ( ik.minAngle !== undefined && angle < ik.minAngle ) {
+
+								angle = ik.minAngle;
+
+							}
+
+							if ( ik.maxAngle !== undefined && angle > ik.maxAngle ) {
+
+								angle = ik.maxAngle;
+
+							}
+
+							axis.crossVectors( effectorVec, targetVec );
+							axis.normalize();
+
+							q.setFromAxisAngle( axis, angle );
+							link.quaternion.multiply( q );
+
+							// TODO: re-consider the limitation specification
+							if ( limitation !== undefined ) {
+
+								var c = link.quaternion.w;
+
+								if ( c > 1.0 ) c = 1.0;
+
+								var c2 = math.sqrt( 1 - c * c );
+								link.quaternion.set( limitation.x * c2,
+								                     limitation.y * c2,
+								                     limitation.z * c2,
+								                     c );
+
+							}
+
+							if ( rotationMin !== undefined ) {
+
+								link.rotation.setFromVector3(
+									link.rotation
+										.toVector3( vector )
+										.max( rotationMin ) );
+
+							}
+
+							if ( rotationMax !== undefined ) {
+
+								link.rotation.setFromVector3(
+									link.rotation
+										.toVector3( vector )
+										.min( rotationMax ) );
+
+							}
+
+							link.updateMatrixWorld( true );
+
+							rotated = true;
+
+						}
+
+						if ( ! rotated ) break;
+
+					}
+
+				}
+
+				return this;
+
+			};
+
+		}(),
+
+		/**
+		 * Creates Helper
+		 *
+		 * @return {CCDIKHelper}
+		 */
+		createHelper: function () {
+
+			return new CCDIKHelper( this.mesh, this.mesh.geometry.userData.MMD.iks );
+
+		},
+
+		// private methods
+
+		_valid: function () {
+
+			var iks = this.iks;
+			var bones = this.mesh.skeleton.bones;
+
+			for ( var i = 0, il = iks.length; i < il; i ++ ) {
+
+				var ik = iks[ i ];
+				var effector = bones[ ik.effector ];
+				var links = ik.links;
+				var link0, link1;
+
+				link0 = effector;
+
+				for ( var j = 0, jl = links.length; j < jl; j ++ ) {
+
+					link1 = bones[ links[ j ].index ];
+
+					if ( link0.parent !== link1 ) {
+
+						console.warn( 'THREE.CCDIKSolver: bone ' + link0.name + ' is not the child of bone ' + link1.name );
+
+					}
+
+					link0 = link1;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	/**
+	 * Visualize IK bones
+	 *
+	 * @param {SkinnedMesh} mesh
+	 * @param {Array<Object>} iks
+	 */
+	function CCDIKHelper( mesh, iks ) {
+
+		Object3D.call( this );
+
+		this.root = mesh;
+		this.iks = iks || [];
+
+		this.matrix.copy( mesh.matrixWorld );
+		this.matrixAutoUpdate = false;
+
+		this.sphereGeometry = new SphereBufferGeometry( 0.25, 16, 8 );
+
+		this.targetSphereMaterial = new MeshBasicMaterial( {
+			color: new Color( 0xff8888 ),
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
+
+		this.effectorSphereMaterial = new MeshBasicMaterial( {
+			color: new Color( 0x88ff88 ),
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
+
+		this.linkSphereMaterial = new MeshBasicMaterial( {
+			color: new Color( 0x8888ff ),
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
+
+		this.lineMaterial = new LineBasicMaterial( {
+			color: new Color( 0xff0000 ),
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
+
+		this._init();
+
+	}
+
+	CCDIKHelper.prototype = Object.assign( Object.create( Object3D.prototype ), {
+
+		constructor: CCDIKHelper,
+
+		/**
+		 * Updates IK bones visualization.
+		 */
+		updateMatrixWorld: function () {
+
+			var matrix = new Matrix4();
+			var vector = new Vector3();
+
+			function getPosition( bone, matrixWorldInv ) {
+
+				return vector
+					.setFromMatrixPosition( bone.matrixWorld )
+					.applyMatrix4( matrixWorldInv );
+
+			}
+
+			function setPositionOfBoneToAttributeArray( array, index, bone, matrixWorldInv ) {
+
+				var v = getPosition( bone, matrixWorldInv );
+
+				array[ index * 3 + 0 ] = v.x;
+				array[ index * 3 + 1 ] = v.y;
+				array[ index * 3 + 2 ] = v.z;
+
+			}
+
+			return function updateMatrixWorld( force ) {
+
+				var mesh = this.root;
+
+				if ( this.visible ) {
+
+					var offset = 0;
+
+					var iks = this.iks;
+					var bones = mesh.skeleton.bones;
+
+					matrix.getInverse( mesh.matrixWorld );
+
+					for ( var i = 0, il = iks.length; i < il; i ++ ) {
+
+						var ik = iks[ i ];
+
+						var targetBone = bones[ ik.target ];
+						var effectorBone = bones[ ik.effector ];
+
+						var targetMesh = this.children[ offset ++ ];
+						var effectorMesh = this.children[ offset ++ ];
+
+						targetMesh.position.copy( getPosition( targetBone, matrix ) );
+						effectorMesh.position.copy( getPosition( effectorBone, matrix ) );
+
+						for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
+
+							var link = ik.links[ j ];
+							var linkBone = bones[ link.index ];
+
+							var linkMesh = this.children[ offset ++ ];
+
+							linkMesh.position.copy( getPosition( linkBone, matrix ) );
+
+						}
+
+						var line = this.children[ offset ++ ];
+						var array = line.geometry.attributes.position.array;
+
+						setPositionOfBoneToAttributeArray( array, 0, targetBone, matrix );
+						setPositionOfBoneToAttributeArray( array, 1, effectorBone, matrix );
+
+						for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
+
+							var link = ik.links[ j ];
+							var linkBone = bones[ link.index ];
+							setPositionOfBoneToAttributeArray( array, j + 2, linkBone, matrix );
+
+						}
+
+						line.geometry.attributes.position.needsUpdate = true;
+
+					}
+
+				}
+
+				this.matrix.copy( mesh.matrixWorld );
+
+				Object3D.prototype.updateMatrixWorld.call( this, force );
+
+			};
+
+		}(),
+
+		// private method
+
+		_init: function () {
+
+			var self = this;
+			var iks = this.iks;
+
+			function createLineGeometry( ik ) {
+
+				var geometry = new BufferGeometry();
+				var vertices = new Float32Array( ( 2 + ik.links.length ) * 3 );
+				geometry.addAttribute( 'position', new BufferAttribute( vertices, 3 ) );
+
+				return geometry;
+
+			}
+
+			function createTargetMesh() {
+
+				return new Mesh( self.sphereGeometry, self.targetSphereMaterial );
+
+			}
+
+			function createEffectorMesh() {
+
+				return new Mesh( self.sphereGeometry, self.effectorSphereMaterial );
+
+			}
+
+			function createLinkMesh() {
+
+				return new Mesh( self.sphereGeometry, self.linkSphereMaterial );
+
+			}
+
+			function createLine( ik ) {
+
+				return new Line( createLineGeometry( ik ), self.lineMaterial );
+
+			}
+
+			for ( var i = 0, il = iks.length; i < il; i ++ ) {
+
+				var ik = iks[ i ];
+
+				this.add( createTargetMesh() );
+				this.add( createEffectorMesh() );
+
+				for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
+
+					this.add( createLinkMesh() );
+
+				}
+
+				this.add( createLine( ik ) );
+
+			}
+
+		}
+
+	} );
+
+	return CCDIKSolver;
+
+} )();
+
+export { CCDIKSolver };

+ 50 - 0
examples/jsm/loaders/AWDLoader.d.ts

@@ -0,0 +1,50 @@
+import {
+  Bone,
+  BufferGeometry,
+  LoadingManager,
+  Material,
+  Matrix4,
+  Mesh,
+  Object3D,
+  Texture
+} from '../../../src/Three';
+
+export class AWDLoader {
+  constructor(manager?: LoadingManager);
+  manager: LoadingManager;
+  materialFactory: any;
+  path: string;
+  trunk: Object3D;
+
+  getBlock(id: number): any;
+  load(url: string, onLoad: (result: Object3D) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  loadTexture(url: string): Texture;
+  setPath(path: string): this;
+  parse(data: ArrayBuffer): Object3D;
+  parseAnimatorSet(): object;
+  parseAttrValue(type: number, value: number): any;
+  parseContainer(): Object3D;
+  parseMaterial(): Material;
+  parseMatrix4(): Matrix4;
+  parseMeshData(): BufferGeometry[];
+  parseMeshInstance(): Mesh;
+  parseMeshPoseAnimation(poseOnly: boolean): null;
+  parseNextBlock(): void;
+  parseProperties(expected: object): object;
+  parseSkeleton(): Bone[];
+  parseSkeletonAnimation(): object[];
+  parseSkeletonPose(): Matrix4[];
+  parseTexture(): Texture;
+  parseUserAttributes(): null;
+  parseVertexAnimationSet(): object[];
+  readU8(): number;
+  readI8(): number;
+  readU16(): number;
+  readI16(): number;
+  readU32(): number;
+  readI32(): number;
+  readF32(): number;
+  readF64(): number;
+  readUTF(): string;
+  readUTFBytes(len: number): string;
+}

+ 1234 - 0
examples/jsm/loaders/AWDLoader.js

@@ -0,0 +1,1234 @@
+/**
+ * Author: Pierre Lepers
+ * Date: 09/12/2013 17:21
+ */
+
+import {
+	Bone,
+	BufferAttribute,
+	BufferGeometry,
+	DefaultLoadingManager,
+	FileLoader,
+	ImageLoader,
+	Matrix4,
+	Mesh,
+	MeshPhongMaterial,
+	Object3D,
+	Texture
+} from "../../../build/three.module.js";
+
+var AWDLoader = ( function () {
+
+	var //UNCOMPRESSED = 0,
+		//DEFLATE = 1,
+		//LZMA = 2,
+
+		AWD_FIELD_INT8 = 1,
+		AWD_FIELD_INT16 = 2,
+		AWD_FIELD_INT32 = 3,
+		AWD_FIELD_UINT8 = 4,
+		AWD_FIELD_UINT16 = 5,
+		AWD_FIELD_UINT32 = 6,
+		AWD_FIELD_FLOAT32 = 7,
+		AWD_FIELD_FLOAT64 = 8,
+		AWD_FIELD_BOOL = 21,
+		//AWD_FIELD_COLOR = 22,
+		AWD_FIELD_BADDR = 23,
+		//AWD_FIELD_STRING = 31,
+		//AWD_FIELD_BYTEARRAY = 32,
+		AWD_FIELD_VECTOR2x1 = 41,
+		AWD_FIELD_VECTOR3x1 = 42,
+		AWD_FIELD_VECTOR4x1 = 43,
+		AWD_FIELD_MTX3x2 = 44,
+		AWD_FIELD_MTX3x3 = 45,
+		AWD_FIELD_MTX4x3 = 46,
+		AWD_FIELD_MTX4x4 = 47,
+
+		BOOL = 21,
+		//COLOR = 22,
+		BADDR = 23,
+
+		//INT8 = 1,
+		//INT16 = 2,
+		//INT32 = 3,
+		UINT8 = 4,
+		UINT16 = 5,
+		//UINT32 = 6,
+		FLOAT32 = 7,
+		FLOAT64 = 8;
+
+	var littleEndian = true;
+
+	function Block() {
+
+		this.id = 0;
+		this.data = null;
+
+	}
+
+	function AWDProperties() {}
+
+	AWDProperties.prototype = {
+		set: function ( key, value ) {
+
+			this[ key ] = value;
+
+		},
+
+		get: function ( key, fallback ) {
+
+			if ( this.hasOwnProperty( key ) ) {
+
+				return this[ key ];
+
+			} else {
+
+				return fallback;
+
+			}
+
+		}
+	};
+
+	var AWDLoader = function ( manager ) {
+
+		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+
+		this.trunk = new Object3D();
+
+		this.materialFactory = undefined;
+
+		this._url = '';
+		this._baseDir = '';
+
+		this._data = undefined;
+		this._ptr = 0;
+
+		this._version = [];
+		this._streaming = false;
+		this._optimized_for_accuracy = false;
+		this._compression = 0;
+		this._bodylen = 0xFFFFFFFF;
+
+		this._blocks = [ new Block() ];
+
+		this._accuracyMatrix = false;
+		this._accuracyGeo = false;
+		this._accuracyProps = false;
+
+	};
+
+	AWDLoader.prototype = {
+
+		constructor: AWDLoader,
+
+		load: function ( url, onLoad, onProgress, onError ) {
+
+			var scope = this;
+
+			this._url = url;
+			this._baseDir = url.substr( 0, url.lastIndexOf( '/' ) + 1 );
+
+			var loader = new FileLoader( this.manager );
+			loader.setPath( this.path );
+			loader.setResponseType( 'arraybuffer' );
+			loader.load( url, function ( text ) {
+
+				onLoad( scope.parse( text ) );
+
+			}, onProgress, onError );
+
+		},
+
+		setPath: function ( value ) {
+
+			this.path = value;
+			return this;
+
+		},
+
+		parse: function ( data ) {
+
+			var blen = data.byteLength;
+
+			this._ptr = 0;
+			this._data = new DataView( data );
+
+			this._parseHeader( );
+
+			if ( this._compression != 0 ) {
+
+				console.error( 'compressed AWD not supported' );
+
+			}
+
+			if ( ! this._streaming && this._bodylen != data.byteLength - this._ptr ) {
+
+				console.error( 'AWDLoader: body len does not match file length', this._bodylen, blen - this._ptr );
+
+			}
+
+			while ( this._ptr < blen ) {
+
+				this.parseNextBlock();
+
+			}
+
+			return this.trunk;
+
+		},
+
+		parseNextBlock: function () {
+
+			var assetData,
+				ns, type, len, block,
+				blockId = this.readU32(),
+				ns = this.readU8(),
+				type = this.readU8(),
+				flags = this.readU8(),
+				len = this.readU32();
+
+
+			switch ( type ) {
+
+				case 1:
+					assetData = this.parseMeshData();
+					break;
+
+				case 22:
+					assetData = this.parseContainer();
+					break;
+
+				case 23:
+					assetData = this.parseMeshInstance();
+					break;
+
+				case 81:
+					assetData = this.parseMaterial();
+					break;
+
+				case 82:
+					assetData = this.parseTexture();
+					break;
+
+				case 101:
+					assetData = this.parseSkeleton();
+					break;
+
+				case 112:
+					assetData = this.parseMeshPoseAnimation( false );
+					break;
+
+				case 113:
+					assetData = this.parseVertexAnimationSet();
+					break;
+
+				case 102:
+					assetData = this.parseSkeletonPose();
+					break;
+
+				case 103:
+					assetData = this.parseSkeletonAnimation();
+					break;
+
+				case 122:
+					assetData = this.parseAnimatorSet();
+					break;
+
+				default:
+					//debug('Ignoring block!',type, len);
+					this._ptr += len;
+					break;
+
+			}
+
+
+			// Store block reference for later use
+			this._blocks[ blockId ] = block = new Block();
+			block.data = assetData;
+			block.id = blockId;
+
+
+		},
+
+		_parseHeader: function () {
+
+			var version = this._version,
+				awdmagic = ( this.readU8() << 16 ) | ( this.readU8() << 8 ) | this.readU8();
+
+			if ( awdmagic != 4282180 )
+				throw new Error( "AWDLoader - bad magic" );
+
+			version[ 0 ] = this.readU8();
+			version[ 1 ] = this.readU8();
+
+			var flags = this.readU16();
+
+			this._streaming = ( flags & 0x1 ) == 0x1;
+
+			if ( ( version[ 0 ] === 2 ) && ( version[ 1 ] === 1 ) ) {
+
+				this._accuracyMatrix = ( flags & 0x2 ) === 0x2;
+				this._accuracyGeo = ( flags & 0x4 ) === 0x4;
+				this._accuracyProps = ( flags & 0x8 ) === 0x8;
+
+			}
+
+			this._geoNrType = this._accuracyGeo ? FLOAT64 : FLOAT32;
+			this._matrixNrType = this._accuracyMatrix ? FLOAT64 : FLOAT32;
+			this._propsNrType = this._accuracyProps ? FLOAT64 : FLOAT32;
+
+			this._optimized_for_accuracy = ( flags & 0x2 ) === 0x2;
+
+			this._compression = this.readU8();
+			this._bodylen = this.readU32();
+
+		},
+
+		parseContainer: function () {
+
+			var parent,
+				ctr = new Object3D(),
+				par_id = this.readU32(),
+				mtx = this.parseMatrix4();
+
+			ctr.name = this.readUTF();
+			ctr.applyMatrix( mtx );
+
+			parent = this._blocks[ par_id ].data || this.trunk;
+			parent.add( ctr );
+
+			this.parseProperties( {
+				1: this._matrixNrType,
+				2: this._matrixNrType,
+				3: this._matrixNrType,
+				4: UINT8
+			} );
+
+			ctr.extra = this.parseUserAttributes();
+
+			return ctr;
+
+		},
+
+		parseMeshInstance: function () {
+
+			var name,
+				mesh, geometries, meshLen, meshes,
+				par_id, data_id,
+				mtx,
+				materials, mat, mat_id,
+				num_materials,
+				parent,
+				i;
+
+			par_id = this.readU32();
+			mtx = this.parseMatrix4();
+			name = this.readUTF();
+			data_id = this.readU32();
+			num_materials = this.readU16();
+
+			geometries = this.getBlock( data_id );
+
+			materials = [];
+
+			for ( i = 0; i < num_materials; i ++ ) {
+
+				mat_id = this.readU32();
+				mat = this.getBlock( mat_id );
+				materials.push( mat );
+
+			}
+
+			meshLen = geometries.length;
+			meshes = [];
+
+			// TODO : BufferGeometry don't support "geometryGroups" for now.
+			// so we create sub meshes for each groups
+			if ( meshLen > 1 ) {
+
+				mesh = new Object3D();
+				for ( i = 0; i < meshLen; i ++ ) {
+
+					var sm = new Mesh( geometries[ i ] );
+					meshes.push( sm );
+					mesh.add( sm );
+
+				}
+
+			} else {
+
+				mesh = new Mesh( geometries[ 0 ] );
+				meshes.push( mesh );
+
+			}
+
+			mesh.applyMatrix( mtx );
+			mesh.name = name;
+
+
+			parent = this.getBlock( par_id ) || this.trunk;
+			parent.add( mesh );
+
+
+			var matLen = materials.length;
+			var maxLen = Math.max( meshLen, matLen );
+			for ( i = 0; i < maxLen; i ++ )
+				meshes[ i % meshLen ].material = materials[ i % matLen ];
+
+
+			// Ignore for now
+			this.parseProperties( null );
+			mesh.extra = this.parseUserAttributes();
+
+			return mesh;
+
+		},
+
+		parseMaterial: function () {
+
+			var name,
+				type,
+				props,
+				mat,
+				attributes,
+				num_methods,
+				methods_parsed;
+
+			name = this.readUTF();
+			type = this.readU8();
+			num_methods = this.readU8();
+
+			//log( "AWDLoader parseMaterial ",name )
+
+			// Read material numerical properties
+			// (1=color, 2=bitmap url, 11=alpha_blending, 12=alpha_threshold, 13=repeat)
+			props = this.parseProperties( {
+				1: AWD_FIELD_INT32,
+				2: AWD_FIELD_BADDR,
+				11: AWD_FIELD_BOOL,
+				12: AWD_FIELD_FLOAT32,
+				13: AWD_FIELD_BOOL
+			} );
+
+			methods_parsed = 0;
+
+			while ( methods_parsed < num_methods ) {
+
+				var method_type = this.readU16();
+				this.parseProperties( null );
+				this.parseUserAttributes();
+
+			}
+
+			attributes = this.parseUserAttributes();
+
+			if ( this.materialFactory !== undefined ) {
+
+				mat = this.materialFactory( name );
+				if ( mat ) return mat;
+
+			}
+
+			mat = new MeshPhongMaterial();
+
+			if ( type === 1 ) {
+
+				// Color material
+				mat.color.setHex( props.get( 1, 0xcccccc ) );
+
+			} else if ( type === 2 ) {
+
+				// Bitmap material
+				var tex_addr = props.get( 2, 0 );
+				mat.map = this.getBlock( tex_addr );
+
+			}
+
+			mat.extra = attributes;
+			mat.alphaThreshold = props.get( 12, 0.0 );
+			mat.repeat = props.get( 13, false );
+
+
+			return mat;
+
+		},
+
+		parseTexture: function () {
+
+			var name = this.readUTF(),
+				type = this.readU8(),
+				asset,
+				data_len;
+
+			// External
+			if ( type === 0 ) {
+
+				data_len = this.readU32();
+				var url = this.readUTFBytes( data_len );
+				console.log( url );
+
+				asset = this.loadTexture( url );
+
+			} else {
+				// embed texture not supported
+			}
+			// Ignore for now
+			this.parseProperties( null );
+
+			this.parseUserAttributes();
+			return asset;
+
+		},
+
+		loadTexture: function ( url ) {
+
+			var tex = new Texture();
+
+			var loader = new ImageLoader( this.manager );
+
+			loader.load( this._baseDir + url, function ( image ) {
+
+				tex.image = image;
+				tex.needsUpdate = true;
+
+			} );
+
+			return tex;
+
+		},
+
+		parseSkeleton: function () {
+
+			// Array<Bone>
+			var name = this.readUTF(),
+				num_joints = this.readU16(),
+				skeleton = [],
+				joints_parsed = 0;
+
+			this.parseProperties( null );
+
+			while ( joints_parsed < num_joints ) {
+
+				var joint, ibp;
+
+				// Ignore joint id
+				this.readU16();
+
+				joint = new Bone();
+				joint.parent = this.readU16() - 1; // 0=null in AWD
+				joint.name = this.readUTF();
+
+				ibp = this.parseMatrix4();
+				joint.skinMatrix = ibp;
+
+				// Ignore joint props/attributes for now
+				this.parseProperties( null );
+				this.parseUserAttributes();
+
+				skeleton.push( joint );
+				joints_parsed ++;
+
+			}
+
+			// Discard attributes for now
+			this.parseUserAttributes();
+
+
+			return skeleton;
+
+		},
+
+		parseSkeletonPose: function () {
+
+			var name = this.readUTF();
+
+			var num_joints = this.readU16();
+			this.parseProperties( null );
+
+			// debug( 'parse Skeleton Pose. joints : ' + num_joints);
+
+			var pose = [];
+
+			var joints_parsed = 0;
+
+			while ( joints_parsed < num_joints ) {
+
+				var has_transform; //:uint;
+				var mtx_data;
+
+				has_transform = this.readU8();
+
+				if ( has_transform === 1 ) {
+
+					mtx_data = this.parseMatrix4();
+
+				} else {
+
+					mtx_data = new Matrix4();
+
+				}
+				pose[ joints_parsed ] = mtx_data;
+				joints_parsed ++;
+
+			}
+
+			// Skip attributes for now
+			this.parseUserAttributes();
+
+			return pose;
+
+		},
+
+		parseSkeletonAnimation: function () {
+
+			var frame_dur;
+			var pose_addr;
+			var pose;
+
+			var name = this.readUTF();
+
+			var clip = [];
+
+			var num_frames = this.readU16();
+			this.parseProperties( null );
+
+			var frames_parsed = 0;
+
+			// debug( 'parse Skeleton Animation. frames : ' + num_frames);
+
+			while ( frames_parsed < num_frames ) {
+
+				pose_addr = this.readU32();
+				frame_dur = this.readU16();
+
+				pose = this._blocks[ pose_addr ].data;
+				// debug( 'pose address ',pose[2].elements[12],pose[2].elements[13],pose[2].elements[14] );
+				clip.push( {
+					pose: pose,
+					duration: frame_dur
+				} );
+
+				frames_parsed ++;
+
+			}
+
+			if ( clip.length === 0 ) {
+
+				// debug("Could not this SkeletonClipNode, because no Frames where set.");
+				return;
+
+			}
+			// Ignore attributes for now
+			this.parseUserAttributes();
+			return clip;
+
+		},
+
+		parseVertexAnimationSet: function () {
+
+			var poseBlockAdress,
+				name = this.readUTF(),
+				num_frames = this.readU16(),
+				props = this.parseProperties( { 1: UINT16 } ),
+				frames_parsed = 0,
+				skeletonFrames = [];
+
+			while ( frames_parsed < num_frames ) {
+
+				poseBlockAdress = this.readU32();
+				skeletonFrames.push( this._blocks[ poseBlockAdress ].data );
+				frames_parsed ++;
+
+			}
+
+			this.parseUserAttributes();
+
+
+			return skeletonFrames;
+
+		},
+
+		parseAnimatorSet: function () {
+
+			var animSetBlockAdress; //:int
+
+			var targetAnimationSet; //:AnimationSetBase;
+			var name = this.readUTF();
+			var type = this.readU16();
+
+			var props = this.parseProperties( { 1: BADDR } );
+
+			animSetBlockAdress = this.readU32();
+			var targetMeshLength = this.readU16();
+
+			var meshAdresses = []; //:Vector.<uint> = new Vector.<uint>;
+
+			for ( var i = 0; i < targetMeshLength; i ++ )
+				meshAdresses.push( this.readU32() );
+
+			var activeState = this.readU16();
+			var autoplay = Boolean( this.readU8() );
+			this.parseUserAttributes();
+			this.parseUserAttributes();
+
+			var targetMeshes = []; //:Vector.<Mesh> = new Vector.<Mesh>;
+
+			for ( i = 0; i < meshAdresses.length; i ++ ) {
+
+				//			returnedArray = getAssetByID(meshAdresses[i], [AssetType.MESH]);
+				//			if (returnedArray[0])
+				targetMeshes.push( this._blocks[ meshAdresses[ i ] ].data );
+
+			}
+
+			targetAnimationSet = this._blocks[ animSetBlockAdress ].data;
+			var thisAnimator;
+
+			if ( type == 1 ) {
+
+
+				thisAnimator = {
+					animationSet: targetAnimationSet,
+					skeleton: this._blocks[ props.get( 1, 0 ) ].data
+				};
+
+			} else if ( type == 2 ) {
+				// debug( "vertex Anim???");
+			}
+
+
+			for ( i = 0; i < targetMeshes.length; i ++ ) {
+
+				targetMeshes[ i ].animator = thisAnimator;
+
+			}
+			// debug("Parsed a Animator: Name = " + name);
+
+			return thisAnimator;
+
+		},
+
+		parseMeshData: function () {
+
+			var name = this.readUTF(),
+				num_subs = this.readU16(),
+				geom,
+				subs_parsed = 0,
+				buffer,
+				geometries = [];
+
+			// Ignore for now
+			this.parseProperties( { 1: this._geoNrType, 2: this._geoNrType } );
+
+			// Loop through sub meshes
+			while ( subs_parsed < num_subs ) {
+
+				var sm_len, sm_end, attrib;
+
+				geom = new BufferGeometry();
+				geom.name = name;
+				geometries.push( geom );
+
+
+				sm_len = this.readU32();
+				sm_end = this._ptr + sm_len;
+
+
+				// Ignore for now
+				this.parseProperties( { 1: this._geoNrType, 2: this._geoNrType } );
+
+				// Loop through data streams
+				while ( this._ptr < sm_end ) {
+
+					var idx = 0,
+						str_type = this.readU8(),
+						str_ftype = this.readU8(),
+						str_len = this.readU32(),
+						str_end = str_len + this._ptr;
+
+					if ( str_type === 1 ) {
+
+						// VERTICES
+
+						buffer = new Float32Array( ( str_len / 12 ) * 3 );
+						attrib = new BufferAttribute( buffer, 3 );
+
+						geom.addAttribute( 'position', attrib );
+						idx = 0;
+
+						while ( this._ptr < str_end ) {
+
+							buffer[ idx ] = - this.readF32();
+							buffer[ idx + 1 ] = this.readF32();
+							buffer[ idx + 2 ] = this.readF32();
+							idx += 3;
+
+						}
+
+					} else if ( str_type === 2 ) {
+
+						// INDICES
+
+						buffer = new Uint16Array( str_len / 2 );
+						attrib = new BufferAttribute( buffer, 1 );
+						geom.setIndex( attrib );
+
+						idx = 0;
+
+						while ( this._ptr < str_end ) {
+
+							buffer[ idx + 1 ] = this.readU16();
+							buffer[ idx ] = this.readU16();
+							buffer[ idx + 2 ] = this.readU16();
+							idx += 3;
+
+						}
+
+					} else if ( str_type === 3 ) {
+
+						// UVS
+
+						buffer = new Float32Array( ( str_len / 8 ) * 2 );
+						attrib = new BufferAttribute( buffer, 2 );
+
+						geom.addAttribute( 'uv', attrib );
+						idx = 0;
+
+						while ( this._ptr < str_end ) {
+
+							buffer[ idx ] = this.readF32();
+							buffer[ idx + 1 ] = 1.0 - this.readF32();
+							idx += 2;
+
+						}
+
+					} else if ( str_type === 4 ) {
+
+						// NORMALS
+
+						buffer = new Float32Array( ( str_len / 12 ) * 3 );
+						attrib = new BufferAttribute( buffer, 3 );
+						geom.addAttribute( 'normal', attrib );
+						idx = 0;
+
+						while ( this._ptr < str_end ) {
+
+							buffer[ idx ] = - this.readF32();
+							buffer[ idx + 1 ] = this.readF32();
+							buffer[ idx + 2 ] = this.readF32();
+							idx += 3;
+
+						}
+
+					} else {
+
+						this._ptr = str_end;
+
+					}
+
+				}
+
+				this.parseUserAttributes();
+
+				geom.computeBoundingSphere();
+				subs_parsed ++;
+
+			}
+
+			//geom.computeFaceNormals();
+
+			this.parseUserAttributes();
+			//finalizeAsset(geom, name);
+
+			return geometries;
+
+		},
+
+		parseMeshPoseAnimation: function ( poseOnly ) {
+
+			var num_frames = 1,
+				num_submeshes,
+				frames_parsed,
+				subMeshParsed,
+
+				str_len,
+				str_end,
+				geom,
+				idx = 0,
+				clip = {},
+				num_Streams,
+				streamsParsed,
+				streamtypes = [],
+
+				props,
+				name = this.readUTF(),
+				geoAdress = this.readU32();
+
+			var mesh = this.getBlock( geoAdress );
+
+			if ( mesh === null ) {
+
+				console.log( "parseMeshPoseAnimation target mesh not found at:", geoAdress );
+				return;
+
+			}
+
+			geom = mesh.geometry;
+			geom.morphTargets = [];
+
+			if ( ! poseOnly )
+				num_frames = this.readU16();
+
+			num_submeshes = this.readU16();
+			num_Streams = this.readU16();
+
+			// debug("VA num_frames : ", num_frames );
+			// debug("VA num_submeshes : ", num_submeshes );
+			// debug("VA numstreams : ", num_Streams );
+
+			streamsParsed = 0;
+			while ( streamsParsed < num_Streams ) {
+
+				streamtypes.push( this.readU16() );
+				streamsParsed ++;
+
+			}
+			props = this.parseProperties( { 1: BOOL, 2: BOOL } );
+
+			clip.looping = props.get( 1, true );
+			clip.stitchFinalFrame = props.get( 2, false );
+
+			frames_parsed = 0;
+
+			while ( frames_parsed < num_frames ) {
+
+				this.readU16();
+				subMeshParsed = 0;
+
+				while ( subMeshParsed < num_submeshes ) {
+
+					streamsParsed = 0;
+					str_len = this.readU32();
+					str_end = this._ptr + str_len;
+
+					while ( streamsParsed < num_Streams ) {
+
+						if ( streamtypes[ streamsParsed ] === 1 ) {
+
+							//geom.addAttribute( 'morphTarget'+frames_parsed, Float32Array, str_len/12, 3 );
+							var buffer = new Float32Array( str_len / 4 );
+							geom.morphTargets.push( {
+								array: buffer
+							} );
+
+							//buffer = geom.attributes['morphTarget'+frames_parsed].array
+							idx = 0;
+
+							while ( this._ptr < str_end ) {
+
+								buffer[ idx ] = this.readF32();
+								buffer[ idx + 1 ] = this.readF32();
+								buffer[ idx + 2 ] = this.readF32();
+								idx += 3;
+
+							}
+
+
+							subMeshParsed ++;
+
+						} else
+							this._ptr = str_end;
+						streamsParsed ++;
+
+					}
+
+				}
+
+
+				frames_parsed ++;
+
+			}
+
+			this.parseUserAttributes();
+
+			return null;
+
+		},
+
+		getBlock: function ( id ) {
+
+			return this._blocks[ id ].data;
+
+		},
+
+		parseMatrix4: function () {
+
+			var mtx = new Matrix4();
+			var e = mtx.elements;
+
+			e[ 0 ] = this.readF32();
+			e[ 1 ] = this.readF32();
+			e[ 2 ] = this.readF32();
+			e[ 3 ] = 0.0;
+			//e[3] = 0.0;
+
+			e[ 4 ] = this.readF32();
+			e[ 5 ] = this.readF32();
+			e[ 6 ] = this.readF32();
+			//e[7] = this.readF32();
+			e[ 7 ] = 0.0;
+
+			e[ 8 ] = this.readF32();
+			e[ 9 ] = this.readF32();
+			e[ 10 ] = this.readF32();
+			//e[11] = this.readF32();
+			e[ 11 ] = 0.0;
+
+			e[ 12 ] = - this.readF32();
+			e[ 13 ] = this.readF32();
+			e[ 14 ] = this.readF32();
+			//e[15] = this.readF32();
+			e[ 15 ] = 1.0;
+			return mtx;
+
+		},
+
+		parseProperties: function ( expected ) {
+
+			var list_len = this.readU32();
+			var list_end = this._ptr + list_len;
+
+			var props = new AWDProperties();
+
+			if ( expected ) {
+
+				while ( this._ptr < list_end ) {
+
+					var key = this.readU16();
+					var len = this.readU32();
+					var type;
+
+					if ( expected.hasOwnProperty( key ) ) {
+
+						type = expected[ key ];
+						props.set( key, this.parseAttrValue( type, len ) );
+
+					} else {
+
+						this._ptr += len;
+
+					}
+
+				}
+
+			}
+
+			return props;
+
+		},
+
+		parseUserAttributes: function () {
+
+			// skip for now
+			this._ptr = this.readU32() + this._ptr;
+			return null;
+
+		},
+
+		parseAttrValue: function ( type, len ) {
+
+			var elem_len;
+			var read_func;
+
+			switch ( type ) {
+
+				case AWD_FIELD_INT8:
+					elem_len = 1;
+					read_func = this.readI8;
+					break;
+
+				case AWD_FIELD_INT16:
+					elem_len = 2;
+					read_func = this.readI16;
+					break;
+
+				case AWD_FIELD_INT32:
+					elem_len = 4;
+					read_func = this.readI32;
+					break;
+
+				case AWD_FIELD_BOOL:
+				case AWD_FIELD_UINT8:
+					elem_len = 1;
+					read_func = this.readU8;
+					break;
+
+				case AWD_FIELD_UINT16:
+					elem_len = 2;
+					read_func = this.readU16;
+					break;
+
+				case AWD_FIELD_UINT32:
+				case AWD_FIELD_BADDR:
+					elem_len = 4;
+					read_func = this.readU32;
+					break;
+
+				case AWD_FIELD_FLOAT32:
+					elem_len = 4;
+					read_func = this.readF32;
+					break;
+
+				case AWD_FIELD_FLOAT64:
+					elem_len = 8;
+					read_func = this.readF64;
+					break;
+
+				case AWD_FIELD_VECTOR2x1:
+				case AWD_FIELD_VECTOR3x1:
+				case AWD_FIELD_VECTOR4x1:
+				case AWD_FIELD_MTX3x2:
+				case AWD_FIELD_MTX3x3:
+				case AWD_FIELD_MTX4x3:
+				case AWD_FIELD_MTX4x4:
+					elem_len = 8;
+					read_func = this.readF64;
+					break;
+
+			}
+
+			if ( elem_len < len ) {
+
+				var list;
+				var num_read;
+				var num_elems;
+
+				list = [];
+				num_read = 0;
+				num_elems = len / elem_len;
+
+				while ( num_read < num_elems ) {
+
+					list.push( read_func.call( this ) );
+					num_read ++;
+
+				}
+
+				return list;
+
+			} else {
+
+				return read_func.call( this );
+
+			}
+
+		},
+
+		readU8: function () {
+
+			return this._data.getUint8( this._ptr ++ );
+
+		},
+		readI8: function () {
+
+			return this._data.getInt8( this._ptr ++ );
+
+		},
+		readU16: function () {
+
+			var a = this._data.getUint16( this._ptr, littleEndian );
+			this._ptr += 2;
+			return a;
+
+		},
+		readI16: function () {
+
+			var a = this._data.getInt16( this._ptr, littleEndian );
+			this._ptr += 2;
+			return a;
+
+		},
+		readU32: function () {
+
+			var a = this._data.getUint32( this._ptr, littleEndian );
+			this._ptr += 4;
+			return a;
+
+		},
+		readI32: function () {
+
+			var a = this._data.getInt32( this._ptr, littleEndian );
+			this._ptr += 4;
+			return a;
+
+		},
+		readF32: function () {
+
+			var a = this._data.getFloat32( this._ptr, littleEndian );
+			this._ptr += 4;
+			return a;
+
+		},
+		readF64: function () {
+
+			var a = this._data.getFloat64( this._ptr, littleEndian );
+			this._ptr += 8;
+			return a;
+
+		},
+
+		/**
+	 * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode.
+	 * @param {Array.<number>} bytes UTF-8 byte array.
+	 * @return {string} 16-bit Unicode string.
+	 */
+		readUTF: function () {
+
+			var len = this.readU16();
+			return this.readUTFBytes( len );
+
+		},
+
+		/**
+		 * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode.
+		 * @param {Array.<number>} bytes UTF-8 byte array.
+		 * @return {string} 16-bit Unicode string.
+		 */
+		readUTFBytes: function ( len ) {
+
+			// TODO(user): Use native implementations if/when available
+			var out = [], c = 0;
+
+			while ( out.length < len ) {
+
+				var c1 = this._data.getUint8( this._ptr ++, littleEndian );
+				if ( c1 < 128 ) {
+
+					out[ c ++ ] = String.fromCharCode( c1 );
+
+				} else if ( c1 > 191 && c1 < 224 ) {
+
+					var c2 = this._data.getUint8( this._ptr ++, littleEndian );
+					out[ c ++ ] = String.fromCharCode( ( c1 & 31 ) << 6 | c2 & 63 );
+
+				} else {
+
+					var c2 = this._data.getUint8( this._ptr ++, littleEndian );
+					var c3 = this._data.getUint8( this._ptr ++, littleEndian );
+					out[ c ++ ] = String.fromCharCode( ( c1 & 15 ) << 12 | ( c2 & 63 ) << 6 | c3 & 63 );
+
+				}
+
+			}
+			return out.join( '' );
+
+		}
+
+	};
+
+	return AWDLoader;
+
+} )();
+
+export { AWDLoader };

+ 21 - 0
examples/jsm/loaders/DRACOLoader.d.ts

@@ -0,0 +1,21 @@
+import {
+  LoadingManager,
+  BufferGeometry,
+  TrianglesDrawModes
+} from '../../../src/Three';
+
+export class DRACOLoader {
+  constructor(manager?: LoadingManager);
+
+  static setDecoderPath(path: string): void;
+  static setDecoderConfig(config: object): void;
+  static getDecoderModule(): Promise<any>;
+  static releaseDecoderModule(): void;
+
+  load(url: string, onLoad: (geometry: BufferGeometry) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  setPath(path: string): DRACOLoader;
+  setVerbosity(level: number): DRACOLoader;
+  setDrawMode(drawMode: TrianglesDrawModes): DRACOLoader;
+  setSkipDequantization(attributeName: 'position', skip?: boolean): DRACOLoader;
+  isVersionSupported(version: number, callback: (isVersionSupported: boolean) => any): void;
+}

+ 740 - 0
examples/jsm/loaders/DRACOLoader.js

@@ -0,0 +1,740 @@
+/** Copyright 2016 The Draco Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+	BufferGeometry,
+	DefaultLoadingManager,
+	FileLoader,
+	Float32BufferAttribute,
+	Int16BufferAttribute,
+	Int32BufferAttribute,
+	Int8BufferAttribute,
+	TriangleStripDrawMode,
+	TrianglesDrawMode,
+	Uint16BufferAttribute,
+	Uint32BufferAttribute,
+	Uint8BufferAttribute
+} from "../../../build/three.module.js";
+
+/**
+ * @param {THREE.LoadingManager} manager
+ */
+var DRACOLoader = function ( manager ) {
+
+	this.timeLoaded = 0;
+	this.manager = manager || DefaultLoadingManager;
+	this.materials = null;
+	this.verbosity = 0;
+	this.attributeOptions = {};
+	this.drawMode = TrianglesDrawMode;
+	// Native Draco attribute type to Three.JS attribute type.
+	this.nativeAttributeMap = {
+		position: "POSITION",
+		normal: "NORMAL",
+		color: "COLOR",
+		uv: "TEX_COORD"
+	};
+
+};
+
+DRACOLoader.prototype = {
+	constructor: DRACOLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+		var loader = new FileLoader( scope.manager );
+		loader.setPath( this.path );
+		loader.setResponseType( "arraybuffer" );
+		loader.load(
+			url,
+			function ( blob ) {
+
+				scope.decodeDracoFile( blob, onLoad );
+
+			},
+			onProgress,
+			onError
+		);
+
+	},
+
+	setPath: function ( value ) {
+
+		this.path = value;
+		return this;
+
+	},
+
+	setVerbosity: function ( level ) {
+
+		this.verbosity = level;
+		return this;
+
+	},
+
+	/**
+	 *  Sets desired mode for generated geometry indices.
+	 *  Can be either:
+	 *      TrianglesDrawMode
+	 *      TriangleStripDrawMode
+	 */
+	setDrawMode: function ( drawMode ) {
+
+		this.drawMode = drawMode;
+		return this;
+
+	},
+
+	/**
+	 * Skips dequantization for a specific attribute.
+	 * |attributeName| is the js name of the given attribute type.
+	 * The only currently supported |attributeName| is 'position', more may be
+	 * added in future.
+	 */
+	setSkipDequantization: function ( attributeName, skip ) {
+
+		var skipDequantization = true;
+		if ( typeof skip !== "undefined" ) skipDequantization = skip;
+		this.getAttributeOptions(
+			attributeName
+		).skipDequantization = skipDequantization;
+		return this;
+
+	},
+
+	/**
+	 * Decompresses a Draco buffer. Names of attributes (for ID and type maps)
+	 * must be one of the supported three.js types, including: position, color,
+	 * normal, uv, uv2, skinIndex, skinWeight.
+	 *
+	 * @param {ArrayBuffer} rawBuffer
+	 * @param {Function} callback
+	 * @param {Object|undefined} attributeUniqueIdMap Provides a pre-defined ID
+	 *     for each attribute in the geometry to be decoded. If given,
+	 *     `attributeTypeMap` is required and `nativeAttributeMap` will be
+	 *     ignored.
+	 * @param {Object|undefined} attributeTypeMap Provides a predefined data
+	 *     type (as a typed array constructor) for each attribute in the
+	 *     geometry to be decoded.
+	 */
+	decodeDracoFile: function (
+		rawBuffer,
+		callback,
+		attributeUniqueIdMap,
+		attributeTypeMap
+	) {
+
+		var scope = this;
+		DRACOLoader.getDecoderModule().then( function ( module ) {
+
+			scope.decodeDracoFileInternal(
+				rawBuffer,
+				module.decoder,
+				callback,
+				attributeUniqueIdMap,
+				attributeTypeMap
+			);
+
+		} );
+
+	},
+
+	decodeDracoFileInternal: function (
+		rawBuffer,
+		dracoDecoder,
+		callback,
+		attributeUniqueIdMap,
+		attributeTypeMap
+	) {
+
+		/*
+		 * Here is how to use Draco Javascript decoder and get the geometry.
+		 */
+		var buffer = new dracoDecoder.DecoderBuffer();
+		buffer.Init( new Int8Array( rawBuffer ), rawBuffer.byteLength );
+		var decoder = new dracoDecoder.Decoder();
+
+		/*
+		 * Determine what type is this file: mesh or point cloud.
+		 */
+		var geometryType = decoder.GetEncodedGeometryType( buffer );
+		if ( geometryType == dracoDecoder.TRIANGULAR_MESH ) {
+
+			if ( this.verbosity > 0 ) {
+
+				console.log( "Loaded a mesh." );
+
+			}
+
+		} else if ( geometryType == dracoDecoder.POINT_CLOUD ) {
+
+			if ( this.verbosity > 0 ) {
+
+				console.log( "Loaded a point cloud." );
+
+			}
+
+		} else {
+
+			var errorMsg = "DRACOLoader: Unknown geometry type.";
+			console.error( errorMsg );
+			throw new Error( errorMsg );
+
+		}
+		callback(
+			this.convertDracoGeometryTo3JS(
+				dracoDecoder,
+				decoder,
+				geometryType,
+				buffer,
+				attributeUniqueIdMap,
+				attributeTypeMap
+			)
+		);
+
+	},
+
+	addAttributeToGeometry: function (
+		dracoDecoder,
+		decoder,
+		dracoGeometry,
+		attributeName,
+		attributeType,
+		attribute,
+		geometry,
+		geometryBuffer
+	) {
+
+		if ( attribute.ptr === 0 ) {
+
+			var errorMsg = "DRACOLoader: No attribute " + attributeName;
+			console.error( errorMsg );
+			throw new Error( errorMsg );
+
+		}
+
+		var numComponents = attribute.num_components();
+		var numPoints = dracoGeometry.num_points();
+		var numValues = numPoints * numComponents;
+		var attributeData;
+		var TypedBufferAttribute;
+
+		switch ( attributeType ) {
+
+			case Float32Array:
+				attributeData = new dracoDecoder.DracoFloat32Array();
+				decoder.GetAttributeFloatForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Float32Array( numValues );
+				TypedBufferAttribute = Float32BufferAttribute;
+				break;
+
+			case Int8Array:
+				attributeData = new dracoDecoder.DracoInt8Array();
+				decoder.GetAttributeInt8ForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Int8Array( numValues );
+				TypedBufferAttribute = Int8BufferAttribute;
+				break;
+
+			case Int16Array:
+				attributeData = new dracoDecoder.DracoInt16Array();
+				decoder.GetAttributeInt16ForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Int16Array( numValues );
+				TypedBufferAttribute = Int16BufferAttribute;
+				break;
+
+			case Int32Array:
+				attributeData = new dracoDecoder.DracoInt32Array();
+				decoder.GetAttributeInt32ForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Int32Array( numValues );
+				TypedBufferAttribute = Int32BufferAttribute;
+				break;
+
+			case Uint8Array:
+				attributeData = new dracoDecoder.DracoUInt8Array();
+				decoder.GetAttributeUInt8ForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Uint8Array( numValues );
+				TypedBufferAttribute = Uint8BufferAttribute;
+				break;
+
+			case Uint16Array:
+				attributeData = new dracoDecoder.DracoUInt16Array();
+				decoder.GetAttributeUInt16ForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Uint16Array( numValues );
+				TypedBufferAttribute = Uint16BufferAttribute;
+				break;
+
+			case Uint32Array:
+				attributeData = new dracoDecoder.DracoUInt32Array();
+				decoder.GetAttributeUInt32ForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Uint32Array( numValues );
+				TypedBufferAttribute = Uint32BufferAttribute;
+				break;
+
+			default:
+				var errorMsg = "DRACOLoader: Unexpected attribute type.";
+				console.error( errorMsg );
+				throw new Error( errorMsg );
+
+		}
+
+		// Copy data from decoder.
+		for ( var i = 0; i < numValues; i ++ ) {
+
+			geometryBuffer[ attributeName ][ i ] = attributeData.GetValue( i );
+
+		}
+		// Add attribute to THREEJS geometry for rendering.
+		geometry.addAttribute(
+			attributeName,
+			new TypedBufferAttribute( geometryBuffer[ attributeName ], numComponents )
+		);
+		dracoDecoder.destroy( attributeData );
+
+	},
+
+	convertDracoGeometryTo3JS: function (
+		dracoDecoder,
+		decoder,
+		geometryType,
+		buffer,
+		attributeUniqueIdMap,
+		attributeTypeMap
+	) {
+
+		// TODO: Should not assume native Draco attribute IDs apply.
+		if ( this.getAttributeOptions( "position" ).skipDequantization === true ) {
+
+			decoder.SkipAttributeTransform( dracoDecoder.POSITION );
+
+		}
+		var dracoGeometry;
+		var decodingStatus;
+		var start_time = performance.now();
+		if ( geometryType === dracoDecoder.TRIANGULAR_MESH ) {
+
+			dracoGeometry = new dracoDecoder.Mesh();
+			decodingStatus = decoder.DecodeBufferToMesh( buffer, dracoGeometry );
+
+		} else {
+
+			dracoGeometry = new dracoDecoder.PointCloud();
+			decodingStatus = decoder.DecodeBufferToPointCloud( buffer, dracoGeometry );
+
+		}
+		if ( ! decodingStatus.ok() || dracoGeometry.ptr == 0 ) {
+
+			var errorMsg = "DRACOLoader: Decoding failed: ";
+			errorMsg += decodingStatus.error_msg();
+			console.error( errorMsg );
+			dracoDecoder.destroy( decoder );
+			dracoDecoder.destroy( dracoGeometry );
+			throw new Error( errorMsg );
+
+		}
+
+		var decode_end = performance.now();
+		dracoDecoder.destroy( buffer );
+		/*
+		 * Example on how to retrieve mesh and attributes.
+		 */
+		var numFaces;
+		if ( geometryType == dracoDecoder.TRIANGULAR_MESH ) {
+
+			numFaces = dracoGeometry.num_faces();
+			if ( this.verbosity > 0 ) {
+
+				console.log( "Number of faces loaded: " + numFaces.toString() );
+
+			}
+
+		} else {
+
+			numFaces = 0;
+
+		}
+
+		var numPoints = dracoGeometry.num_points();
+		var numAttributes = dracoGeometry.num_attributes();
+		if ( this.verbosity > 0 ) {
+
+			console.log( "Number of points loaded: " + numPoints.toString() );
+			console.log( "Number of attributes loaded: " + numAttributes.toString() );
+
+		}
+
+		// Verify if there is position attribute.
+		// TODO: Should not assume native Draco attribute IDs apply.
+		var posAttId = decoder.GetAttributeId( dracoGeometry, dracoDecoder.POSITION );
+		if ( posAttId == - 1 ) {
+
+			var errorMsg = "DRACOLoader: No position attribute found.";
+			console.error( errorMsg );
+			dracoDecoder.destroy( decoder );
+			dracoDecoder.destroy( dracoGeometry );
+			throw new Error( errorMsg );
+
+		}
+		var posAttribute = decoder.GetAttribute( dracoGeometry, posAttId );
+
+		// Structure for converting to THREEJS geometry later.
+		var geometryBuffer = {};
+		// Import data to Three JS geometry.
+		var geometry = new BufferGeometry();
+
+		// Do not use both the native attribute map and a provided (e.g. glTF) map.
+		if ( attributeUniqueIdMap ) {
+
+			// Add attributes of user specified unique id. E.g. GLTF models.
+			for ( var attributeName in attributeUniqueIdMap ) {
+
+				var attributeType = attributeTypeMap[ attributeName ];
+				var attributeId = attributeUniqueIdMap[ attributeName ];
+				var attribute = decoder.GetAttributeByUniqueId(
+					dracoGeometry,
+					attributeId
+				);
+				this.addAttributeToGeometry(
+					dracoDecoder,
+					decoder,
+					dracoGeometry,
+					attributeName,
+					attributeType,
+					attribute,
+					geometry,
+					geometryBuffer
+				);
+
+			}
+
+		} else {
+
+			// Add native Draco attribute type to geometry.
+			for ( var attributeName in this.nativeAttributeMap ) {
+
+				var attId = decoder.GetAttributeId(
+					dracoGeometry,
+					dracoDecoder[ this.nativeAttributeMap[ attributeName ] ]
+				);
+				if ( attId !== - 1 ) {
+
+					if ( this.verbosity > 0 ) {
+
+						console.log( "Loaded " + attributeName + " attribute." );
+
+					}
+					var attribute = decoder.GetAttribute( dracoGeometry, attId );
+					this.addAttributeToGeometry(
+						dracoDecoder,
+						decoder,
+						dracoGeometry,
+						attributeName,
+						Float32Array,
+						attribute,
+						geometry,
+						geometryBuffer
+					);
+
+				}
+
+			}
+
+		}
+
+		// For mesh, we need to generate the faces.
+		if ( geometryType == dracoDecoder.TRIANGULAR_MESH ) {
+
+			if ( this.drawMode === TriangleStripDrawMode ) {
+
+				var stripsArray = new dracoDecoder.DracoInt32Array();
+				decoder.GetTriangleStripsFromMesh(
+					dracoGeometry,
+					stripsArray
+				);
+				geometryBuffer.indices = new Uint32Array( stripsArray.size() );
+				for ( var i = 0; i < stripsArray.size(); ++ i ) {
+
+					geometryBuffer.indices[ i ] = stripsArray.GetValue( i );
+
+				}
+				dracoDecoder.destroy( stripsArray );
+
+			} else {
+
+				var numIndices = numFaces * 3;
+				geometryBuffer.indices = new Uint32Array( numIndices );
+				var ia = new dracoDecoder.DracoInt32Array();
+				for ( var i = 0; i < numFaces; ++ i ) {
+
+					decoder.GetFaceFromMesh( dracoGeometry, i, ia );
+					var index = i * 3;
+					geometryBuffer.indices[ index ] = ia.GetValue( 0 );
+					geometryBuffer.indices[ index + 1 ] = ia.GetValue( 1 );
+					geometryBuffer.indices[ index + 2 ] = ia.GetValue( 2 );
+
+				}
+				dracoDecoder.destroy( ia );
+
+			}
+
+		}
+
+		geometry.drawMode = this.drawMode;
+		if ( geometryType == dracoDecoder.TRIANGULAR_MESH ) {
+
+			geometry.setIndex(
+				new ( geometryBuffer.indices.length > 65535
+					? Uint32BufferAttribute
+					: Uint16BufferAttribute )( geometryBuffer.indices, 1 )
+			);
+
+		}
+
+		// TODO: Should not assume native Draco attribute IDs apply.
+		// TODO: Can other attribute types be quantized?
+		var posTransform = new dracoDecoder.AttributeQuantizationTransform();
+		if ( posTransform.InitFromAttribute( posAttribute ) ) {
+
+			// Quantized attribute. Store the quantization parameters into the
+			// js attribute.
+			geometry.attributes[ "position" ].isQuantized = true;
+			geometry.attributes[ "position" ].maxRange = posTransform.range();
+			geometry.attributes[
+				"position"
+			].numQuantizationBits = posTransform.quantization_bits();
+			geometry.attributes[ "position" ].minValues = new Float32Array( 3 );
+			for ( var i = 0; i < 3; ++ i ) {
+
+				geometry.attributes[ "position" ].minValues[ i ] = posTransform.min_value(
+					i
+				);
+
+			}
+
+		}
+		dracoDecoder.destroy( posTransform );
+		dracoDecoder.destroy( decoder );
+		dracoDecoder.destroy( dracoGeometry );
+
+		this.decode_time = decode_end - start_time;
+		this.import_time = performance.now() - decode_end;
+
+		if ( this.verbosity > 0 ) {
+
+			console.log( "Decode time: " + this.decode_time );
+			console.log( "Import time: " + this.import_time );
+
+		}
+		return geometry;
+
+	},
+
+	isVersionSupported: function ( version, callback ) {
+
+		DRACOLoader.getDecoderModule().then( function ( module ) {
+
+			callback( module.decoder.isVersionSupported( version ) );
+
+		} );
+
+	},
+
+	getAttributeOptions: function ( attributeName ) {
+
+		if ( typeof this.attributeOptions[ attributeName ] === "undefined" )
+			this.attributeOptions[ attributeName ] = {};
+		return this.attributeOptions[ attributeName ];
+
+	}
+};
+
+DRACOLoader.decoderPath = "./";
+DRACOLoader.decoderConfig = {};
+DRACOLoader.decoderModulePromise = null;
+
+/**
+ * Sets the base path for decoder source files.
+ * @param {string} path
+ */
+DRACOLoader.setDecoderPath = function ( path ) {
+
+	DRACOLoader.decoderPath = path;
+
+};
+
+/**
+ * Sets decoder configuration and releases singleton decoder module. Module
+ * will be recreated with the next decoding call.
+ * @param {Object} config
+ */
+DRACOLoader.setDecoderConfig = function ( config ) {
+
+	var wasmBinary = DRACOLoader.decoderConfig.wasmBinary;
+	DRACOLoader.decoderConfig = config || {};
+	DRACOLoader.releaseDecoderModule();
+
+	// Reuse WASM binary.
+	if ( wasmBinary ) DRACOLoader.decoderConfig.wasmBinary = wasmBinary;
+
+};
+
+/**
+ * Releases the singleton DracoDecoderModule instance. Module will be recreated
+ * with the next decoding call.
+ */
+DRACOLoader.releaseDecoderModule = function () {
+
+	DRACOLoader.decoderModulePromise = null;
+
+};
+
+/**
+ * Gets WebAssembly or asm.js singleton instance of DracoDecoderModule
+ * after testing for browser support. Returns Promise that resolves when
+ * module is available.
+ * @return {Promise<{decoder: DracoDecoderModule}>}
+ */
+DRACOLoader.getDecoderModule = function () {
+
+	var scope = this;
+	var path = DRACOLoader.decoderPath;
+	var config = DRACOLoader.decoderConfig;
+	var promise = DRACOLoader.decoderModulePromise;
+
+	if ( promise ) return promise;
+
+	// Load source files.
+	if ( typeof DracoDecoderModule !== "undefined" ) {
+
+		// Loaded externally.
+		promise = Promise.resolve();
+
+	} else if ( typeof WebAssembly !== "object" || config.type === "js" ) {
+
+		// Load with asm.js.
+		promise = DRACOLoader._loadScript( path + "draco_decoder.js" );
+
+	} else {
+
+		// Load with WebAssembly.
+		config.wasmBinaryFile = path + "draco_decoder.wasm";
+		promise = DRACOLoader._loadScript( path + "draco_wasm_wrapper.js" )
+			.then( function () {
+
+				return DRACOLoader._loadArrayBuffer( config.wasmBinaryFile );
+
+			} )
+			.then( function ( wasmBinary ) {
+
+				config.wasmBinary = wasmBinary;
+
+			} );
+
+	}
+
+	// Wait for source files, then create and return a decoder.
+	promise = promise.then( function () {
+
+		return new Promise( function ( resolve ) {
+
+			config.onModuleLoaded = function ( decoder ) {
+
+				scope.timeLoaded = performance.now();
+				// Module is Promise-like. Wrap before resolving to avoid loop.
+				resolve( { decoder: decoder } );
+
+			};
+			DracoDecoderModule( config );
+
+		} );
+
+	} );
+
+	DRACOLoader.decoderModulePromise = promise;
+	return promise;
+
+};
+
+/**
+ * @param {string} src
+ * @return {Promise}
+ */
+DRACOLoader._loadScript = function ( src ) {
+
+	var prevScript = document.getElementById( "decoder_script" );
+	if ( prevScript !== null ) {
+
+		prevScript.parentNode.removeChild( prevScript );
+
+	}
+	var head = document.getElementsByTagName( "head" )[ 0 ];
+	var script = document.createElement( "script" );
+	script.id = "decoder_script";
+	script.type = "text/javascript";
+	script.src = src;
+	return new Promise( function ( resolve ) {
+
+		script.onload = resolve;
+		head.appendChild( script );
+
+	} );
+
+};
+
+/**
+ * @param {string} src
+ * @return {Promise}
+ */
+DRACOLoader._loadArrayBuffer = function ( src ) {
+
+	var loader = new FileLoader();
+	loader.setResponseType( "arraybuffer" );
+	return new Promise( function ( resolve, reject ) {
+
+		loader.load( src, resolve, undefined, reject );
+
+	} );
+
+};
+
+export { DRACOLoader };

+ 16 - 0
examples/jsm/loaders/TTFLoader.d.ts

@@ -0,0 +1,16 @@
+import {
+  BufferGeometry,
+  LoadingManager
+} from '../../../src/Three';
+
+export class TTFLoader {
+  constructor(manager?: LoadingManager);
+  manager: LoadingManager;
+  path: string;
+  reversed: boolean;
+
+  load(url: string, onLoad: (json: object) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  setPath(path: string): this;
+
+  parse(arraybuffer: ArrayBuffer): object;
+}

+ 207 - 0
examples/jsm/loaders/TTFLoader.js

@@ -0,0 +1,207 @@
+/**
+ * @author gero3 / https://github.com/gero3
+ * @author tentone / https://github.com/tentone
+ *
+ * Requires opentype.js to be included in the project.
+ * Loads TTF files and converts them into typeface JSON that can be used directly
+ * to create THREE.Font objects.
+ */
+
+import {
+	DefaultLoadingManager,
+	FileLoader
+} from "../../../build/three.module.js";
+
+var TTFLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+	this.reversed = false;
+
+};
+
+TTFLoader.prototype = {
+
+	constructor: TTFLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var loader = new FileLoader( this.manager );
+		loader.setPath( this.path );
+		loader.setResponseType( 'arraybuffer' );
+		loader.load( url, function ( buffer ) {
+
+			onLoad( scope.parse( buffer ) );
+
+		}, onProgress, onError );
+
+	},
+
+	setPath: function ( value ) {
+
+		this.path = value;
+		return this;
+
+	},
+
+	parse: function ( arraybuffer ) {
+
+		function convert( font, reversed ) {
+
+			var round = Math.round;
+
+			var glyphs = {};
+			var scale = ( 100000 ) / ( ( font.unitsPerEm || 2048 ) * 72 );
+
+			for ( var i = 0; i < font.glyphs.length; i ++ ) {
+
+				var glyph = font.glyphs.glyphs[ i ];
+
+				if ( glyph.unicode !== undefined ) {
+
+					var token = {
+						ha: round( glyph.advanceWidth * scale ),
+						x_min: round( glyph.xMin * scale ),
+						x_max: round( glyph.xMax * scale ),
+						o: ''
+					};
+
+					if ( reversed ) {
+
+						glyph.path.commands = reverseCommands( glyph.path.commands );
+
+					}
+
+					glyph.path.commands.forEach( function ( command ) {
+
+						if ( command.type.toLowerCase() === 'c' ) {
+
+							command.type = 'b';
+
+						}
+
+						token.o += command.type.toLowerCase() + ' ';
+
+						if ( command.x !== undefined && command.y !== undefined ) {
+
+							token.o += round( command.x * scale ) + ' ' + round( command.y * scale ) + ' ';
+
+						}
+
+						if ( command.x1 !== undefined && command.y1 !== undefined ) {
+
+							token.o += round( command.x1 * scale ) + ' ' + round( command.y1 * scale ) + ' ';
+
+						}
+
+						if ( command.x2 !== undefined && command.y2 !== undefined ) {
+
+							token.o += round( command.x2 * scale ) + ' ' + round( command.y2 * scale ) + ' ';
+
+						}
+
+					} );
+
+					glyphs[ String.fromCharCode( glyph.unicode ) ] = token;
+
+				}
+
+			}
+
+			return {
+				glyphs: glyphs,
+				familyName: font.familyName,
+				ascender: round( font.ascender * scale ),
+				descender: round( font.descender * scale ),
+				underlinePosition: font.tables.post.underlinePosition,
+				underlineThickness: font.tables.post.underlineThickness,
+				boundingBox: {
+					xMin: font.tables.head.xMin,
+					xMax: font.tables.head.xMax,
+					yMin: font.tables.head.yMin,
+					yMax: font.tables.head.yMax
+				},
+				resolution: 1000,
+				original_font_information: font.tables.name
+			};
+
+		}
+
+		function reverseCommands( commands ) {
+
+			var paths = [];
+			var path;
+
+			commands.forEach( function ( c ) {
+
+				if ( c.type.toLowerCase() === 'm' ) {
+
+					path = [ c ];
+					paths.push( path );
+
+				} else if ( c.type.toLowerCase() !== 'z' ) {
+
+					path.push( c );
+
+				}
+
+			} );
+
+			var reversed = [];
+
+			paths.forEach( function ( p ) {
+
+				var result = {
+					type: 'm',
+					x: p[ p.length - 1 ].x,
+					y: p[ p.length - 1 ].y
+				};
+
+				reversed.push( result );
+
+				for ( var i = p.length - 1; i > 0; i -- ) {
+
+					var command = p[ i ];
+					var result = { type: command.type };
+
+					if ( command.x2 !== undefined && command.y2 !== undefined ) {
+
+						result.x1 = command.x2;
+						result.y1 = command.y2;
+						result.x2 = command.x1;
+						result.y2 = command.y1;
+
+					} else if ( command.x1 !== undefined && command.y1 !== undefined ) {
+
+						result.x1 = command.x1;
+						result.y1 = command.y1;
+
+					}
+
+					result.x = p[ i - 1 ].x;
+					result.y = p[ i - 1 ].y;
+					reversed.push( result );
+
+				}
+
+			} );
+
+			return reversed;
+
+		}
+
+		if ( typeof opentype === 'undefined' ) {
+
+			console.warn( 'THREE.TTFLoader: The loader requires opentype.js. Make sure it\'s included before using the loader.' );
+			return null;
+
+		}
+
+		return convert( opentype.parse( arraybuffer ), this.reversed );
+
+	}
+
+};
+
+export { TTFLoader };

+ 314 - 0
examples/jsm/misc/CarControls.js

@@ -0,0 +1,314 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author Lewy Blue https://github.com/looeee
+ *
+ * The model is expected to follow real world car proportions. You can try unusual car types
+ * but your results may be unexpected. Scaled models are also not supported.
+ *
+ * Defaults are rough estimates for a real world scale car model
+ *
+ */
+
+import {
+	Box3,
+	Group,
+	Math as _Math,
+	Vector3
+} from "../../../build/three.module.js";
+
+var CarControls = ( function ( ) {
+
+	// private variables
+	var steeringWheelSpeed = 1.5;
+	var maxSteeringRotation = 0.6;
+
+	var acceleration = 0;
+
+	var maxSpeedReverse, accelerationReverse, deceleration;
+
+	var controlKeys = { LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, BRAKE: 32 };
+
+	var wheelOrientation = 0;
+	var carOrientation = 0;
+
+	var root = null;
+
+	var frontLeftWheelRoot = null;
+	var frontRightWheelRoot = null;
+
+	var frontLeftWheel = new Group();
+	var frontRightWheel = new Group();
+	var backLeftWheel = null;
+	var backRightWheel = null;
+
+	var steeringWheel = null;
+
+	var wheelDiameter = 1;
+	var length = 1;
+
+	var loaded = false;
+
+	var controls = {
+
+		brake: false,
+		moveForward: false,
+		moveBackward: false,
+		moveLeft: false,
+		moveRight: false
+
+	};
+
+	function CarControls( maxSpeed, acceleration, brakePower, turningRadius, keys ) {
+
+		this.enabled = true;
+
+		this.elemNames = {
+			flWheel: 'wheel_fl',
+			frWheel: 'wheel_fr',
+			rlWheel: 'wheel_rl',
+			rrWheel: 'wheel_rr',
+			steeringWheel: 'steering_wheel', // set to null to disable
+		};
+
+		// km/hr
+		this.maxSpeed = maxSpeed || 180;
+		maxSpeedReverse = - this.maxSpeed * 0.25;
+
+		// m/s
+		this.acceleration = acceleration || 10;
+		accelerationReverse = this.acceleration * 0.5;
+
+		// metres
+		this.turningRadius = turningRadius || 6;
+
+		// m/s
+		deceleration = this.acceleration * 2;
+
+		// multiplied with deceleration, so breaking deceleration = ( acceleration * 2 * brakePower ) m/s
+		this.brakePower = brakePower || 10;
+
+		// exposed so that a user can use this for various effect, e.g blur
+		this.speed = 0;
+
+		// keys used to control car - by default the arrow keys and space to brake
+		controlKeys = keys || controlKeys;
+
+		// local axes of rotation - these are likely to vary between models
+		this.wheelRotationAxis = 'x';
+		this.wheelTurnAxis = 'z';
+		this.steeringWheelTurnAxis = 'y';
+
+		document.addEventListener( 'keydown', this.onKeyDown, false );
+		document.addEventListener( 'keyup', this.onKeyUp, false );
+
+	}
+
+	CarControls.prototype = {
+
+		constructor: CarControls,
+
+		onKeyDown: function ( event ) {
+
+			switch ( event.keyCode ) {
+
+				case controlKeys.BRAKE:
+					controls.brake = true;
+					controls.moveForward = false;
+					controls.moveBackward = false;
+					break;
+
+				case controlKeys.UP: controls.moveForward = true; break;
+
+				case controlKeys.DOWN: controls.moveBackward = true; break;
+
+				case controlKeys.LEFT: controls.moveLeft = true; break;
+
+				case controlKeys.RIGHT: controls.moveRight = true; break;
+
+			}
+
+		},
+
+		onKeyUp: function ( event ) {
+
+			switch ( event.keyCode ) {
+
+				case controlKeys.BRAKE: controls.brake = false; break;
+
+				case controlKeys.UP: controls.moveForward = false; break;
+
+				case controlKeys.DOWN: controls.moveBackward = false; break;
+
+				case controlKeys.LEFT: controls.moveLeft = false; break;
+
+				case controlKeys.RIGHT: controls.moveRight = false; break;
+
+			}
+
+		},
+
+		dispose: function () {
+
+			document.removeEventListener( 'keydown', this.onKeyDown, false );
+			document.removeEventListener( 'keyup', this.onKeyUp, false );
+
+		},
+
+		update: function ( delta ) {
+
+			if ( ! loaded || ! this.enabled ) return;
+
+			var brakingDeceleration = 1;
+
+			if ( controls.brake ) brakingDeceleration = this.brakePower;
+
+			if ( controls.moveForward ) {
+
+				this.speed = _Math.clamp( this.speed + delta * this.acceleration, maxSpeedReverse, this.maxSpeed );
+				acceleration = _Math.clamp( acceleration + delta, - 1, 1 );
+
+			}
+
+			if ( controls.moveBackward ) {
+
+				this.speed = _Math.clamp( this.speed - delta * accelerationReverse, maxSpeedReverse, this.maxSpeed );
+				acceleration = _Math.clamp( acceleration - delta, - 1, 1 );
+
+			}
+
+			if ( controls.moveLeft ) {
+
+				wheelOrientation = _Math.clamp( wheelOrientation + delta * steeringWheelSpeed, - maxSteeringRotation, maxSteeringRotation );
+
+			}
+
+			if ( controls.moveRight ) {
+
+				wheelOrientation = _Math.clamp( wheelOrientation - delta * steeringWheelSpeed, - maxSteeringRotation, maxSteeringRotation );
+
+			}
+
+			// this.speed decay
+			if ( ! ( controls.moveForward || controls.moveBackward ) ) {
+
+				if ( this.speed > 0 ) {
+
+					var k = exponentialEaseOut( this.speed / this.maxSpeed );
+
+					this.speed = _Math.clamp( this.speed - k * delta * deceleration * brakingDeceleration, 0, this.maxSpeed );
+					acceleration = _Math.clamp( acceleration - k * delta, 0, 1 );
+
+				} else {
+
+					var k = exponentialEaseOut( this.speed / maxSpeedReverse );
+
+					this.speed = _Math.clamp( this.speed + k * delta * accelerationReverse * brakingDeceleration, maxSpeedReverse, 0 );
+					acceleration = _Math.clamp( acceleration + k * delta, - 1, 0 );
+
+				}
+
+			}
+
+			// steering decay
+			if ( ! ( controls.moveLeft || controls.moveRight ) ) {
+
+				if ( wheelOrientation > 0 ) {
+
+					wheelOrientation = _Math.clamp( wheelOrientation - delta * steeringWheelSpeed, 0, maxSteeringRotation );
+
+				} else {
+
+					wheelOrientation = _Math.clamp( wheelOrientation + delta * steeringWheelSpeed, - maxSteeringRotation, 0 );
+
+				}
+
+			}
+
+			var forwardDelta = - this.speed * delta;
+
+			carOrientation -= ( forwardDelta * this.turningRadius * 0.02 ) * wheelOrientation;
+
+			// movement of car
+			root.position.x += Math.sin( carOrientation ) * forwardDelta * length;
+			root.position.z += Math.cos( carOrientation ) * forwardDelta * length;
+
+			// angle of car
+			root.rotation.y = carOrientation;
+
+			// wheels rolling
+			var angularSpeedRatio = - 2 / wheelDiameter;
+
+			var wheelDelta = forwardDelta * angularSpeedRatio * length;
+
+			frontLeftWheel.rotation[ this.wheelRotationAxis ] -= wheelDelta;
+			frontRightWheel.rotation[ this.wheelRotationAxis ] -= wheelDelta;
+			backLeftWheel.rotation[ this.wheelRotationAxis ] -= wheelDelta;
+			backRightWheel.rotation[ this.wheelRotationAxis ] -= wheelDelta;
+
+			// rotation while steering
+			frontLeftWheelRoot.rotation[ this.wheelTurnAxis ] = wheelOrientation;
+			frontRightWheelRoot.rotation[ this.wheelTurnAxis ] = wheelOrientation;
+
+			steeringWheel.rotation[ this.steeringWheelTurnAxis ] = -wheelOrientation * 6;
+
+		},
+
+		setModel: function ( model, elemNames ) {
+
+			if ( elemNames ) this.elemNames = elemNames;
+
+			root = model;
+
+			this.setupWheels();
+			this.computeDimensions();
+
+			loaded = true;
+
+		},
+
+		setupWheels: function () {
+
+			frontLeftWheelRoot = root.getObjectByName( this.elemNames.flWheel );
+			frontRightWheelRoot = root.getObjectByName( this.elemNames.frWheel );
+			backLeftWheel = root.getObjectByName( this.elemNames.rlWheel );
+			backRightWheel = root.getObjectByName( this.elemNames.rrWheel );
+
+			if ( this.elemNames.steeringWheel !== null ) steeringWheel = root.getObjectByName( this.elemNames.steeringWheel );
+
+			while ( frontLeftWheelRoot.children.length > 0 ) frontLeftWheel.add( frontLeftWheelRoot.children[ 0 ] );
+			while ( frontRightWheelRoot.children.length > 0 ) frontRightWheel.add( frontRightWheelRoot.children[ 0 ] );
+
+			frontLeftWheelRoot.add( frontLeftWheel );
+			frontRightWheelRoot.add( frontRightWheel );
+
+		},
+
+		computeDimensions: function () {
+
+			var bb = new Box3().setFromObject( frontLeftWheelRoot );
+
+			var size = new Vector3();
+			bb.getSize( size );
+
+			wheelDiameter = Math.max( size.x, size.y, size.z );
+
+			bb.setFromObject( root );
+
+			size = bb.getSize( size );
+			length = Math.max( size.x, size.y, size.z );
+
+		}
+
+	};
+
+	function exponentialEaseOut( k ) {
+
+		return k === 1 ? 1 : - Math.pow( 2, - 10 * k ) + 1;
+
+	}
+
+	return CarControls;
+
+} )();
+
+export { CarControls };

+ 68 - 0
examples/jsm/misc/Ocean.d.ts

@@ -0,0 +1,68 @@
+import {
+  OrthographicCamera,
+  Mesh,
+  Scene,
+  ShaderMaterial,
+  Vector3,
+  WebGLRenderer,
+  WebGLRenderTarget,
+} from '../../../src/Three';
+
+export class Ocean {
+  constructor( renderer: WebGLRenderer, camera?: OrthographicCamera, scene?: Scene, options?: object );
+
+	changed: boolean;
+	initial: boolean;
+
+	oceanCamera: OrthographicCamera
+	renderer: WebGLRenderer;
+  scene: Scene;
+
+  clearColor: number[];
+	geometryOrigin: number[];
+	sunDirectionX: number;
+	sunDirectionY: number;
+	sunDirectionZ: number;
+	oceanColor: Vector3;
+	skyColor: Vector3;
+	exposure: number;
+	geometryResolution: number;
+	geometrySize: number;
+	resolution: number;
+	floatSize: number;
+	windX: number;
+	windY: number;
+	size: number;
+  choppiness: number;
+
+  initialSpectrumFramebuffer: WebGLRenderTarget;
+	spectrumFramebuffer: WebGLRenderTarget;
+	pingPhaseFramebuffer: WebGLRenderTarget;
+	pongPhaseFramebuffer: WebGLRenderTarget;
+	pingTransformFramebuffer: WebGLRenderTarget;
+	pongTransformFramebuffer: WebGLRenderTarget;
+	displacementMapFramebuffer: WebGLRenderTarget;
+	normalMapFramebuffer: WebGLRenderTarget;
+
+  matrixNeedsUpdate: boolean;
+
+  materialOceanHorizontal: ShaderMaterial;
+  materialOceanVertical: ShaderMaterial;
+  materialInitialSpectrum: ShaderMaterial;
+  materialPhase: ShaderMaterial;
+  materialSpectrum: ShaderMaterial;
+  materialNormal: ShaderMaterial;
+  materialOcean: ShaderMaterial;
+
+  screenQuad: Mesh;
+
+  generateSeedPhaseTexture(): void;
+	generateMesh(): void;
+  render(): void;
+  renderInitialSpectrum(): void;
+  renderWavePhase(): void;
+  renderSpectrum(): void;
+  renderSpectrumFFT(): void;
+  renderNormalMap(): void;
+
+}

+ 411 - 0
examples/jsm/misc/Ocean.js

@@ -0,0 +1,411 @@
+/*
+	three.js Ocean
+*/
+
+import {
+	ClampToEdgeWrapping,
+	DataTexture,
+	FloatType,
+	HalfFloatType,
+	LinearFilter,
+	Mesh,
+	NearestFilter,
+	OrthographicCamera,
+	PlaneBufferGeometry,
+	RGBAFormat,
+	RepeatWrapping,
+	Scene,
+	ShaderMaterial,
+	UniformsUtils,
+	Vector2,
+	Vector3,
+	WebGLRenderTarget
+} from "../../../build/three.module.js";
+import { OceanShaders } from "../shaders/OceanShaders.js";
+
+var Ocean = function ( renderer, camera, scene, options ) {
+
+	// flag used to trigger parameter changes
+	this.changed = true;
+	this.initial = true;
+
+	// Assign required parameters as object properties
+	this.oceanCamera = new OrthographicCamera(); //camera.clone();
+	this.oceanCamera.position.z = 1;
+	this.renderer = renderer;
+	this.renderer.clearColor( 0xffffff );
+
+	this.scene = new Scene();
+
+	// Assign optional parameters as variables and object properties
+	function optionalParameter( value, defaultValue ) {
+
+		return value !== undefined ? value : defaultValue;
+
+	}
+	options = options || {};
+	this.clearColor = optionalParameter( options.CLEAR_COLOR, [ 1.0, 1.0, 1.0, 0.0 ] );
+	this.geometryOrigin = optionalParameter( options.GEOMETRY_ORIGIN, [ - 1000.0, - 1000.0 ] );
+	this.sunDirectionX = optionalParameter( options.SUN_DIRECTION[ 0 ], - 1.0 );
+	this.sunDirectionY = optionalParameter( options.SUN_DIRECTION[ 1 ], 1.0 );
+	this.sunDirectionZ = optionalParameter( options.SUN_DIRECTION[ 2 ], 1.0 );
+	this.oceanColor = optionalParameter( options.OCEAN_COLOR, new Vector3( 0.004, 0.016, 0.047 ) );
+	this.skyColor = optionalParameter( options.SKY_COLOR, new Vector3( 3.2, 9.6, 12.8 ) );
+	this.exposure = optionalParameter( options.EXPOSURE, 0.35 );
+	this.geometryResolution = optionalParameter( options.GEOMETRY_RESOLUTION, 32 );
+	this.geometrySize = optionalParameter( options.GEOMETRY_SIZE, 2000 );
+	this.resolution = optionalParameter( options.RESOLUTION, 64 );
+	this.floatSize = optionalParameter( options.SIZE_OF_FLOAT, 4 );
+	this.windX = optionalParameter( options.INITIAL_WIND[ 0 ], 10.0 );
+	this.windY = optionalParameter( options.INITIAL_WIND[ 1 ], 10.0 );
+	this.size = optionalParameter( options.INITIAL_SIZE, 250.0 );
+	this.choppiness = optionalParameter( options.INITIAL_CHOPPINESS, 1.5 );
+
+	//
+	this.matrixNeedsUpdate = false;
+
+	// Setup framebuffer pipeline
+	var renderTargetType = optionalParameter( options.USE_HALF_FLOAT, false ) ? HalfFloatType : FloatType;
+	var LinearClampParams = {
+		minFilter: LinearFilter,
+		magFilter: LinearFilter,
+		wrapS: ClampToEdgeWrapping,
+		wrapT: ClampToEdgeWrapping,
+		format: RGBAFormat,
+		stencilBuffer: false,
+		depthBuffer: false,
+		premultiplyAlpha: false,
+		type: renderTargetType
+	};
+	var NearestClampParams = {
+		minFilter: NearestFilter,
+		magFilter: NearestFilter,
+		wrapS: ClampToEdgeWrapping,
+		wrapT: ClampToEdgeWrapping,
+		format: RGBAFormat,
+		stencilBuffer: false,
+		depthBuffer: false,
+		premultiplyAlpha: false,
+		type: renderTargetType
+	};
+	var NearestRepeatParams = {
+		minFilter: NearestFilter,
+		magFilter: NearestFilter,
+		wrapS: RepeatWrapping,
+		wrapT: RepeatWrapping,
+		format: RGBAFormat,
+		stencilBuffer: false,
+		depthBuffer: false,
+		premultiplyAlpha: false,
+		type: renderTargetType
+	};
+	this.initialSpectrumFramebuffer = new WebGLRenderTarget( this.resolution, this.resolution, NearestRepeatParams );
+	this.spectrumFramebuffer = new WebGLRenderTarget( this.resolution, this.resolution, NearestClampParams );
+	this.pingPhaseFramebuffer = new WebGLRenderTarget( this.resolution, this.resolution, NearestClampParams );
+	this.pongPhaseFramebuffer = new WebGLRenderTarget( this.resolution, this.resolution, NearestClampParams );
+	this.pingTransformFramebuffer = new WebGLRenderTarget( this.resolution, this.resolution, NearestClampParams );
+	this.pongTransformFramebuffer = new WebGLRenderTarget( this.resolution, this.resolution, NearestClampParams );
+	this.displacementMapFramebuffer = new WebGLRenderTarget( this.resolution, this.resolution, LinearClampParams );
+	this.normalMapFramebuffer = new WebGLRenderTarget( this.resolution, this.resolution, LinearClampParams );
+
+	// Define shaders and constant uniforms
+	////////////////////////////////////////
+
+	// 0 - The vertex shader used in all of the simulation steps
+	var fullscreeenVertexShader = OceanShaders[ "ocean_sim_vertex" ];
+
+	// 1 - Horizontal wave vertices used for FFT
+	var oceanHorizontalShader = OceanShaders[ "ocean_subtransform" ];
+	var oceanHorizontalUniforms = UniformsUtils.clone( oceanHorizontalShader.uniforms );
+	this.materialOceanHorizontal = new ShaderMaterial( {
+		uniforms: oceanHorizontalUniforms,
+		vertexShader: fullscreeenVertexShader.vertexShader,
+		fragmentShader: "#define HORIZONTAL \n" + oceanHorizontalShader.fragmentShader
+	} );
+	this.materialOceanHorizontal.uniforms.u_transformSize = { value: this.resolution };
+	this.materialOceanHorizontal.uniforms.u_subtransformSize = { value: null };
+	this.materialOceanHorizontal.uniforms.u_input = { value: null };
+	this.materialOceanHorizontal.depthTest = false;
+
+	// 2 - Vertical wave vertices used for FFT
+	var oceanVerticalShader = OceanShaders[ "ocean_subtransform" ];
+	var oceanVerticalUniforms = UniformsUtils.clone( oceanVerticalShader.uniforms );
+	this.materialOceanVertical = new ShaderMaterial( {
+		uniforms: oceanVerticalUniforms,
+		vertexShader: fullscreeenVertexShader.vertexShader,
+		fragmentShader: oceanVerticalShader.fragmentShader
+	} );
+	this.materialOceanVertical.uniforms.u_transformSize = { value: this.resolution };
+	this.materialOceanVertical.uniforms.u_subtransformSize = { value: null };
+	this.materialOceanVertical.uniforms.u_input = { value: null };
+	this.materialOceanVertical.depthTest = false;
+
+	// 3 - Initial spectrum used to generate height map
+	var initialSpectrumShader = OceanShaders[ "ocean_initial_spectrum" ];
+	var initialSpectrumUniforms = UniformsUtils.clone( initialSpectrumShader.uniforms );
+	this.materialInitialSpectrum = new ShaderMaterial( {
+		uniforms: initialSpectrumUniforms,
+		vertexShader: initialSpectrumShader.vertexShader,
+		fragmentShader: initialSpectrumShader.fragmentShader
+	} );
+	this.materialInitialSpectrum.uniforms.u_wind = { value: new Vector2() };
+	this.materialInitialSpectrum.uniforms.u_resolution = { value: this.resolution };
+	this.materialInitialSpectrum.depthTest = false;
+
+	// 4 - Phases used to animate heightmap
+	var phaseShader = OceanShaders[ "ocean_phase" ];
+	var phaseUniforms = UniformsUtils.clone( phaseShader.uniforms );
+	this.materialPhase = new ShaderMaterial( {
+		uniforms: phaseUniforms,
+		vertexShader: fullscreeenVertexShader.vertexShader,
+		fragmentShader: phaseShader.fragmentShader
+	} );
+	this.materialPhase.uniforms.u_resolution = { value: this.resolution };
+	this.materialPhase.depthTest = false;
+
+	// 5 - Shader used to update spectrum
+	var spectrumShader = OceanShaders[ "ocean_spectrum" ];
+	var spectrumUniforms = UniformsUtils.clone( spectrumShader.uniforms );
+	this.materialSpectrum = new ShaderMaterial( {
+		uniforms: spectrumUniforms,
+		vertexShader: fullscreeenVertexShader.vertexShader,
+		fragmentShader: spectrumShader.fragmentShader
+	} );
+	this.materialSpectrum.uniforms.u_initialSpectrum = { value: null };
+	this.materialSpectrum.uniforms.u_resolution = { value: this.resolution };
+	this.materialSpectrum.depthTest = false;
+
+	// 6 - Shader used to update spectrum normals
+	var normalShader = OceanShaders[ "ocean_normals" ];
+	var normalUniforms = UniformsUtils.clone( normalShader.uniforms );
+	this.materialNormal = new ShaderMaterial( {
+		uniforms: normalUniforms,
+		vertexShader: fullscreeenVertexShader.vertexShader,
+		fragmentShader: normalShader.fragmentShader
+	} );
+	this.materialNormal.uniforms.u_displacementMap = { value: null };
+	this.materialNormal.uniforms.u_resolution = { value: this.resolution };
+	this.materialNormal.depthTest = false;
+
+	// 7 - Shader used to update normals
+	var oceanShader = OceanShaders[ "ocean_main" ];
+	var oceanUniforms = UniformsUtils.clone( oceanShader.uniforms );
+	this.materialOcean = new ShaderMaterial( {
+		uniforms: oceanUniforms,
+		vertexShader: oceanShader.vertexShader,
+		fragmentShader: oceanShader.fragmentShader
+	} );
+	// this.materialOcean.wireframe = true;
+	this.materialOcean.uniforms.u_geometrySize = { value: this.resolution };
+	this.materialOcean.uniforms.u_displacementMap = { value: this.displacementMapFramebuffer.texture };
+	this.materialOcean.uniforms.u_normalMap = { value: this.normalMapFramebuffer.texture };
+	this.materialOcean.uniforms.u_oceanColor = { value: this.oceanColor };
+	this.materialOcean.uniforms.u_skyColor = { value: this.skyColor };
+	this.materialOcean.uniforms.u_sunDirection = { value: new Vector3( this.sunDirectionX, this.sunDirectionY, this.sunDirectionZ ) };
+	this.materialOcean.uniforms.u_exposure = { value: this.exposure };
+
+	// Disable blending to prevent default premultiplied alpha values
+	this.materialOceanHorizontal.blending = 0;
+	this.materialOceanVertical.blending = 0;
+	this.materialInitialSpectrum.blending = 0;
+	this.materialPhase.blending = 0;
+	this.materialSpectrum.blending = 0;
+	this.materialNormal.blending = 0;
+	this.materialOcean.blending = 0;
+
+	// Create the simulation plane
+	this.screenQuad = new Mesh( new PlaneBufferGeometry( 2, 2 ) );
+	this.scene.add( this.screenQuad );
+
+	// Initialise spectrum data
+	this.generateSeedPhaseTexture();
+
+	// Generate the ocean mesh
+	this.generateMesh();
+
+};
+
+Ocean.prototype.generateMesh = function () {
+
+	var geometry = new PlaneBufferGeometry( this.geometrySize, this.geometrySize, this.geometryResolution, this.geometryResolution );
+
+	geometry.rotateX( - Math.PI / 2 );
+
+	this.oceanMesh = new Mesh( geometry, this.materialOcean );
+
+};
+
+Ocean.prototype.render = function () {
+
+	var currentRenderTarget = this.renderer.getRenderTarget();
+
+	this.scene.overrideMaterial = null;
+
+	if ( this.changed )
+		this.renderInitialSpectrum();
+
+	this.renderWavePhase();
+	this.renderSpectrum();
+	this.renderSpectrumFFT();
+	this.renderNormalMap();
+	this.scene.overrideMaterial = null;
+
+	this.renderer.setRenderTarget( currentRenderTarget );
+
+};
+
+Ocean.prototype.generateSeedPhaseTexture = function () {
+
+	// Setup the seed texture
+	this.pingPhase = true;
+	var phaseArray = new window.Float32Array( this.resolution * this.resolution * 4 );
+	for ( var i = 0; i < this.resolution; i ++ ) {
+
+		for ( var j = 0; j < this.resolution; j ++ ) {
+
+			phaseArray[ i * this.resolution * 4 + j * 4 ] = Math.random() * 2.0 * Math.PI;
+			phaseArray[ i * this.resolution * 4 + j * 4 + 1 ] = 0.0;
+			phaseArray[ i * this.resolution * 4 + j * 4 + 2 ] = 0.0;
+			phaseArray[ i * this.resolution * 4 + j * 4 + 3 ] = 0.0;
+
+		}
+
+	}
+
+	this.pingPhaseTexture = new DataTexture( phaseArray, this.resolution, this.resolution, RGBAFormat );
+	this.pingPhaseTexture.wrapS = ClampToEdgeWrapping;
+	this.pingPhaseTexture.wrapT = ClampToEdgeWrapping;
+	this.pingPhaseTexture.type = FloatType;
+	this.pingPhaseTexture.needsUpdate = true;
+
+};
+
+Ocean.prototype.renderInitialSpectrum = function () {
+
+	this.scene.overrideMaterial = this.materialInitialSpectrum;
+	this.materialInitialSpectrum.uniforms.u_wind.value.set( this.windX, this.windY );
+	this.materialInitialSpectrum.uniforms.u_size.value = this.size;
+
+	this.renderer.setRenderTarget( this.initialSpectrumFramebuffer );
+	this.renderer.clear();
+	this.renderer.render( this.scene, this.oceanCamera );
+
+};
+
+Ocean.prototype.renderWavePhase = function () {
+
+	this.scene.overrideMaterial = this.materialPhase;
+	this.screenQuad.material = this.materialPhase;
+	if ( this.initial ) {
+
+		this.materialPhase.uniforms.u_phases.value = this.pingPhaseTexture;
+		this.initial = false;
+
+	} else {
+
+		this.materialPhase.uniforms.u_phases.value = this.pingPhase ? this.pingPhaseFramebuffer.texture : this.pongPhaseFramebuffer.texture;
+
+	}
+	this.materialPhase.uniforms.u_deltaTime.value = this.deltaTime;
+	this.materialPhase.uniforms.u_size.value = this.size;
+	this.renderer.setRenderTarget( this.pingPhase ? this.pongPhaseFramebuffer : this.pingPhaseFramebuffer );
+	this.renderer.render( this.scene, this.oceanCamera );
+	this.pingPhase = ! this.pingPhase;
+
+};
+
+Ocean.prototype.renderSpectrum = function () {
+
+	this.scene.overrideMaterial = this.materialSpectrum;
+	this.materialSpectrum.uniforms.u_initialSpectrum.value = this.initialSpectrumFramebuffer.texture;
+	this.materialSpectrum.uniforms.u_phases.value = this.pingPhase ? this.pingPhaseFramebuffer.texture : this.pongPhaseFramebuffer.texture;
+	this.materialSpectrum.uniforms.u_choppiness.value = this.choppiness;
+	this.materialSpectrum.uniforms.u_size.value = this.size;
+
+	this.renderer.setRenderTarget( this.spectrumFramebuffer );
+	this.renderer.render( this.scene, this.oceanCamera );
+
+};
+
+Ocean.prototype.renderSpectrumFFT = function () {
+
+	// GPU FFT using Stockham formulation
+	var iterations = Math.log( this.resolution ) / Math.log( 2 ); // log2
+
+	this.scene.overrideMaterial = this.materialOceanHorizontal;
+
+	for ( var i = 0; i < iterations; i ++ ) {
+
+		if ( i === 0 ) {
+
+			this.materialOceanHorizontal.uniforms.u_input.value = this.spectrumFramebuffer.texture;
+			this.materialOceanHorizontal.uniforms.u_subtransformSize.value = Math.pow( 2, ( i % ( iterations ) ) + 1 );
+
+			this.renderer.setRenderTarget( this.pingTransformFramebuffer );
+			this.renderer.render( this.scene, this.oceanCamera );
+
+		} else if ( i % 2 === 1 ) {
+
+			this.materialOceanHorizontal.uniforms.u_input.value = this.pingTransformFramebuffer.texture;
+			this.materialOceanHorizontal.uniforms.u_subtransformSize.value = Math.pow( 2, ( i % ( iterations ) ) + 1 );
+
+			this.renderer.setRenderTarget( this.pongTransformFramebuffer );
+			this.renderer.render( this.scene, this.oceanCamera );
+
+		} else {
+
+			this.materialOceanHorizontal.uniforms.u_input.value = this.pongTransformFramebuffer.texture;
+			this.materialOceanHorizontal.uniforms.u_subtransformSize.value = Math.pow( 2, ( i % ( iterations ) ) + 1 );
+
+			this.renderer.setRenderTarget( this.pingTransformFramebuffer );
+			this.renderer.render( this.scene, this.oceanCamera );
+
+		}
+
+	}
+	this.scene.overrideMaterial = this.materialOceanVertical;
+	for ( var i = iterations; i < iterations * 2; i ++ ) {
+
+		if ( i === iterations * 2 - 1 ) {
+
+			this.materialOceanVertical.uniforms.u_input.value = ( iterations % 2 === 0 ) ? this.pingTransformFramebuffer.texture : this.pongTransformFramebuffer.texture;
+			this.materialOceanVertical.uniforms.u_subtransformSize.value = Math.pow( 2, ( i % ( iterations ) ) + 1 );
+
+			this.renderer.setRenderTarget( this.displacementMapFramebuffer );
+			this.renderer.render( this.scene, this.oceanCamera );
+
+		} else if ( i % 2 === 1 ) {
+
+			this.materialOceanVertical.uniforms.u_input.value = this.pingTransformFramebuffer.texture;
+			this.materialOceanVertical.uniforms.u_subtransformSize.value = Math.pow( 2, ( i % ( iterations ) ) + 1 );
+
+			this.renderer.setRenderTarget( this.pongTransformFramebuffer );
+			this.renderer.render( this.scene, this.oceanCamera );
+
+		} else {
+
+			this.materialOceanVertical.uniforms.u_input.value = this.pongTransformFramebuffer.texture;
+			this.materialOceanVertical.uniforms.u_subtransformSize.value = Math.pow( 2, ( i % ( iterations ) ) + 1 );
+
+			this.renderer.setRenderTarget( this.pingTransformFramebuffer );
+			this.renderer.render( this.scene, this.oceanCamera );
+
+		}
+
+	}
+
+};
+
+Ocean.prototype.renderNormalMap = function () {
+
+	this.scene.overrideMaterial = this.materialNormal;
+	if ( this.changed ) this.materialNormal.uniforms.u_size.value = this.size;
+	this.materialNormal.uniforms.u_displacementMap.value = this.displacementMapFramebuffer.texture;
+
+	this.renderer.setRenderTarget( this.normalMapFramebuffer );
+	this.renderer.clear();
+	this.renderer.render( this.scene, this.oceanCamera );
+
+};
+
+export { Ocean };

+ 1 - 1
examples/jsm/postprocessing/EffectComposer.d.ts

@@ -22,7 +22,7 @@ export class EffectComposer {
   addPass(pass: Pass): void;
   insertPass(pass: Pass, index: number): void;
   isLastEnabledPass(): boolean;
-  render(deltaTime: number): void;
+  render(deltaTime?: number): void;
   reset(renderTarget?: WebGLRenderTarget): void;
   setSize(width: number, height: number): void;
   setPixelRatio(pixelRatio: number): void;

+ 1 - 1
examples/jsm/postprocessing/RenderPass.d.ts

@@ -8,7 +8,7 @@ import {
 import { Pass } from './Pass';
 
 export class RenderPass extends Pass {
-  constructor(scene: Scene, camera: Camera, overrideMaterial: Material, clearColor: Color, clearAlpha: number);
+  constructor(scene: Scene, camera: Camera, overrideMaterial?: Material, clearColor?: Color, clearAlpha?: number);
   scene: Scene;
   camera: Camera;
   overrideMaterial: Material;

+ 75 - 0
examples/jsm/shaders/OceanShaders.d.ts

@@ -0,0 +1,75 @@
+import {
+  Texture,
+  Uniform,
+  Vector2,
+} from '../../../src/Three';
+
+export interface OceanShaders {
+
+  ocean_sim_vertex: {
+    vertexShader: string;
+  }
+  ocean_subtransform: {
+    uniforms: {
+      u_input: Uniform,
+      u_transformSize: Uniform,
+      u_subtransformSize: Uniform,
+    },
+    fragmentShader: string;
+  }
+  ocean_initial_spectrum: {
+    uniforms: {
+      u_wind: Uniform,
+      u_resolution: Uniform,
+      u_size: Uniform,
+    },
+    vertexShader: string;
+    fragmentShader: string;
+  }
+  ocean_phase: {
+    uniforms: {
+      u_phases: Uniform,
+      u_deltaTime: Uniform,
+      u_resolution: Uniform,
+      u_size: Uniform,
+    },
+    vertexShader: string;
+    fragmentShader: string;
+  }
+  ocean_spectrum: {
+    uniforms: {
+      u_size: Uniform,
+      u_resolution: Uniform,
+      u_choppiness: Uniform,
+      u_phases: Uniform,
+      u_initialSpectrum: Uniform,
+    },
+    fragmentShader: string;
+  }
+  ocean_normals: {
+    uniforms: {
+      u_displacementMap: Uniform,
+      u_resolution: Uniform,
+      u_size: Uniform,
+    },
+    fragmentShader: string;
+  }
+  ocean_main: {
+    uniforms: {
+      u_displacementMap: Uniform,
+      u_normalMap: Uniform,
+      u_geometrySize: Uniform,
+      u_size: Uniform,
+      u_projectionMatrix: Uniform,
+      u_viewMatrix: Uniform,
+      u_cameraPosition: Uniform,
+      u_skyColor: Uniform,
+      u_oceanColor: Uniform,
+      u_sunDirection: Uniform,
+      u_exposure: Uniform,
+    },
+    vertexShader: string;
+    fragmentShader: string;
+  }
+
+}

+ 377 - 0
examples/jsm/shaders/OceanShaders.js

@@ -0,0 +1,377 @@
+/* Author: Aleksandr Albert
+// Website: www.routter.co.tt
+
+// Description: A deep water ocean shader set
+// based on an implementation of a Tessendorf Waves
+// originally presented by David Li ( www.david.li/waves )
+
+// The general method is to apply shaders to simulation Framebuffers
+// and then sample these framebuffers when rendering the ocean mesh
+
+// The set uses 7 shaders:
+
+// -- Simulation shaders
+// [1] ocean_sim_vertex         -> Vertex shader used to set up a 2x2 simulation plane centered at (0,0)
+// [2] ocean_subtransform       -> Fragment shader used to subtransform the mesh (generates the displacement map)
+// [3] ocean_initial_spectrum   -> Fragment shader used to set intitial wave frequency at a texel coordinate
+// [4] ocean_phase              -> Fragment shader used to set wave phase at a texel coordinate
+// [5] ocean_spectrum           -> Fragment shader used to set current wave frequency at a texel coordinate
+// [6] ocean_normal             -> Fragment shader used to set face normals at a texel coordinate
+
+// -- Rendering Shader
+// [7] ocean_main               -> Vertex and Fragment shader used to create the final render
+*/
+
+import {
+	Vector2
+} from "../../../build/three.module.js";
+
+var OceanShaders = {}
+OceanShaders[ 'ocean_sim_vertex' ] = {
+	vertexShader: [
+		'varying vec2 vUV;',
+
+		'void main (void) {',
+			'vUV = position.xy * 0.5 + 0.5;',
+			'gl_Position = vec4(position, 1.0 );',
+		'}'
+	].join( '\n' )
+};
+OceanShaders[ 'ocean_subtransform' ] = {
+	uniforms: {
+		"u_input": { value: null },
+		"u_transformSize": { value: 512.0 },
+		"u_subtransformSize": { value: 250.0 }
+	},
+	fragmentShader: [
+		//GPU FFT using a Stockham formulation
+
+		'precision highp float;',
+		'#include <common>',
+
+		'uniform sampler2D u_input;',
+		'uniform float u_transformSize;',
+		'uniform float u_subtransformSize;',
+
+		'varying vec2 vUV;',
+
+		'vec2 multiplyComplex (vec2 a, vec2 b) {',
+			'return vec2(a[0] * b[0] - a[1] * b[1], a[1] * b[0] + a[0] * b[1]);',
+		'}',
+
+		'void main (void) {',
+			'#ifdef HORIZONTAL',
+			'float index = vUV.x * u_transformSize - 0.5;',
+			'#else',
+			'float index = vUV.y * u_transformSize - 0.5;',
+			'#endif',
+
+			'float evenIndex = floor(index / u_subtransformSize) * (u_subtransformSize * 0.5) + mod(index, u_subtransformSize * 0.5);',
+
+			//transform two complex sequences simultaneously
+			'#ifdef HORIZONTAL',
+			'vec4 even = texture2D(u_input, vec2(evenIndex + 0.5, gl_FragCoord.y) / u_transformSize).rgba;',
+			'vec4 odd = texture2D(u_input, vec2(evenIndex + u_transformSize * 0.5 + 0.5, gl_FragCoord.y) / u_transformSize).rgba;',
+			'#else',
+			'vec4 even = texture2D(u_input, vec2(gl_FragCoord.x, evenIndex + 0.5) / u_transformSize).rgba;',
+			'vec4 odd = texture2D(u_input, vec2(gl_FragCoord.x, evenIndex + u_transformSize * 0.5 + 0.5) / u_transformSize).rgba;',
+			'#endif',
+
+			'float twiddleArgument = -2.0 * PI * (index / u_subtransformSize);',
+			'vec2 twiddle = vec2(cos(twiddleArgument), sin(twiddleArgument));',
+
+			'vec2 outputA = even.xy + multiplyComplex(twiddle, odd.xy);',
+			'vec2 outputB = even.zw + multiplyComplex(twiddle, odd.zw);',
+
+			'gl_FragColor = vec4(outputA, outputB);',
+		'}'
+	].join( '\n' )
+};
+OceanShaders[ 'ocean_initial_spectrum' ] = {
+	uniforms: {
+		"u_wind": { value: new Vector2( 10.0, 10.0 ) },
+		"u_resolution": { value: 512.0 },
+		"u_size": { value: 250.0 }
+	},
+	vertexShader: [
+		'void main (void) {',
+			'gl_Position = vec4(position, 1.0);',
+		'}'
+	].join( '\n' ),
+	fragmentShader: [
+		'precision highp float;',
+		'#include <common>',
+
+		'const float G = 9.81;',
+		'const float KM = 370.0;',
+		'const float CM = 0.23;',
+
+		'uniform vec2 u_wind;',
+		'uniform float u_resolution;',
+		'uniform float u_size;',
+
+		'float omega (float k) {',
+			'return sqrt(G * k * (1.0 + pow2(k / KM)));',
+		'}',
+
+		'float tanh (float x) {',
+			'return (1.0 - exp(-2.0 * x)) / (1.0 + exp(-2.0 * x));',
+		'}',
+
+		'void main (void) {',
+			'vec2 coordinates = gl_FragCoord.xy - 0.5;',
+
+			'float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;',
+			'float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;',
+
+			'vec2 K = (2.0 * PI * vec2(n, m)) / u_size;',
+			'float k = length(K);',
+
+			'float l_wind = length(u_wind);',
+
+			'float Omega = 0.84;',
+			'float kp = G * pow2(Omega / l_wind);',
+
+			'float c = omega(k) / k;',
+			'float cp = omega(kp) / kp;',
+
+			'float Lpm = exp(-1.25 * pow2(kp / k));',
+			'float gamma = 1.7;',
+			'float sigma = 0.08 * (1.0 + 4.0 * pow(Omega, -3.0));',
+			'float Gamma = exp(-pow2(sqrt(k / kp) - 1.0) / 2.0 * pow2(sigma));',
+			'float Jp = pow(gamma, Gamma);',
+			'float Fp = Lpm * Jp * exp(-Omega / sqrt(10.0) * (sqrt(k / kp) - 1.0));',
+			'float alphap = 0.006 * sqrt(Omega);',
+			'float Bl = 0.5 * alphap * cp / c * Fp;',
+
+			'float z0 = 0.000037 * pow2(l_wind) / G * pow(l_wind / cp, 0.9);',
+			'float uStar = 0.41 * l_wind / log(10.0 / z0);',
+			'float alpham = 0.01 * ((uStar < CM) ? (1.0 + log(uStar / CM)) : (1.0 + 3.0 * log(uStar / CM)));',
+			'float Fm = exp(-0.25 * pow2(k / KM - 1.0));',
+			'float Bh = 0.5 * alpham * CM / c * Fm * Lpm;',
+
+			'float a0 = log(2.0) / 4.0;',
+			'float am = 0.13 * uStar / CM;',
+			'float Delta = tanh(a0 + 4.0 * pow(c / cp, 2.5) + am * pow(CM / c, 2.5));',
+
+			'float cosPhi = dot(normalize(u_wind), normalize(K));',
+
+			'float S = (1.0 / (2.0 * PI)) * pow(k, -4.0) * (Bl + Bh) * (1.0 + Delta * (2.0 * cosPhi * cosPhi - 1.0));',
+
+			'float dk = 2.0 * PI / u_size;',
+			'float h = sqrt(S / 2.0) * dk;',
+
+			'if (K.x == 0.0 && K.y == 0.0) {',
+				'h = 0.0;', //no DC term
+			'}',
+			'gl_FragColor = vec4(h, 0.0, 0.0, 0.0);',
+		'}'
+	].join( '\n' )
+};
+OceanShaders[ 'ocean_phase' ] = {
+	uniforms: {
+		"u_phases": { value: null },
+		"u_deltaTime": { value: null },
+		"u_resolution": { value: null },
+		"u_size": { value: null }
+	},
+	fragmentShader: [
+		'precision highp float;',
+		'#include <common>',
+
+		'const float G = 9.81;',
+		'const float KM = 370.0;',
+
+		'varying vec2 vUV;',
+
+		'uniform sampler2D u_phases;',
+		'uniform float u_deltaTime;',
+		'uniform float u_resolution;',
+		'uniform float u_size;',
+
+		'float omega (float k) {',
+			'return sqrt(G * k * (1.0 + k * k / KM * KM));',
+		'}',
+
+		'void main (void) {',
+			'float deltaTime = 1.0 / 60.0;',
+			'vec2 coordinates = gl_FragCoord.xy - 0.5;',
+			'float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;',
+			'float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;',
+			'vec2 waveVector = (2.0 * PI * vec2(n, m)) / u_size;',
+
+			'float phase = texture2D(u_phases, vUV).r;',
+			'float deltaPhase = omega(length(waveVector)) * u_deltaTime;',
+			'phase = mod(phase + deltaPhase, 2.0 * PI);',
+
+			'gl_FragColor = vec4(phase, 0.0, 0.0, 0.0);',
+		'}'
+	].join( '\n' )
+};
+OceanShaders[ 'ocean_spectrum' ] = {
+	uniforms: {
+		"u_size": { value: null },
+		"u_resolution": { value: null },
+		"u_choppiness": { value: null },
+		"u_phases": { value: null },
+		"u_initialSpectrum": { value: null }
+	},
+	fragmentShader: [
+		'precision highp float;',
+		'#include <common>',
+
+		'const float G = 9.81;',
+		'const float KM = 370.0;',
+
+		'varying vec2 vUV;',
+
+		'uniform float u_size;',
+		'uniform float u_resolution;',
+		'uniform float u_choppiness;',
+		'uniform sampler2D u_phases;',
+		'uniform sampler2D u_initialSpectrum;',
+
+		'vec2 multiplyComplex (vec2 a, vec2 b) {',
+			'return vec2(a[0] * b[0] - a[1] * b[1], a[1] * b[0] + a[0] * b[1]);',
+		'}',
+
+		'vec2 multiplyByI (vec2 z) {',
+			'return vec2(-z[1], z[0]);',
+		'}',
+
+		'float omega (float k) {',
+			'return sqrt(G * k * (1.0 + k * k / KM * KM));',
+		'}',
+
+		'void main (void) {',
+			'vec2 coordinates = gl_FragCoord.xy - 0.5;',
+			'float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;',
+			'float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;',
+			'vec2 waveVector = (2.0 * PI * vec2(n, m)) / u_size;',
+
+			'float phase = texture2D(u_phases, vUV).r;',
+			'vec2 phaseVector = vec2(cos(phase), sin(phase));',
+
+			'vec2 h0 = texture2D(u_initialSpectrum, vUV).rg;',
+			'vec2 h0Star = texture2D(u_initialSpectrum, vec2(1.0 - vUV + 1.0 / u_resolution)).rg;',
+			'h0Star.y *= -1.0;',
+
+			'vec2 h = multiplyComplex(h0, phaseVector) + multiplyComplex(h0Star, vec2(phaseVector.x, -phaseVector.y));',
+
+			'vec2 hX = -multiplyByI(h * (waveVector.x / length(waveVector))) * u_choppiness;',
+			'vec2 hZ = -multiplyByI(h * (waveVector.y / length(waveVector))) * u_choppiness;',
+
+			//no DC term
+			'if (waveVector.x == 0.0 && waveVector.y == 0.0) {',
+				'h = vec2(0.0);',
+				'hX = vec2(0.0);',
+				'hZ = vec2(0.0);',
+			'}',
+
+			'gl_FragColor = vec4(hX + multiplyByI(h), hZ);',
+		'}'
+	].join( '\n' )
+};
+OceanShaders[ 'ocean_normals' ] = {
+	uniforms: {
+		"u_displacementMap": { value: null },
+		"u_resolution": { value: null },
+		"u_size": { value: null }
+	},
+	fragmentShader: [
+		'precision highp float;',
+
+		'varying vec2 vUV;',
+
+		'uniform sampler2D u_displacementMap;',
+		'uniform float u_resolution;',
+		'uniform float u_size;',
+
+		'void main (void) {',
+			'float texel = 1.0 / u_resolution;',
+			'float texelSize = u_size / u_resolution;',
+
+			'vec3 center = texture2D(u_displacementMap, vUV).rgb;',
+			'vec3 right = vec3(texelSize, 0.0, 0.0) + texture2D(u_displacementMap, vUV + vec2(texel, 0.0)).rgb - center;',
+			'vec3 left = vec3(-texelSize, 0.0, 0.0) + texture2D(u_displacementMap, vUV + vec2(-texel, 0.0)).rgb - center;',
+			'vec3 top = vec3(0.0, 0.0, -texelSize) + texture2D(u_displacementMap, vUV + vec2(0.0, -texel)).rgb - center;',
+			'vec3 bottom = vec3(0.0, 0.0, texelSize) + texture2D(u_displacementMap, vUV + vec2(0.0, texel)).rgb - center;',
+
+			'vec3 topRight = cross(right, top);',
+			'vec3 topLeft = cross(top, left);',
+			'vec3 bottomLeft = cross(left, bottom);',
+			'vec3 bottomRight = cross(bottom, right);',
+
+			'gl_FragColor = vec4(normalize(topRight + topLeft + bottomLeft + bottomRight), 1.0);',
+		'}'
+	].join( '\n' )
+};
+OceanShaders[ 'ocean_main' ] = {
+	uniforms: {
+		"u_displacementMap": { value: null },
+		"u_normalMap": { value: null },
+		"u_geometrySize": { value: null },
+		"u_size": { value: null },
+		"u_projectionMatrix": { value: null },
+		"u_viewMatrix": { value: null },
+		"u_cameraPosition": { value: null },
+		"u_skyColor": { value: null },
+		"u_oceanColor": { value: null },
+		"u_sunDirection": { value: null },
+		"u_exposure": { value: null }
+	},
+	vertexShader: [
+		'precision highp float;',
+
+		'varying vec3 vPos;',
+		'varying vec2 vUV;',
+
+		'uniform mat4 u_projectionMatrix;',
+		'uniform mat4 u_viewMatrix;',
+		'uniform float u_size;',
+		'uniform float u_geometrySize;',
+		'uniform sampler2D u_displacementMap;',
+
+		'void main (void) {',
+			'vec3 newPos = position + texture2D(u_displacementMap, uv).rgb * (u_geometrySize / u_size);',
+			'vPos = newPos;',
+			'vUV = uv;',
+			'gl_Position = u_projectionMatrix * u_viewMatrix * vec4(newPos, 1.0);',
+		'}'
+	].join( '\n' ),
+	fragmentShader: [
+		'precision highp float;',
+
+		'varying vec3 vPos;',
+		'varying vec2 vUV;',
+
+		'uniform sampler2D u_displacementMap;',
+		'uniform sampler2D u_normalMap;',
+		'uniform vec3 u_cameraPosition;',
+		'uniform vec3 u_oceanColor;',
+		'uniform vec3 u_skyColor;',
+		'uniform vec3 u_sunDirection;',
+		'uniform float u_exposure;',
+
+		'vec3 hdr (vec3 color, float exposure) {',
+			'return 1.0 - exp(-color * exposure);',
+		'}',
+
+		'void main (void) {',
+			'vec3 normal = texture2D(u_normalMap, vUV).rgb;',
+
+			'vec3 view = normalize(u_cameraPosition - vPos);',
+			'float fresnel = 0.02 + 0.98 * pow(1.0 - dot(normal, view), 5.0);',
+			'vec3 sky = fresnel * u_skyColor;',
+
+			'float diffuse = clamp(dot(normal, normalize(u_sunDirection)), 0.0, 1.0);',
+			'vec3 water = (1.0 - fresnel) * u_oceanColor * u_skyColor * diffuse;',
+
+			'vec3 color = sky + water;',
+
+			'gl_FragColor = vec4(hdr(color, u_exposure), 1.0);',
+		'}'
+	].join( '\n' )
+};
+
+export { OceanShaders };

+ 1 - 1
examples/webgl_gpgpu_birds.html

@@ -28,7 +28,7 @@
 		<script src="js/libs/stats.min.js"></script>
 		<script src="js/libs/dat.gui.min.js"></script>
 
-		<script src="js/GPUComputationRenderer.js"></script>
+		<script src="js/misc/GPUComputationRenderer.js"></script>
 
 		<!--
 		TODO: If you're reading this, you may wish to improve this example by

+ 1 - 1
examples/webgl_gpgpu_protoplanet.html

@@ -25,7 +25,7 @@
 		<script src="js/libs/dat.gui.min.js"></script>
 		<script src="js/controls/OrbitControls.js"></script>
 
-		<script src="js/GPUComputationRenderer.js"></script>
+		<script src="js/misc/GPUComputationRenderer.js"></script>
 
 
 		<!-- Fragment shader for protoplanet's position -->

+ 1 - 1
examples/webgl_gpgpu_water.html

@@ -22,7 +22,7 @@
 		<script src="js/controls/OrbitControls.js"></script>
 		<script src="js/math/SimplexNoise.js"></script>
 
-		<script src="js/GPUComputationRenderer.js"></script>
+		<script src="js/misc/GPUComputationRenderer.js"></script>
 
 
 		<!-- This is the 'compute shader' for the water heightmap: -->

+ 6 - 6
examples/webgl_materials_cars.html

@@ -37,7 +37,7 @@
 		<script src="js/pmrem/PMREMGenerator.js"></script>
 		<script src="js/pmrem/PMREMCubeUVPacker.js"></script>
 
-		<script src="js/Car.js"></script>
+		<script src="js/misc/CarControls.js"></script>
 
 		<script src="js/WebGL.js"></script>
 		<script src="js/libs/stats.min.js"></script>
@@ -59,8 +59,8 @@
 			var followCamera = document.getElementById( 'camera-toggle' );
 
 			var clock = new THREE.Clock();
-			var car = new THREE.Car();
-			car.turningRadius = 75;
+			var carControls = new THREE.CarControls();
+			carControls.turningRadius = 75;
 
 			var carParts = {
 				body: [],
@@ -156,7 +156,7 @@
 
 					carModel = gltf.scene.children[ 0 ];
 
-					car.setModel( carModel );
+					carControls.setModel( carModel );
 
 					carModel.traverse( function ( child ) {
 
@@ -289,14 +289,14 @@
 
 				if ( carModel ) {
 
-					car.update( delta / 3 );
+					carControls.update( delta / 3 );
 
 					console.log(   );
 
 					if ( carModel.position.length() > 200 ) {
 
 						carModel.position.set( 0, 0, 0 );
-						car.speed = 0;
+						carControls.speed = 0;
 
 					}
 

+ 1 - 1
examples/webgl_physics_convex_break.html

@@ -20,7 +20,7 @@
 	<script src="js/controls/OrbitControls.js"></script>
 	<script src="js/WebGL.js"></script>
 	<script src="js/libs/stats.min.js"></script>
-	<script src="js/ConvexObjectBreaker.js"></script>
+	<script src="js/misc/ConvexObjectBreaker.js"></script>
 	<script src="js/math/ConvexHull.js"></script>
 	<script src="js/geometries/ConvexGeometry.js"></script>
 

+ 1 - 1
examples/webgl_shaders_ocean2.html

@@ -17,7 +17,7 @@
 		<script src="js/libs/dat.gui.min.js"></script>
 		<script src="js/controls/OrbitControls.js"></script>
 		<script src="js/shaders/OceanShaders.js"></script>
-		<script src="js/Ocean.js"></script>
+		<script src="js/misc/Ocean.js"></script>
 
 		<script>
 			var stats = new Stats();

+ 8 - 1
utils/modularize.js

@@ -11,6 +11,7 @@ var dstFolder = __dirname + '/../examples/jsm/';
 
 var files = [
 	{ path: 'animation/AnimationClipCreator.js', dependencies: [], ignoreList: [] },
+	{ path: 'animation/CCDIKSolver.js', dependencies: [], ignoreList: [ 'SkinnedMesh' ] },
 	{ path: 'animation/TimelinerController.js', dependencies: [], ignoreList: [] },
 
 	{ path: 'cameras/CinematicCamera.js', dependencies: [ { name: 'BokehShader', path: 'shaders/BokehShader2.js' }, { name: 'BokehDepthShader', path: 'shaders/BokehShader2.js' } ], ignoreList: [] },
@@ -65,12 +66,14 @@ var files = [
 
 	{ path: 'loaders/3MFLoader.js', dependencies: [], ignoreList: [] },
 	{ path: 'loaders/AMFLoader.js', dependencies: [], ignoreList: [] },
+	{ path: 'loaders/AWDLoader.js', dependencies: [], ignoreList: [] },
 	{ path: 'loaders/AssimpJSONLoader.js', dependencies: [], ignoreList: [] },
 	{ path: 'loaders/AssimpLoader.js', dependencies: [], ignoreList: [] },
 	{ path: 'loaders/BabylonLoader.js', dependencies: [], ignoreList: [] },
 	{ path: 'loaders/BVHLoader.js', dependencies: [], ignoreList: [ 'Bones' ] },
 	{ path: 'loaders/ColladaLoader.js', dependencies: [ { name: 'TGALoader', path: 'loaders/TGALoader.js' } ], ignoreList: [] },
 	{ path: 'loaders/DDSLoader.js', dependencies: [], ignoreList: [] },
+	{ path: 'loaders/DRACOLoader.js', dependencies: [], ignoreList: [ 'LoadingManager' ] },
 	{ path: 'loaders/EXRLoader.js', dependencies: [], ignoreList: [] },
 	{ path: 'loaders/FBXLoader.js', dependencies: [ { name: 'TGALoader', path: 'loaders/TGALoader.js' }, { name: 'NURBSCurve', path: 'curves/NURBSCurve.js' } ], ignoreList: [] },
 	{ path: 'loaders/GCodeLoader.js', dependencies: [], ignoreList: [] },
@@ -92,6 +95,7 @@ var files = [
 	{ path: 'loaders/SVGLoader.js', dependencies: [], ignoreList: [] },
 	{ path: 'loaders/TDSLoader.js', dependencies: [], ignoreList: [] },
 	{ path: 'loaders/TGALoader.js', dependencies: [], ignoreList: [] },
+	{ path: 'loaders/TTFLoader.js', dependencies: [], ignoreList: [ 'Font' ] },
 	{ path: 'loaders/VRMLLoader.js', dependencies: [], ignoreList: [] },
 	{ path: 'loaders/VTKLoader.js', dependencies: [], ignoreList: [] },
 
@@ -101,7 +105,9 @@ var files = [
 	{ path: 'math/Lut.js', dependencies: [], ignoreList: [] },
 	{ path: 'math/SimplexNoise.js', dependencies: [], ignoreList: [] },
 
-	{ path: 'misc/ConvexObjectBreaker.js', dependencies: [ { name: 'ConvexBufferGeometry', path: 'geometries/ConvexGeometry.js' } ], ignoreList: [] },
+	{ path: 'misc/CarControls.js', dependencies: [], ignoreList: [] },
+	{ path: 'misc/ConvexObjectBreaker.js', dependencies: [ { name: 'ConvexBufferGeometry', path: 'geometries/ConvexGeometry.js' } ], ignoreList: [ 'Matrix4' ] },
+	{ path: 'misc/Ocean.js', dependencies: [ { name: 'OceanShaders', path: 'shaders/OceanShaders.js' } ], ignoreList: [] },
 
 	{ path: 'modifiers/ExplodeModifier.js', dependencies: [], ignoreList: [] },
 	{ path: 'modifiers/SimplifyModifier.js', dependencies: [], ignoreList: [] },
@@ -184,6 +190,7 @@ var files = [
 	{ path: 'shaders/LuminosityShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/MirrorShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/NormalMapShader.js', dependencies: [], ignoreList: [] },
+	{ path: 'shaders/OceanShaders.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/ParallaxShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/PixelShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/RGBShiftShader.js', dependencies: [], ignoreList: [] },

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است