2
0
Эх сурвалжийг харах

Merge pull request #15295 from mrdoob/revert-15252-dev9

Revert "Remove JSONLoader from THREE namespace"
Michael Herzog 6 жил өмнө
parent
commit
c1c6bd78b7

+ 4 - 1
docs/api/en/animation/AnimationClip.html

@@ -96,7 +96,10 @@
 		<p>
 			Returns an array of new AnimationClips created from the [page:Geometry.morphTargets morph
 			target sequences] of a geometry, trying to sort morph target names into animation-group-based
-			patterns like "Walk_001, Walk_002, Run_001, Run_002 ...".
+			patterns like "Walk_001, Walk_002, Run_001, Run_002 ..."<br /><br />
+
+			This method is called by the [page:JSONLoader] internally, and it uses
+			[page:.CreateFromMorphTargetSequence CreateFromMorphTargetSequence].
 		</p>
 
 		<h3>[method:AnimationClip CreateFromMorphTargetSequence]( [param:String name], [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>

+ 122 - 0
docs/api/en/loaders/JSONLoader.html

@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<p class="desc">
+			A loader for loading objects in JSON format.
+			This uses the [page:FileLoader] internally for loading files.
+		</p>
+
+		<h2>Example</h2>
+
+		<p>
+		[example:webgl_loader_json WebGL / loader / json]
+		</p>
+
+		<code>
+		// instantiate a loader
+		var loader = new THREE.JSONLoader();
+
+		// load a resource
+		loader.load(
+			// resource URL
+			'models/animated/monster/monster.js',
+
+			// onLoad callback
+			function ( geometry, materials ) {
+				var material = materials[ 0 ];
+				var object = new THREE.Mesh( geometry, material );
+				scene.add( object );
+			},
+
+			// onProgress callback
+			function ( xhr ) {
+				console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
+			},
+
+			// onError callback
+			function( err ) {
+				console.log( 'An error happened' );
+			}
+		);
+		</code>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:LoadingManager manager] )</h3>
+		<p>
+		[page:LoadingManager manager] — The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].
+		</p>
+		<p>
+		Creates a new [name].
+		</p>
+
+		<h2>Properties</h2>
+
+		<h3>[property:String crossOrigin]</h3>
+		<p>
+		If set, assigns the [link:https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes crossOrigin]
+	 attribute of the image to the value of *crossOrigin*, prior to starting the load. Default is *"anonymous"*.
+		</p>
+
+		<h3>[property:LoadingManager manager]</h3>
+		<p>
+			The [page:LoadingManager loadingManager]  the loader is using. Default is [page:DefaultLoadingManager].
+		</p>
+
+		<h3>[property:String withCredentials]</h3>
+		<p>
+			Whether the XMLHttpRequest uses credentials.
+			Default is *false*.
+		</p>
+
+		<h2>Methods</h2>
+
+		<h3>[method:null load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
+		<p>
+		[page:String url] — the path or URL to the file. This can also be a
+			[link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs Data URI]..<br />
+		[page:Function onLoad] — Will be called when load completes. The argument will be the loaded text response.<br />
+		[page:Function onProgress] — Will be called while load progresses. The argument will be the XMLHttpRequest instance, which contains .[page:Integer total] and .[page:Integer loaded] bytes.<br />
+		[page:Function onError] — Will be called when load errors.<br />
+		</p>
+		<p>
+		Begin loading from url and pass the <em>JSON</em> to onLoad.
+		</p>
+
+		<h3>[method:Object3D parse]( [param:Object json], [param:String path] )</h3>
+		<p>
+		[page:String json] — JSON object to parse.<br />
+		[page:String path] — Base path for resources if no resource path is defined.<br /><br />
+
+		Parse a <em>JSON</em> structure and return an [page:object] containing the parsed [page:Geometry geometry] and [page:Array materials].
+		</p>
+
+		<h3>[method:JSONLoader setCrossOrigin]( [param:String value] )</h3>
+		<p>
+			Set the [page:.crossOrigin] attribute.
+		</p>
+
+		<h3>[method:JSONLoader setPath]( [param:String value] )</h3>
+		<p>
+			Set the base path for the original file.
+		</p>
+
+		<h3>[method:JSONLoader setResourcePath]( [param:String value] )</h3>
+		<p>
+			Set the base path for dependent resources like textures.
+		</p>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 3 - 0
docs/api/en/loaders/ObjectLoader.html

@@ -13,6 +13,8 @@
 		<p class="desc">
 			A loader for loading a JSON resource in the [link:https://github.com/mrdoob/three.js/wiki/JSON-Object-Scene-format-4 JSON Object/Scene format].<br /><br />
 
+			Note that this loader can't load geometries of type [page:Geometry]. Use [page:JSONLoader] instead for that.<br /><br />
+
 			This uses the [page:FileLoader] internally for loading files.
 		</p>
 
@@ -117,6 +119,7 @@
 		[page:Object json] — required. The JSON source to parse.<br /><br />
 
 		This is used [page:.parse] to parse any [page:Geometry geometries] or [page:BufferGeometry buffer geometries]  in the JSON structure.
+		Internally it uses [page:JSONLoader] for geometries and [page:BufferGeometryLoader] for buffer geometries.
 		</p>
 
 		<h3>[method:Object3D parseMaterials]( [param:Object json] )</h3>

+ 4 - 1
docs/api/zh/animation/AnimationClip.html

@@ -91,7 +91,10 @@
 		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [param:String name], [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>
 		<p>
 			返回从几何体的变形目标序列([page:Geometry.morphTargets morph
-			target sequences])创建的新动画剪辑(AnimationClip)数组,并尝试将变形目标名称分类为基于动画组的模式,如“Walk_001、Walk_002、Run_001、Run_002……”。
+			target sequences])创建的新动画剪辑(AnimationClip)数组,并尝试将变形目标名称分类为基于动画组的模式,如“Walk_001、Walk_002、Run_001、Run_002……”。<br /><br />
+
+			该方法被[page:JSONLoader]内部调用, 并且它使用了
+			[page:.CreateFromMorphTargetSequence CreateFromMorphTargetSequence].
 		</p>
 
 		<h3>[method:AnimationClip CreateFromMorphTargetSequence]( [param:String name], [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>

+ 119 - 0
docs/api/zh/loaders/JSONLoader.html

@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<p class="desc">
+            以JSON格式来加载对象的加载器。
+			此加载器内部用 [page:FileLoader] 来加载文件。
+		</p>
+
+		<h2>例子</h2>
+
+		<p>
+		[example:webgl_loader_json WebGL / loader / json]<br />
+		[example:webgl_loader_json_objconverter WebGL / loader / json / objconverter]
+		</p>
+
+		<code>
+		// 初始化一个加载器
+		var loader = new THREE.JSONLoader();
+
+		// 加载一个资源
+		loader.load(
+			// 资源URL
+			'models/animated/monster/monster.js',
+
+			// onLoad的回调
+			function ( geometry, materials ) {
+				var material = materials[ 0 ];
+				var object = new THREE.Mesh( geometry, material );
+				scene.add( object );
+			},
+
+			// onProgress的回调
+			function ( xhr ) {
+				console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
+			},
+
+			// onError的回调
+			function( err ) {
+				console.log( 'An error happened' );
+			}
+		);
+		</code>
+
+		<h2>构造函数</h2>
+
+		<h3>[name]( [param:LoadingManager manager] )</h3>
+		<p>
+		[page:LoadingManager manager] — 加载器所使用的 [page:LoadingManager loadingManager]。 默认为 [page:LoadingManager THREE.DefaultLoadingManager].
+		</p>
+		<p>
+		创建一个新的 [name].
+		</p>
+
+		<h2>属性</h2>
+
+		<h3>[property:String crossOrigin]</h3>
+		<p>
+		如果设置了,在开始加载前, 将为图片分配 [link:https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes crossOrigin]
+	 属性,其值为 *crossOrigin*,默认为"anonymous"。
+		</p>
+
+		<h3>[property:LoadingManager manager]</h3>
+		<p>
+			加载器正在使用的 [page:LoadingManager loadingManager] 。 默认为 [page:DefaultLoadingManager].
+		</p>
+
+		<h3>[property:String withCredentials]</h3>
+		<p>
+            XMLHttpRequest请求是否使用了证书。
+			默认为 *false*.
+		</p>
+
+		<h2>方法</h2>
+
+		<h3>[method:null load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
+		<p>
+		[page:String url] — 文件的URL或者路径,也可以为
+			[link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs Data URI]..<br />
+		[page:Function onLoad] — 加载完成时将调用。回调参数将是加载的响应。<br />
+		[page:Function onProgress] — 将在加载过程中进行调用。参数为XMLHttpRequest实例,
+            其中包含 [page:Integer total] 和 [page:Integer loaded] 字节。<br />
+		[page:Function onError] — 在加载错误时被调用。<br />
+		</p>
+		<p>
+		从URL中进行加载并将 <em>JSON</em> 传递给 onLoad。
+		</p>
+
+		<h3>[method:JSONLoader setCrossOrigin]( [param:String value] )</h3>
+		<p>
+			设置 [page:.crossOrigin] 的属性。
+		</p>
+
+		<h3>[method:JSONLoader setTexturePath]( [param:String texturePath] )</h3>
+		<p>
+            设置加载文件的基本路径或URL。当加载同一目录中的许多模型,此方法将很有用。
+		</p>
+
+		<h3>[method:Object3D parse]( [param:Object json], [param:String texturePath] )</h3>
+		<p>
+		[page:String json] — 需要解析的JSON对象。<br />
+		[page:String texturePath] — 纹理的基本路径。<br /><br />
+
+		解析要给 <em>JSON</em> 结构并返回 [page:object] 包含 [page:Geometry geometry] 和 [page:Array materials] 的对象.
+		</p>
+
+		<h2>源</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 3 - 0
docs/api/zh/loaders/ObjectLoader.html

@@ -13,6 +13,8 @@
 		<p class="desc">
 			A loader for loading a JSON resource in the [link:https://github.com/mrdoob/three.js/wiki/JSON-Object-Scene-format-4 JSON Object/Scene format].<br /><br />
 
+			Note that this loader can't load geometries of type [page:Geometry]. Use [page:JSONLoader] instead for that.<br /><br />
+
 			此加载器内部使用[page:FileLoader]进行加载文件。
 		</p>
 
@@ -116,6 +118,7 @@
 		[page:Object json] — 必选参数,需要被解析的JSON源。<br /><br />
 
             此函数以JSON结构,用[page:.parse]去解析[page:Geometry geometries]或[page:BufferGeometry buffer geometries]。
+            在内部,它使用JSONLoader作为几何加载器,使用BufferGeometryLoader作为几何缓冲区加载器。
 		</p>
 
 		<h3>[method:Object3D parseMaterials]( [param:Object json] )</h3>

+ 2 - 0
docs/list.js

@@ -233,6 +233,7 @@ var list = {
 				"FontLoader": "api/en/loaders/FontLoader",
 				"ImageBitmapLoader": "api/en/loaders/ImageBitmapLoader",
 				"ImageLoader": "api/en/loaders/ImageLoader",
+				"JSONLoader": "api/en/loaders/JSONLoader",
 				"Loader": "api/en/loaders/Loader",
 				"LoaderUtils": "api/en/loaders/LoaderUtils",
 				"MaterialLoader": "api/en/loaders/MaterialLoader",
@@ -657,6 +658,7 @@ var list = {
 				"FontLoader": "api/zh/loaders/FontLoader",
 				"ImageBitmapLoader": "api/zh/loaders/ImageBitmapLoader",
 				"ImageLoader": "api/zh/loaders/ImageLoader",
+				"JSONLoader": "api/zh/loaders/JSONLoader",
 				"Loader": "api/zh/loaders/Loader",
 				"LoaderUtils": "api/zh/loaders/LoaderUtils",
 				"MaterialLoader": "api/zh/loaders/MaterialLoader",

+ 1 - 0
docs/manual/en/introduction/Animation-system.html

@@ -107,6 +107,7 @@
 		</p>
 
 			<ul>
+				<li>[page:JSONLoader THREE.JSONLoader]</li>
 				<li>[page:ObjectLoader THREE.ObjectLoader]</li>
 				<li>THREE.BVHLoader</li>
 				<li>THREE.ColladaLoader</li>

+ 1 - 1
docs/manual/en/introduction/Loading-3D-models.html

@@ -79,7 +79,7 @@
 	<h2>Loading</h2>
 
 	<p>
-		Only a few loaders like [page:ObjectLoader] are included by default with
+		Only a few loaders ([page:ObjectLoader] and [page:JSONLoader]) are included by default with
 		three.js — others should be added to your page individually. Depending on your
 		preference and comfort with build tools, choose one of the following:
 	</p>

+ 3 - 2
docs/manual/zh/introduction/Animation-system.html

@@ -32,7 +32,7 @@
 			<br /><br />
 			每个* AnimationClip *通常保存对象的某个活动的数据。 如果
 			mesh是一个字符,例如,可以有一个用于walkcycle的动画片段,第二个
-			跳跃,三分之一的回避等等。
+			跳跃,三分之一的回避等等。		
 		</p>
 
 		<h3>Keyframe Tracks(关键帧轨道)</h3>
@@ -83,10 +83,11 @@
 		<p class="desc">
 			请注意,并非所有模型格式都包含动画(尤其是OBJ,没有),而且只有一些
 			three.js加载器支持[page:AnimationClip AnimationClip]序列。 以下几个<i>确实</ i>
-			支持此动画类型:
+			支持此动画类型:		
 		</p>
 
 			<ul>
+				<li>[page:JSONLoader THREE.JSONLoader]</li>
 				<li>[page:ObjectLoader THREE.ObjectLoader]</li>
 				<li>THREE.BVHLoader</li>
 				<li>THREE.ColladaLoader</li>

+ 49 - 0
editor/js/Loader.js

@@ -574,6 +574,55 @@ var Loader = function ( editor ) {
 
 				break;
 
+			case 'geometry':
+
+				var loader = new THREE.JSONLoader();
+				loader.setResourcePath( scope.texturePath );
+
+				var result = loader.parse( data );
+
+				var geometry = result.geometry;
+				var material;
+
+				if ( result.materials !== undefined ) {
+
+					if ( result.materials.length > 1 ) {
+
+						material = new THREE.MultiMaterial( result.materials );
+
+					} else {
+
+						material = result.materials[ 0 ];
+
+					}
+
+				} else {
+
+					material = new THREE.MeshStandardMaterial();
+
+				}
+
+				geometry.sourceType = "ascii";
+				geometry.sourceFile = file.name;
+
+				var mesh;
+
+				if ( geometry.animation && geometry.animation.hierarchy ) {
+
+					mesh = new THREE.SkinnedMesh( geometry, material );
+
+				} else {
+
+					mesh = new THREE.Mesh( geometry, material );
+
+				}
+
+				mesh.name = filename;
+
+				editor.execute( new AddObjectCommand( mesh ) );
+
+				break;
+
 			case 'object':
 
 				var loader = new THREE.ObjectLoader();

+ 49 - 1
editor/js/libs/tern-threejs/threejs.js

@@ -1714,6 +1714,54 @@
       "!doc": "A loader for loading an [page:Image].",
       "!type": "fn(manager: +THREE.LoadingManager)"
     },
+    "JSONLoader": {
+      "!url": "http://threejs.org/docs/#Reference/loaders/JSONLoader",
+      "prototype": {
+        "!proto": "THREE.Loader.prototype",
+        "withCredentials": {
+          "!type": "boolean",
+          "!doc": "If true, the ajax request will use cookies."
+        },
+        "onLoadStart": {
+          "!type": "function",
+          "!doc": "The default is a function with empty body."
+        },
+        "onLoadComplete": {
+          "!type": "function",
+          "!doc": "The default is a function with empty body."
+        },
+        "load": {
+          "!type": "fn(url: string, callback: function, texturePath: string)",
+          "!doc": "[page:String url] — required<br>\n\t\t[page:Function callback] — required. Will be called when load completes. The arguments will be the loaded [page:Object3D] and the loaded [page:Array materials].<br>\n\t\t[page:String texturePath] — optional. If not specified, textures will be assumed to be in the same folder as the Javascript model file."
+        },
+        "loadAjaxJSON": {
+          "!type": "fn(context: +THREE.JSONLoader, url: string, callback: function, texturePath: string, callbackProgress: function)",
+          "!doc": "Begin loading from url and call <em>callback</em> with the parsed response content."
+        },
+        "parse": {
+          "!type": "fn(json: object, texturePath: string) -> +THREE.Object3D",
+          "!doc": "Parse a <em>JSON</em> structure and return an [page:Object] containing the parsed .[page:Geometry] and .[page:Array materials]."
+        },
+        "updateProgress": {
+          "!type": "fn(progress: object)",
+          "!doc": "Updates the DOM object with the progress made."
+        },
+        "createMaterial": {
+          "!type": "fn(m: object, texturePath: string) -> +THREE.Material",
+          "!doc": "Creates the Material based on the parameters m."
+        },
+        "initMaterials": {
+          "!type": "fn(materials: [], texturePath: string) -> []",
+          "!doc": "Creates an array of [page:Material] based on the array of parameters m. The index of the parameters decide the correct index of the materials."
+        },
+        "extractUrlBase": {
+          "!type": "fn(url: string) -> string",
+          "!doc": "Extract the base from the URL."
+        }
+      },
+      "!doc": "A loader for loading objects in JSON format.",
+      "!type": "fn()"
+    },
     "Loader": {
       "!url": "http://threejs.org/docs/#Reference/loaders/Loader",
       "prototype": {
@@ -1856,7 +1904,7 @@
           "!doc": "[page:String value] — The crossOrigin string to implement CORS for loading the url from a different domain that allows CORS."
         }
       },
-      "!doc": "A loader for loading a JSON resource.",
+      "!doc": "A loader for loading a JSON resource. Unlike the [page:JSONLoader], this one make use of the <em>.type</em> attributes of objects to map them to their original classes.",
       "!type": "fn(manager: +THREE.LoadingManager)"
     },
     "PDBLoader": {

+ 1 - 0
examples/files.js

@@ -90,6 +90,7 @@ var files = {
 		"webgl_loader_gltf",
 		"webgl_loader_gltf_extensions",
 		"webgl_loader_imagebitmap",
+		"webgl_loader_json",
 		"webgl_loader_json_claraio",
 		"webgl_loader_kmz",
 		"webgl_loader_md2",

+ 0 - 578
examples/js/loaders/deprecated/JSONLoader.js

@@ -1,578 +0,0 @@
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- */
-
-THREE.JSONLoader = ( function () {
-
-	function JSONLoader( manager ) {
-
-		if ( typeof manager === 'boolean' ) {
-
-			console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' );
-			manager = undefined;
-
-		}
-
-		this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-
-		this.withCredentials = false;
-
-	}
-
-	Object.assign( JSONLoader.prototype, {
-
-		crossOrigin: 'anonymous',
-
-		load: function ( url, onLoad, onProgress, onError ) {
-
-			var scope = this;
-
-			var path = ( this.path === undefined ) ? THREE.LoaderUtils.extractUrlBase( url ) : this.path;
-
-			var loader = new THREE.FileLoader( this.manager );
-			loader.setPath( this.path );
-			loader.setWithCredentials( this.withCredentials );
-			loader.load( url, function ( text ) {
-
-				var json = JSON.parse( text );
-				var metadata = json.metadata;
-
-				if ( metadata !== undefined ) {
-
-					var type = metadata.type;
-
-					if ( type !== undefined ) {
-
-						if ( type.toLowerCase() === 'object' ) {
-
-							console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' );
-							return;
-
-						}
-
-					}
-
-				}
-
-				var object = scope.parse( json, path );
-				onLoad( object.geometry, object.materials );
-
-			}, onProgress, onError );
-
-		},
-
-		setPath: function ( value ) {
-
-			this.path = value;
-			return this;
-
-		},
-
-		setResourcePath: function ( value ) {
-
-			this.resourcePath = value;
-			return this;
-
-		},
-
-		setCrossOrigin: function ( value ) {
-
-			this.crossOrigin = value;
-			return this;
-
-		},
-
-		parse: ( function () {
-
-			function parseModel( json, geometry ) {
-
-				function isBitSet( value, position ) {
-
-					return value & ( 1 << position );
-
-				}
-
-				var i, j, fi,
-
-					offset, zLength,
-
-					colorIndex, normalIndex, uvIndex, materialIndex,
-
-					type,
-					isQuad,
-					hasMaterial,
-					hasFaceVertexUv,
-					hasFaceNormal, hasFaceVertexNormal,
-					hasFaceColor, hasFaceVertexColor,
-
-					vertex, face, faceA, faceB, hex, normal,
-
-					uvLayer, uv, u, v,
-
-					faces = json.faces,
-					vertices = json.vertices,
-					normals = json.normals,
-					colors = json.colors,
-
-					scale = json.scale,
-
-					nUvLayers = 0;
-
-
-				if ( json.uvs !== undefined ) {
-
-					// disregard empty arrays
-
-					for ( i = 0; i < json.uvs.length; i ++ ) {
-
-						if ( json.uvs[ i ].length ) nUvLayers ++;
-
-					}
-
-					for ( i = 0; i < nUvLayers; i ++ ) {
-
-						geometry.faceVertexUvs[ i ] = [];
-
-					}
-
-				}
-
-				offset = 0;
-				zLength = vertices.length;
-
-				while ( offset < zLength ) {
-
-					vertex = new THREE.Vector3();
-
-					vertex.x = vertices[ offset ++ ] * scale;
-					vertex.y = vertices[ offset ++ ] * scale;
-					vertex.z = vertices[ offset ++ ] * scale;
-
-					geometry.vertices.push( vertex );
-
-				}
-
-				offset = 0;
-				zLength = faces.length;
-
-				while ( offset < zLength ) {
-
-					type = faces[ offset ++ ];
-
-					isQuad = isBitSet( type, 0 );
-					hasMaterial = isBitSet( type, 1 );
-					hasFaceVertexUv = isBitSet( type, 3 );
-					hasFaceNormal = isBitSet( type, 4 );
-					hasFaceVertexNormal = isBitSet( type, 5 );
-					hasFaceColor = isBitSet( type, 6 );
-					hasFaceVertexColor = isBitSet( type, 7 );
-
-					// console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);
-
-					if ( isQuad ) {
-
-						faceA = new THREE.Face3();
-						faceA.a = faces[ offset ];
-						faceA.b = faces[ offset + 1 ];
-						faceA.c = faces[ offset + 3 ];
-
-						faceB = new THREE.Face3();
-						faceB.a = faces[ offset + 1 ];
-						faceB.b = faces[ offset + 2 ];
-						faceB.c = faces[ offset + 3 ];
-
-						offset += 4;
-
-						if ( hasMaterial ) {
-
-							materialIndex = faces[ offset ++ ];
-							faceA.materialIndex = materialIndex;
-							faceB.materialIndex = materialIndex;
-
-						}
-
-						// to get face <=> uv index correspondence
-
-						fi = geometry.faces.length;
-
-						if ( hasFaceVertexUv ) {
-
-							for ( i = 0; i < nUvLayers; i ++ ) {
-
-								uvLayer = json.uvs[ i ];
-
-								geometry.faceVertexUvs[ i ][ fi ] = [];
-								geometry.faceVertexUvs[ i ][ fi + 1 ] = [];
-
-								for ( j = 0; j < 4; j ++ ) {
-
-									uvIndex = faces[ offset ++ ];
-
-									u = uvLayer[ uvIndex * 2 ];
-									v = uvLayer[ uvIndex * 2 + 1 ];
-
-									uv = new THREE.Vector2( u, v );
-
-									if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv );
-									if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv );
-
-								}
-
-							}
-
-						}
-
-						if ( hasFaceNormal ) {
-
-							normalIndex = faces[ offset ++ ] * 3;
-
-							faceA.normal.set(
-								normals[ normalIndex ++ ],
-								normals[ normalIndex ++ ],
-								normals[ normalIndex ]
-							);
-
-							faceB.normal.copy( faceA.normal );
-
-						}
-
-						if ( hasFaceVertexNormal ) {
-
-							for ( i = 0; i < 4; i ++ ) {
-
-								normalIndex = faces[ offset ++ ] * 3;
-
-								normal = new THREE.Vector3(
-									normals[ normalIndex ++ ],
-									normals[ normalIndex ++ ],
-									normals[ normalIndex ]
-								);
-
-
-								if ( i !== 2 ) faceA.vertexNormals.push( normal );
-								if ( i !== 0 ) faceB.vertexNormals.push( normal );
-
-							}
-
-						}
-
-
-						if ( hasFaceColor ) {
-
-							colorIndex = faces[ offset ++ ];
-							hex = colors[ colorIndex ];
-
-							faceA.color.setHex( hex );
-							faceB.color.setHex( hex );
-
-						}
-
-
-						if ( hasFaceVertexColor ) {
-
-							for ( i = 0; i < 4; i ++ ) {
-
-								colorIndex = faces[ offset ++ ];
-								hex = colors[ colorIndex ];
-
-								if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) );
-								if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) );
-
-							}
-
-						}
-
-						geometry.faces.push( faceA );
-						geometry.faces.push( faceB );
-
-					} else {
-
-						face = new THREE.Face3();
-						face.a = faces[ offset ++ ];
-						face.b = faces[ offset ++ ];
-						face.c = faces[ offset ++ ];
-
-						if ( hasMaterial ) {
-
-							materialIndex = faces[ offset ++ ];
-							face.materialIndex = materialIndex;
-
-						}
-
-						// to get face <=> uv index correspondence
-
-						fi = geometry.faces.length;
-
-						if ( hasFaceVertexUv ) {
-
-							for ( i = 0; i < nUvLayers; i ++ ) {
-
-								uvLayer = json.uvs[ i ];
-
-								geometry.faceVertexUvs[ i ][ fi ] = [];
-
-								for ( j = 0; j < 3; j ++ ) {
-
-									uvIndex = faces[ offset ++ ];
-
-									u = uvLayer[ uvIndex * 2 ];
-									v = uvLayer[ uvIndex * 2 + 1 ];
-
-									uv = new THREE.Vector2( u, v );
-
-									geometry.faceVertexUvs[ i ][ fi ].push( uv );
-
-								}
-
-							}
-
-						}
-
-						if ( hasFaceNormal ) {
-
-							normalIndex = faces[ offset ++ ] * 3;
-
-							face.normal.set(
-								normals[ normalIndex ++ ],
-								normals[ normalIndex ++ ],
-								normals[ normalIndex ]
-							);
-
-						}
-
-						if ( hasFaceVertexNormal ) {
-
-							for ( i = 0; i < 3; i ++ ) {
-
-								normalIndex = faces[ offset ++ ] * 3;
-
-								normal = new THREE.Vector3(
-									normals[ normalIndex ++ ],
-									normals[ normalIndex ++ ],
-									normals[ normalIndex ]
-								);
-
-								face.vertexNormals.push( normal );
-
-							}
-
-						}
-
-
-						if ( hasFaceColor ) {
-
-							colorIndex = faces[ offset ++ ];
-							face.color.setHex( colors[ colorIndex ] );
-
-						}
-
-
-						if ( hasFaceVertexColor ) {
-
-							for ( i = 0; i < 3; i ++ ) {
-
-								colorIndex = faces[ offset ++ ];
-								face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) );
-
-							}
-
-						}
-
-						geometry.faces.push( face );
-
-					}
-
-				}
-
-			}
-
-			function parseSkin( json, geometry ) {
-
-				var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2;
-
-				if ( json.skinWeights ) {
-
-					for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) {
-
-						var x = json.skinWeights[ i ];
-						var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0;
-						var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0;
-						var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0;
-
-						geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) );
-
-					}
-
-				}
-
-				if ( json.skinIndices ) {
-
-					for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) {
-
-						var a = json.skinIndices[ i ];
-						var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0;
-						var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0;
-						var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0;
-
-						geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) );
-
-					}
-
-				}
-
-				geometry.bones = json.bones;
-
-				if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) {
-
-					console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' +
-						geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' );
-
-				}
-
-			}
-
-			function parseMorphing( json, geometry ) {
-
-				var scale = json.scale;
-
-				if ( json.morphTargets !== undefined ) {
-
-					for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) {
-
-						geometry.morphTargets[ i ] = {};
-						geometry.morphTargets[ i ].name = json.morphTargets[ i ].name;
-						geometry.morphTargets[ i ].vertices = [];
-
-						var dstVertices = geometry.morphTargets[ i ].vertices;
-						var srcVertices = json.morphTargets[ i ].vertices;
-
-						for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) {
-
-							var vertex = new THREE.Vector3();
-							vertex.x = srcVertices[ v ] * scale;
-							vertex.y = srcVertices[ v + 1 ] * scale;
-							vertex.z = srcVertices[ v + 2 ] * scale;
-
-							dstVertices.push( vertex );
-
-						}
-
-					}
-
-				}
-
-				if ( json.morphColors !== undefined && json.morphColors.length > 0 ) {
-
-					console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' );
-
-					var faces = geometry.faces;
-					var morphColors = json.morphColors[ 0 ].colors;
-
-					for ( var i = 0, l = faces.length; i < l; i ++ ) {
-
-						faces[ i ].color.fromArray( morphColors, i * 3 );
-
-					}
-
-				}
-
-			}
-
-			function parseAnimations( json, geometry ) {
-
-				var outputAnimations = [];
-
-				// parse old style Bone/Hierarchy animations
-				var animations = [];
-
-				if ( json.animation !== undefined ) {
-
-					animations.push( json.animation );
-
-				}
-
-				if ( json.animations !== undefined ) {
-
-					if ( json.animations.length ) {
-
-						animations = animations.concat( json.animations );
-
-					} else {
-
-						animations.push( json.animations );
-
-					}
-
-				}
-
-				for ( var i = 0; i < animations.length; i ++ ) {
-
-					var clip = THREE.AnimationClip.parseAnimation( animations[ i ], geometry.bones );
-					if ( clip ) outputAnimations.push( clip );
-
-				}
-
-				// parse implicit morph animations
-				if ( geometry.morphTargets ) {
-
-					// TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary.
-					var morphAnimationClips = THREE.AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 );
-					outputAnimations = outputAnimations.concat( morphAnimationClips );
-
-				}
-
-				if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations;
-
-			}
-
-			return function parse( json, path ) {
-
-				if ( json.data !== undefined ) {
-
-					// Geometry 4.0 spec
-					json = json.data;
-
-				}
-
-				if ( json.scale !== undefined ) {
-
-					json.scale = 1.0 / json.scale;
-
-				} else {
-
-					json.scale = 1.0;
-
-				}
-
-				var geometry = new THREE.Geometry();
-
-				parseModel( json, geometry );
-				parseSkin( json, geometry );
-				parseMorphing( json, geometry );
-				parseAnimations( json, geometry );
-
-				geometry.computeFaceNormals();
-				geometry.computeBoundingSphere();
-
-				if ( json.materials === undefined || json.materials.length === 0 ) {
-
-					return { geometry: geometry };
-
-				} else {
-
-					var materials = THREE.Loader.prototype.initMaterials( json.materials, this.resourcePath || path, this.crossOrigin );
-
-					return { geometry: geometry, materials: materials };
-
-				}
-
-			};
-
-		} )()
-
-	} );
-
-	return JSONLoader;
-
-} )();

+ 179 - 0
examples/webgl_loader_json.html

@@ -0,0 +1,179 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - loader -json</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				background:#777;
+				padding:0;
+				margin:0;
+				font-weight: bold;
+				overflow:hidden;
+			}
+
+			#info {
+				position: absolute;
+				top: 0px;
+				width: 100%;
+				color: #ffffff;
+				padding: 5px;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+			}
+
+			a {
+				color: #ffffff;
+			}
+		</style>
+	</head>
+	<body>
+
+		<div id="container"></div>
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> -
+			monster by <a href="http://www.3drt.com/downloads.htm" target="_blank" rel="noopener">3drt</a>
+		</div>
+
+		<script src="../build/three.js"></script>
+
+		<script src="js/WebGL.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script>
+
+			if ( WEBGL.isWebGLAvailable() === false ) {
+
+				document.body.appendChild( WEBGL.getWebGLErrorMessage() );
+
+			}
+
+			var container, stats, clock, mixer;
+			var camera, scene, renderer;
+
+			init();
+			animate();
+
+			function init() {
+
+				container = document.getElementById( 'container' );
+
+				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 2000 );
+				camera.position.set( 2, 4, 5 );
+
+				clock = new THREE.Clock();
+
+				scene = new THREE.Scene();
+				scene.fog = new THREE.FogExp2( 0x000000, 0.035 );
+
+				mixer = new THREE.AnimationMixer( scene );
+
+				var loader = new THREE.JSONLoader();
+				loader.load( 'models/json/legacy/monster/monster.js', function ( geometry, materials ) {
+
+					// adjust color a bit
+
+					var material = materials[ 0 ];
+					material.morphTargets = true;
+					material.color.setHex( 0xffaaaa );
+
+					for ( var i = 0; i < 729; i ++ ) {
+
+						var mesh = new THREE.Mesh( geometry, materials );
+
+						// random placement in a grid
+
+						var x = ( ( i % 27 ) - 13.5 ) * 2 + THREE.Math.randFloatSpread( 1 );
+						var z = ( Math.floor( i / 27 ) - 13.5 ) * 2 + THREE.Math.randFloatSpread( 1 );
+
+						mesh.position.set( x, 0, z );
+
+						var s = THREE.Math.randFloat( 0.00075, 0.001 );
+						mesh.scale.set( s, s, s );
+
+						mesh.rotation.y = THREE.Math.randFloat( - 0.25, 0.25 );
+
+						mesh.matrixAutoUpdate = false;
+						mesh.updateMatrix();
+
+						scene.add( mesh );
+
+						mixer.clipAction( geometry.animations[ 0 ], mesh )
+							.setDuration( 1 )			// one second
+							.startAt( - Math.random() )	// random phase (already running)
+							.play();					// let's go
+
+					}
+
+				} );
+
+				// lights
+
+				var ambientLight = new THREE.AmbientLight( 0xcccccc );
+				scene.add( ambientLight );
+
+				var pointLight = new THREE.PointLight( 0xff4400, 5, 30 );
+				pointLight.position.set( 5, 0, 0 );
+				scene.add( pointLight );
+
+				// renderer
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				// stats
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				// events
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			//
+
+			function onWindowResize() {
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+
+				var timer = Date.now() * 0.0005;
+
+				camera.position.x = Math.cos( timer ) * 10;
+				camera.position.y = 4;
+				camera.position.z = Math.sin( timer ) * 10;
+
+				mixer.update( clock.getDelta() );
+
+				camera.lookAt( scene.position );
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+	</body>
+</html>

+ 12 - 8
src/Three.Legacy.js

@@ -44,6 +44,7 @@ import { FileLoader } from './loaders/FileLoader.js';
 import { AudioLoader } from './loaders/AudioLoader.js';
 import { CubeTextureLoader } from './loaders/CubeTextureLoader.js';
 import { DataTextureLoader } from './loaders/DataTextureLoader.js';
+import { JSONLoader } from './loaders/JSONLoader.js';
 import { ObjectLoader } from './loaders/ObjectLoader.js';
 import { TextureLoader } from './loaders/TextureLoader.js';
 import { Material } from './materials/Material.js';
@@ -436,6 +437,17 @@ export function BinaryTextureLoader( manager ) {
 
 }
 
+Object.assign( JSONLoader.prototype, {
+
+	setTexturePath: function ( value ) {
+
+		console.warn( 'THREE.JSONLoader: .setTexturePath() has been renamed to .setResourcePath().' );
+		return this.setResourcePath( value );
+
+	}
+
+} );
+
 Object.assign( ObjectLoader.prototype, {
 
 	setTexturePath: function ( value ) {
@@ -1873,14 +1885,6 @@ export function Projector() {
 
 //
 
-export function JSONLoader() {
-
-	console.error( 'THREE.JSONLoader has been removed.' );
-
-}
-
-//
-
 export function CanvasRenderer() {
 
 	console.error( 'THREE.CanvasRenderer has been removed' );

+ 1 - 0
src/Three.js

@@ -40,6 +40,7 @@ export { ObjectLoader } from './loaders/ObjectLoader.js';
 export { MaterialLoader } from './loaders/MaterialLoader.js';
 export { BufferGeometryLoader } from './loaders/BufferGeometryLoader.js';
 export { DefaultLoadingManager, LoadingManager } from './loaders/LoadingManager.js';
+export { JSONLoader } from './loaders/JSONLoader.js';
 export { ImageLoader } from './loaders/ImageLoader.js';
 export { ImageBitmapLoader } from './loaders/ImageBitmapLoader.js';
 export { FontLoader } from './loaders/FontLoader.js';

+ 1 - 0
test/three.source.unit.js

@@ -161,6 +161,7 @@ import './unit/src/loaders/DataTextureLoader.tests';
 import './unit/src/loaders/FileLoader.tests';
 import './unit/src/loaders/FontLoader.tests';
 import './unit/src/loaders/ImageLoader.tests';
+import './unit/src/loaders/JSONLoader.tests';
 import './unit/src/loaders/Loader.tests';
 import './unit/src/loaders/LoaderUtils.tests';
 import './unit/src/loaders/LoadingManager.tests';

+ 131 - 1
test/unit/src/core/BufferGeometry.tests.js

@@ -5,10 +5,13 @@
 /* global QUnit */
 
 import { BufferGeometry } from '../../../../src/core/BufferGeometry';
+import { JSONLoader } from '../../../../src/loaders/JSONLoader';
+import { DirectGeometry } from '../../../../src/core/DirectGeometry';
 import {
 	BufferAttribute,
 	Uint16BufferAttribute,
-	Uint32BufferAttribute
+	Uint32BufferAttribute,
+	Float32BufferAttribute
 } from '../../../../src/core/BufferAttribute';
 import { Vector3 } from '../../../../src/math/Vector3';
 import { Matrix4 } from '../../../../src/math/Matrix4';
@@ -542,6 +545,133 @@ export default QUnit.module( 'Core', () => {
 
 		} );
 
+		QUnit.test( "fromGeometry/fromDirectGeometry", ( assert ) => {
+
+			if ( typeof XMLHttpRequest === 'undefined' ) {
+
+				assert.expect( 0 );
+				return;
+
+			}
+
+			assert.timeout( 1000 );
+
+			var a = new BufferGeometry();
+			// BoxGeometry is a bit too simple but works fine in a pinch
+			// var b = new BoxGeometry( 1, 1, 1 );
+			// b.mergeVertices();
+			// b.computeVertexNormals();
+			// b.computeBoundingBox();
+			// b.computeBoundingSphere();
+			var asyncDone = assert.async(); // tell QUnit we're done with asserts
+
+			var loader = new JSONLoader();
+			loader.load( "../../examples/models/skinned/simple/simple.js", function ( modelGeometry ) {
+
+				a.fromGeometry( modelGeometry );
+
+				var attr;
+				var geometry = new DirectGeometry().fromGeometry( modelGeometry );
+
+				var positions = new Float32Array( geometry.vertices.length * 3 );
+				attr = new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices );
+				assert.ok( bufferAttributeEquals( a.attributes.position, attr ), "Vertices are identical" );
+
+				if ( geometry.normals.length > 0 ) {
+
+					var normals = new Float32Array( geometry.normals.length * 3 );
+					attr = new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals );
+					assert.ok( bufferAttributeEquals( a.attributes.normal, attr ), "Normals are identical" );
+
+				}
+
+				if ( geometry.colors.length > 0 ) {
+
+					var colors = new Float32Array( geometry.colors.length * 3 );
+					attr = new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors );
+					assert.ok( bufferAttributeEquals( a.attributes.color, attr ), "Colors are identical" );
+
+				}
+
+				if ( geometry.uvs.length > 0 ) {
+
+					var uvs = new Float32Array( geometry.uvs.length * 2 );
+					attr = new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs );
+					assert.ok( bufferAttributeEquals( a.attributes.uv, attr ), "UVs are identical" );
+
+				}
+
+				if ( geometry.uvs2.length > 0 ) {
+
+					var uvs2 = new Float32Array( geometry.uvs2.length * 2 );
+					attr = new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 );
+					assert.ok( bufferAttributeEquals( a.attributes.uv2, attr ), "UV2s are identical" );
+
+				}
+
+				// groups
+				assert.deepEqual( a.groups, geometry.groups, "Groups are identical" );
+
+				// morphs
+				if ( geometry.morphTargets !== undefined ) {
+
+					for ( var name in geometry.morphTargets ) {
+
+						var morphTargets = geometry.morphTargets[ name ];
+
+						for ( var i = 0, l = morphTargets.length; i < l; i ++ ) {
+
+							var morphTarget = morphTargets[ i ];
+
+							attr = new Float32BufferAttribute( morphTarget.length * 3, 3 );
+							attr.copyVector3sArray( morphTarget );
+
+							assert.ok(
+								bufferAttributeEquals( a.morphAttributes[ name ][ i ], attr ),
+								"MorphTargets #" + i + " are identical"
+							);
+
+						}
+
+					}
+
+				}
+
+				// skinning
+				if ( geometry.skinIndices.length > 0 ) {
+
+					attr = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 );
+					attr.copyVector4sArray( geometry.skinIndices );
+					assert.ok( bufferAttributeEquals( a.attributes.skinIndex, attr ), "SkinIndices are identical" );
+
+				}
+
+				if ( geometry.skinWeights.length > 0 ) {
+
+					attr = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 );
+					attr.copyVector4sArray( geometry.skinWeights );
+					assert.ok( bufferAttributeEquals( a.attributes.skinWeight, attr ), "SkinWeights are identical" );
+
+				}
+
+				if ( geometry.boundingSphere !== null ) {
+
+					assert.ok( a.boundingSphere.equals( geometry.boundingSphere ), "BoundingSphere is identical" );
+
+				}
+
+				if ( geometry.boundingBox !== null ) {
+
+					assert.ok( a.boundingBox.equals( geometry.boundingBox ), "BoundingBox is identical" );
+
+				}
+
+				asyncDone();
+
+			} );
+
+		} );
+
 		QUnit.test( "computeBoundingBox", ( assert ) => {
 
 			var bb = getBBForVertices( [ - 1, - 2, - 3, 13, - 2, - 3.5, - 1, - 20, 0, - 4, 5, 6 ] );

+ 215 - 0
test/unit/src/core/DirectGeometry.tests.js

@@ -5,6 +5,8 @@
 /* global QUnit */
 
 import { DirectGeometry } from '../../../../src/core/DirectGeometry';
+import { JSONLoader } from '../../../../src/loaders/JSONLoader';
+import { Vector2 } from '../../../../src/math/Vector2';
 import { Face3 } from '../../../../src/core/Face3';
 import { Geometry } from '../../../../src/core/Geometry';
 
@@ -44,6 +46,219 @@ export default QUnit.module( 'Core', () => {
 
 		} );
 
+		QUnit.test( "fromGeometry", ( assert ) => {
+
+			if ( typeof XMLHttpRequest === 'undefined' ) {
+
+				assert.expect( 0 );
+				return;
+
+			}
+
+			assert.timeout( 1000 );
+
+			var a = new DirectGeometry();
+
+			var asyncDone = assert.async(); // tell QUnit when we're done with async stuff
+
+			var loader = new JSONLoader();
+			loader.load( "../../examples/models/skinned/simple/simple.js", function ( geometry ) {
+
+				a.fromGeometry( geometry );
+
+				var tmp = new DirectGeometry();
+				tmp.computeGroups( geometry );
+				assert.deepEqual( a.groups, tmp.groups, "Check groups" );
+
+				var morphTargets = geometry.morphTargets;
+				var morphTargetsLength = morphTargets.length;
+
+				var morphTargetsPosition;
+
+				if ( morphTargetsLength > 0 ) {
+
+					morphTargetsPosition = [];
+
+					for ( var i = 0; i < morphTargetsLength; i ++ ) {
+
+						morphTargetsPosition[ i ] = [];
+
+					}
+
+					morphTargets.position = morphTargetsPosition;
+
+				}
+
+				var morphNormals = geometry.morphNormals;
+				var morphNormalsLength = morphNormals.length;
+
+				var morphTargetsNormal;
+
+				if ( morphNormalsLength > 0 ) {
+
+					morphTargetsNormal = [];
+
+					for ( var i = 0; i < morphNormalsLength; i ++ ) {
+
+						morphTargetsNormal[ i ] = [];
+
+					}
+
+					morphTargets.normal = morphTargetsNormal;
+
+				}
+
+				var vertices = [];
+				var normals = [];
+				var colors = [];
+				var uvs = [];
+				var uvs2 = [];
+				var skinIndices = [];
+				var skinWeights = [];
+
+				var hasFaceVertexUv = geometry.faceVertexUvs[ 0 ] && geometry.faceVertexUvs[ 0 ].length > 0;
+				var hasFaceVertexUv2 = geometry.faceVertexUvs[ 1 ] && geometry.faceVertexUvs[ 1 ].length > 0;
+
+				var hasSkinIndices = geometry.skinIndices.length === geometry.vertices.length;
+				var hasSkinWeights = geometry.skinWeights.length === geometry.vertices.length;
+
+				for ( var i = 0; i < geometry.faces.length; i ++ ) {
+
+					var face = geometry.faces[ i ];
+
+					vertices.push(
+						geometry.vertices[ face.a ],
+						geometry.vertices[ face.b ],
+						geometry.vertices[ face.c ]
+					);
+
+					var vertexNormals = face.vertexNormals;
+
+					if ( vertexNormals.length === 3 ) {
+
+						normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] );
+
+					} else {
+
+						normals.push( face.normal, face.normal, face.normal );
+
+					}
+
+					var vertexColors = face.vertexColors;
+
+					if ( vertexColors.length === 3 ) {
+
+						colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] );
+
+					} else {
+
+						colors.push( face.color, face.color, face.color );
+
+					}
+
+					if ( hasFaceVertexUv === true ) {
+
+						var vertexUvs = geometry.faceVertexUvs[ 0 ][ i ];
+
+						if ( vertexUvs !== undefined ) {
+
+							uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
+
+						} else {
+
+							uvs.push( new Vector2(), new Vector2(), new Vector2() );
+
+						}
+
+					}
+
+					if ( hasFaceVertexUv2 === true ) {
+
+						var vertexUvs = geometry.faceVertexUvs[ 1 ][ i ];
+
+						if ( vertexUvs !== undefined ) {
+
+							uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
+
+						} else {
+
+							uvs2.push( new Vector2(), new Vector2(), new Vector2() );
+
+						}
+
+					}
+
+					// morphs
+
+					for ( var j = 0; j < morphTargetsLength; j ++ ) {
+
+						var morphTarget = morphTargets[ j ].vertices;
+
+						morphTargetsPosition[ j ].push(
+							morphTarget[ face.a ],
+							morphTarget[ face.b ],
+							morphTarget[ face.c ]
+						);
+
+					}
+
+					for ( var j = 0; j < morphNormalsLength; j ++ ) {
+
+						var morphNormal = morphNormals[ j ].vertexNormals[ i ];
+
+						morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c );
+
+					}
+
+					// skins
+
+					if ( hasSkinIndices ) {
+
+						skinIndices.push(
+							geometry.skinIndices[ face.a ],
+							geometry.skinIndices[ face.b ],
+							geometry.skinIndices[ face.c ]
+						);
+
+					}
+
+					if ( hasSkinWeights ) {
+
+						skinWeights.push(
+							geometry.skinWeights[ face.a ],
+							geometry.skinWeights[ face.b ],
+							geometry.skinWeights[ face.c ]
+						);
+
+					}
+
+				}
+
+				assert.deepEqual( a.vertices, vertices, "Vertices are identical" );
+				assert.deepEqual( a.normals, normals, "Normals are identical" );
+				assert.deepEqual( a.colors, colors, "Colors are identical" );
+				assert.deepEqual( a.uvs, uvs, "UV coordinates are identical" );
+				assert.deepEqual( a.uvs2, uvs2, "UV(2) coordinates are identical" );
+				assert.deepEqual( a.skinIndices, skinIndices, "SkinIndices are identical" );
+				assert.deepEqual( a.skinWeights, skinWeights, "SkinWeights are identical" );
+				assert.deepEqual( a.morphTargetsPosition, morphTargetsPosition, "MorphTargets (Position) are identical" );
+				assert.deepEqual( a.morphTargetsNormal, morphTargetsNormal, "MorphTargets (Normals) are identical" );
+
+				asyncDone();
+
+			}, onProgress, onError );
+
+			function onProgress() {}
+
+			function onError( error ) {
+
+				console.error( error );
+				asyncDone();
+
+			}
+
+		} );
+
 	} );
 
 } );

+ 40 - 0
test/unit/src/loaders/JSONLoader.tests.js

@@ -0,0 +1,40 @@
+/**
+ * @author TristanVALCKE / https://github.com/Itee
+ */
+/* global QUnit */
+
+import { JSONLoader } from '../../../../src/loaders/JSONLoader';
+
+export default QUnit.module( 'Loaders', () => {
+
+	QUnit.module( 'JSONLoader', () => {
+
+		// INSTANCING
+		QUnit.todo( "Instancing", ( assert ) => {
+
+			assert.ok( false, "everything's gonna be alright" );
+
+		} );
+
+		// PUBLIC STUFF
+		QUnit.todo( "load", ( assert ) => {
+
+			assert.ok( false, "everything's gonna be alright" );
+
+		} );
+
+		QUnit.todo( "setTexturePath", ( assert ) => {
+
+			assert.ok( false, "everything's gonna be alright" );
+
+		} );
+
+		QUnit.todo( "parse", ( assert ) => {
+
+			assert.ok( false, "everything's gonna be alright" );
+
+		} );
+
+	} );
+
+} );

+ 1 - 1
utils/exporters/blender/README.md

@@ -1,3 +1,3 @@
 # Three.js Blender Export
 
-> **NOTICE:** The Blender exporter for the Three.js JSON format has been removed, to focus on better support for other workflows. For recommended alternatives, see [Loading 3D Models](https://threejs.org/docs/#manual/introduction/Loading-3D-models). The Three.js JSON format is still fully supported for use with [Object3D.toJSON](https://threejs.org/docs/#api/core/Object3D.toJSON), the [Editor](https://threejs.org/editor/), [THREE.ObjectLoader](https://threejs.org/docs/#api/loaders/ObjectLoader) and [converters](https://github.com/mrdoob/three.js/tree/dev/utils/converters).
+> **NOTICE:** The Blender exporter for the Three.js JSON format has been removed, to focus on better support for other workflows. For recommended alternatives, see [Loading 3D Models](https://threejs.org/docs/#manual/introduction/Loading-3D-models). The Three.js JSON format is still fully supported for use with [Object3D.toJSON](https://threejs.org/docs/#api/core/Object3D.toJSON), the [Editor](https://threejs.org/editor/), [THREE.ObjectLoader](https://threejs.org/docs/#api/loaders/ObjectLoader), [THREE.JSONLoader](https://threejs.org/docs/#api/loaders/JSONLoader), and [converters](https://github.com/mrdoob/three.js/tree/dev/utils/converters).