Browse Source

Merge branch 'dev' into types-gltfloader-plugins

Yutaka "FMS_Cat" Obuchi 4 years ago
parent
commit
ec0f7d6c83

+ 19 - 4
editor/js/Loader.js

@@ -187,12 +187,27 @@ function Loader( editor ) {
 					loader.setDecoderPath( '../examples/js/libs/draco/' );
 					loader.decodeDracoFile( contents, function ( geometry ) {
 
-						var material = new THREE.MeshStandardMaterial();
+						var object;
 
-						var mesh = new THREE.Mesh( geometry, material );
-						mesh.name = filename;
+						if ( geometry.index !== null ) {
 
-						editor.execute( new AddObjectCommand( editor, mesh ) );
+							var material = new THREE.MeshStandardMaterial();
+
+							object = new THREE.Mesh( geometry, material );
+							object.name = filename;
+
+						} else {
+
+							var material = new THREE.PointsMaterial( { size: 0.01 } );
+
+							if ( geometry.getAttribute( 'color' ) !== undefined ) material.vertexColors = true;
+
+							object = new THREE.Points( geometry, material );
+							object.name = filename;
+
+						}
+
+						editor.execute( new AddObjectCommand( editor, object ) );
 
 					} );
 

+ 54 - 20
editor/js/Sidebar.Material.js

@@ -60,26 +60,7 @@ function SidebarMaterial( editor ) {
 	// type
 
 	var materialClassRow = new UIRow();
-	var materialClass = new UISelect().setOptions( {
-
-		'LineBasicMaterial': 'LineBasicMaterial',
-		'LineDashedMaterial': 'LineDashedMaterial',
-		'MeshBasicMaterial': 'MeshBasicMaterial',
-		'MeshDepthMaterial': 'MeshDepthMaterial',
-		'MeshNormalMaterial': 'MeshNormalMaterial',
-		'MeshLambertMaterial': 'MeshLambertMaterial',
-		'MeshMatcapMaterial': 'MeshMatcapMaterial',
-		'MeshPhongMaterial': 'MeshPhongMaterial',
-		'MeshToonMaterial': 'MeshToonMaterial',
-		'MeshStandardMaterial': 'MeshStandardMaterial',
-		'MeshPhysicalMaterial': 'MeshPhysicalMaterial',
-		'RawShaderMaterial': 'RawShaderMaterial',
-		'ShaderMaterial': 'ShaderMaterial',
-		'ShadowMaterial': 'ShadowMaterial',
-		'SpriteMaterial': 'SpriteMaterial',
-		'PointsMaterial': 'PointsMaterial'
-
-	} ).setWidth( '150px' ).setFontSize( '12px' ).onChange( update );
+	var materialClass = new UISelect().setWidth( '150px' ).setFontSize( '12px' ).onChange( update );
 
 	materialClassRow.add( new UIText( strings.getKey( 'sidebar/material/type' ) ).setWidth( '90px' ) );
 	materialClassRow.add( materialClass );
@@ -1365,8 +1346,27 @@ function SidebarMaterial( editor ) {
 
 		}
 
+		if ( currentObject.isMesh ) {
+
+			materialClass.setOptions( meshMaterialOptions );
+
+		} else if ( currentObject.isSprite ) {
+
+			materialClass.setOptions( spriteMaterialOptions );
+
+		} else if ( currentObject.isPoints ) {
+
+			materialClass.setOptions( pointsMaterialOptions );
+
+		} else if ( currentObject.isLine ) {
+
+			lineMaterialOptions.setOptions( lineMaterialOptions );
+
+		}
+
 		materialClass.setValue( material.type );
 
+
 		if ( material.color !== undefined ) {
 
 			materialColor.setHexValue( material.color.getHexString() );
@@ -1759,6 +1759,40 @@ function SidebarMaterial( editor ) {
 		'attribute vec3 position;\n\n',
 	].join( '\n' );
 
+	var meshMaterialOptions = {
+		'MeshBasicMaterial': 'MeshBasicMaterial',
+		'MeshDepthMaterial': 'MeshDepthMaterial',
+		'MeshNormalMaterial': 'MeshNormalMaterial',
+		'MeshLambertMaterial': 'MeshLambertMaterial',
+		'MeshMatcapMaterial': 'MeshMatcapMaterial',
+		'MeshPhongMaterial': 'MeshPhongMaterial',
+		'MeshToonMaterial': 'MeshToonMaterial',
+		'MeshStandardMaterial': 'MeshStandardMaterial',
+		'MeshPhysicalMaterial': 'MeshPhysicalMaterial',
+		'RawShaderMaterial': 'RawShaderMaterial',
+		'ShaderMaterial': 'ShaderMaterial',
+		'ShadowMaterial': 'ShadowMaterial'
+	};
+
+	var lineMaterialOptions = {
+		'LineBasicMaterial': 'LineBasicMaterial',
+		'LineDashedMaterial': 'LineDashedMaterial',
+		'RawShaderMaterial': 'RawShaderMaterial',
+		'ShaderMaterial': 'ShaderMaterial'
+	};
+
+	var spriteMaterialOptions = {
+		'SpriteMaterial': 'SpriteMaterial',
+		'RawShaderMaterial': 'RawShaderMaterial',
+		'ShaderMaterial': 'ShaderMaterial'
+	};
+
+	var pointsMaterialOptions = {
+		'PointsMaterial': 'PointsMaterial',
+		'RawShaderMaterial': 'RawShaderMaterial',
+		'ShaderMaterial': 'ShaderMaterial'
+	};
+
 	return container;
 
 }

+ 1 - 1
examples/files.json

@@ -92,7 +92,7 @@
 		"webgl_loader_gcode",
 		"webgl_loader_gltf",
 		"webgl_loader_gltf_extensions",
-		"webgl_loader_gltf_variant",
+		"webgl_loader_gltf_variants",
 		"webgl_loader_imagebitmap",
 		"webgl_loader_kmz",
 		"webgl_loader_ldraw",

+ 84 - 32
examples/js/exporters/DRACOExporter.js

@@ -20,8 +20,13 @@ THREE.DRACOExporter.prototype = {
 
 	constructor: THREE.DRACOExporter,
 
-	parse: function ( geometry, options ) {
+	parse: function ( object, options ) {
 
+		if ( object.isBufferGeometry === true || object.isGeometry === true ) {
+
+			throw new Error( 'DRACOExporter: The first parameter of parse() is now an instance of Mesh or Points.' );
+
+		}
 
 		if ( DracoEncoderModule === undefined ) {
 
@@ -45,15 +50,17 @@ THREE.DRACOExporter.prototype = {
 
 		}
 
+		var geometry = object.geometry;
+
 		var dracoEncoder = DracoEncoderModule();
 		var encoder = new dracoEncoder.Encoder();
-		var builder = new dracoEncoder.MeshBuilder();
-		var mesh = new dracoEncoder.Mesh();
+		var builder;
+		var dracoObject;
 
 		if ( geometry.isGeometry === true ) {
 
 			var bufferGeometry = new THREE.BufferGeometry();
-			bufferGeometry.fromGeometry( geometry );
+			bufferGeometry.setFromObject( object );
 			geometry = bufferGeometry;
 
 		}
@@ -64,63 +71,94 @@ THREE.DRACOExporter.prototype = {
 
 		}
 
-		var vertices = geometry.getAttribute( 'position' );
-		builder.AddFloatAttributeToMesh( mesh, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
+		if ( object.isMesh === true ) {
 
-		var faces = geometry.getIndex();
+			builder = new dracoEncoder.MeshBuilder();
+			dracoObject = new dracoEncoder.Mesh();
 
-		if ( faces !== null ) {
+			var vertices = geometry.getAttribute( 'position' );
+			builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
 
-			builder.AddFacesToMesh( mesh, faces.count / 3, faces.array );
+			var faces = geometry.getIndex();
 
-		} else {
+			if ( faces !== null ) {
 
-			var faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count );
+				builder.AddFacesToMesh( dracoObject, faces.count / 3, faces.array );
 
-			for ( var i = 0; i < faces.length; i ++ ) {
+			} else {
+
+				var faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count );
+
+				for ( var i = 0; i < faces.length; i ++ ) {
+
+					faces[ i ] = i;
+
+				}
 
-				faces[ i ] = i;
+				builder.AddFacesToMesh( dracoObject, vertices.count, faces );
 
 			}
 
-			builder.AddFacesToMesh( mesh, vertices.count, faces );
+			if ( options.exportNormals === true ) {
 
-		}
+				var normals = geometry.getAttribute( 'normal' );
+
+				if ( normals !== undefined ) {
 
-		if ( options.exportNormals === true ) {
+					builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array );
 
-			var normals = geometry.getAttribute( 'normal' );
+				}
+
+			}
 
-			if ( normals !== undefined ) {
+			if ( options.exportUvs === true ) {
 
-				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array );
+				var uvs = geometry.getAttribute( 'uv' );
+
+				if ( uvs !== undefined ) {
+
+					builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array );
+
+				}
 
 			}
 
-		}
+			if ( options.exportColor === true ) {
 
-		if ( options.exportUvs === true ) {
+				var colors = geometry.getAttribute( 'color' );
 
-			var uvs = geometry.getAttribute( 'uv' );
+				if ( colors !== undefined ) {
 
-			if ( uvs !== undefined ) {
+					builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
 
-				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array );
+				}
 
 			}
 
-		}
+		} else if ( object.isPoints === true ) {
+
+			builder = new dracoEncoder.PointCloudBuilder();
+			dracoObject = new dracoEncoder.PointCloud();
+
+			var vertices = geometry.getAttribute( 'position' );
+			builder.AddFloatAttribute( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
 
-		if ( options.exportColor === true ) {
+			if ( options.exportColor === true ) {
 
-			var colors = geometry.getAttribute( 'color' );
+				var colors = geometry.getAttribute( 'color' );
 
-			if ( colors !== undefined ) {
+				if ( colors !== undefined ) {
 
-				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
+					builder.AddFloatAttribute( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
+
+				}
 
 			}
 
+		} else {
+
+			throw new Error( 'DRACOExporter: Unsupported object type.' );
+
 		}
 
 		//Compress using draco encoder
@@ -129,7 +167,10 @@ THREE.DRACOExporter.prototype = {
 
 		//Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression).
 
-		encoder.SetSpeedOptions( options.encodeSpeed || 5, options.decodeSpeed || 5 );
+		var encodeSpeed = ( options.encodeSpeed !== undefined ) ? options.encodeSpeed : 5;
+		var decodeSpeed = ( options.decodeSpeed !== undefined ) ? options.decodeSpeed : 5;
+
+		encoder.SetSpeedOptions( encodeSpeed, decodeSpeed );
 
 		// Sets the desired encoding method for a given geometry.
 
@@ -155,8 +196,19 @@ THREE.DRACOExporter.prototype = {
 
 		}
 
-		var length = encoder.EncodeMeshToDracoBuffer( mesh, encodedData );
-		dracoEncoder.destroy( mesh );
+		var length;
+
+		if ( object.isMesh === true ) {
+
+			length = encoder.EncodeMeshToDracoBuffer( dracoObject, encodedData );
+
+		} else {
+
+			length = encoder.EncodePointCloudToDracoBuffer( dracoObject, true, encodedData );
+
+		}
+
+		dracoEncoder.destroy( dracoObject );
 
 		if ( length === 0 ) {
 

+ 109 - 79
examples/js/loaders/OBJLoader.js

@@ -711,151 +711,181 @@ THREE.OBJLoader = ( function () {
 			var container = new THREE.Group();
 			container.materialLibraries = [].concat( state.materialLibraries );
 
-			for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
+			var hasPrimitives = ! ( state.objects.length === 1 && state.objects[ 0 ].geometry.vertices.length === 0 );
 
-				var object = state.objects[ i ];
-				var geometry = object.geometry;
-				var materials = object.materials;
-				var isLine = ( geometry.type === 'Line' );
-				var isPoints = ( geometry.type === 'Points' );
-				var hasVertexColors = false;
+			if ( hasPrimitives === true ) {
 
-				// Skip o/g line declarations that did not follow with any faces
-				if ( geometry.vertices.length === 0 ) continue;
+				for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
 
-				var buffergeometry = new THREE.BufferGeometry();
+					var object = state.objects[ i ];
+					var geometry = object.geometry;
+					var materials = object.materials;
+					var isLine = ( geometry.type === 'Line' );
+					var isPoints = ( geometry.type === 'Points' );
+					var hasVertexColors = false;
 
-				buffergeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( geometry.vertices, 3 ) );
+					// Skip o/g line declarations that did not follow with any faces
+					if ( geometry.vertices.length === 0 ) continue;
 
-				if ( geometry.normals.length > 0 ) {
+					var buffergeometry = new THREE.BufferGeometry();
 
-					buffergeometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( geometry.normals, 3 ) );
+					buffergeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( geometry.vertices, 3 ) );
 
-				}
+					if ( geometry.normals.length > 0 ) {
 
-				if ( geometry.colors.length > 0 ) {
+						buffergeometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( geometry.normals, 3 ) );
 
-					hasVertexColors = true;
-					buffergeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( geometry.colors, 3 ) );
+					}
 
-				}
+					if ( geometry.colors.length > 0 ) {
 
-				if ( geometry.hasUVIndices === true ) {
+						hasVertexColors = true;
+						buffergeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( geometry.colors, 3 ) );
 
-					buffergeometry.setAttribute( 'uv', new THREE.Float32BufferAttribute( geometry.uvs, 2 ) );
+					}
 
-				}
+					if ( geometry.hasUVIndices === true ) {
 
-				// Create materials
+						buffergeometry.setAttribute( 'uv', new THREE.Float32BufferAttribute( geometry.uvs, 2 ) );
 
-				var createdMaterials = [];
+					}
 
-				for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
+					// Create materials
 
-					var sourceMaterial = materials[ mi ];
-					var materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;
-					var material = state.materials[ materialHash ];
+					var createdMaterials = [];
 
-					if ( this.materials !== null ) {
+					for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
 
-						material = this.materials.create( sourceMaterial.name );
+						var sourceMaterial = materials[ mi ];
+						var materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;
+						var material = state.materials[ materialHash ];
+
+						if ( this.materials !== null ) {
+
+							material = this.materials.create( sourceMaterial.name );
 
-						// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
-						if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
+							// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
+							if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
 
-							var materialLine = new THREE.LineBasicMaterial();
-							THREE.Material.prototype.copy.call( materialLine, material );
-							materialLine.color.copy( material.color );
-							material = materialLine;
+								var materialLine = new THREE.LineBasicMaterial();
+								THREE.Material.prototype.copy.call( materialLine, material );
+								materialLine.color.copy( material.color );
+								material = materialLine;
 
-						} else if ( isPoints && material && ! ( material instanceof THREE.PointsMaterial ) ) {
+							} else if ( isPoints && material && ! ( material instanceof THREE.PointsMaterial ) ) {
 
-							var materialPoints = new THREE.PointsMaterial( { size: 10, sizeAttenuation: false } );
-							THREE.Material.prototype.copy.call( materialPoints, material );
-							materialPoints.color.copy( material.color );
-							materialPoints.map = material.map;
-							material = materialPoints;
+								var materialPoints = new THREE.PointsMaterial( { size: 10, sizeAttenuation: false } );
+								THREE.Material.prototype.copy.call( materialPoints, material );
+								materialPoints.color.copy( material.color );
+								materialPoints.map = material.map;
+								material = materialPoints;
+
+							}
 
 						}
 
-					}
+						if ( material === undefined ) {
 
-					if ( material === undefined ) {
+							if ( isLine ) {
 
-						if ( isLine ) {
+								material = new THREE.LineBasicMaterial();
 
-							material = new THREE.LineBasicMaterial();
+							} else if ( isPoints ) {
 
-						} else if ( isPoints ) {
+								material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
 
-							material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
+							} else {
 
-						} else {
+								material = new THREE.MeshPhongMaterial();
 
-							material = new THREE.MeshPhongMaterial();
+							}
 
-						}
+							material.name = sourceMaterial.name;
+							material.flatShading = sourceMaterial.smooth ? false : true;
+							material.vertexColors = hasVertexColors;
 
-						material.name = sourceMaterial.name;
-						material.flatShading = sourceMaterial.smooth ? false : true;
-						material.vertexColors = hasVertexColors;
+							state.materials[ materialHash ] = material;
 
-						state.materials[ materialHash ] = material;
+						}
+
+						createdMaterials.push( material );
 
 					}
 
-					createdMaterials.push( material );
+					// Create mesh
 
-				}
+					var mesh;
 
-				// Create mesh
+					if ( createdMaterials.length > 1 ) {
 
-				var mesh;
+						for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
 
-				if ( createdMaterials.length > 1 ) {
+							var sourceMaterial = materials[ mi ];
+							buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
 
-					for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
+						}
 
-						var sourceMaterial = materials[ mi ];
-						buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
+						if ( isLine ) {
 
-					}
+							mesh = new THREE.LineSegments( buffergeometry, createdMaterials );
 
-					if ( isLine ) {
+						} else if ( isPoints ) {
 
-						mesh = new THREE.LineSegments( buffergeometry, createdMaterials );
+							mesh = new THREE.Points( buffergeometry, createdMaterials );
 
-					} else if ( isPoints ) {
+						} else {
 
-						mesh = new THREE.Points( buffergeometry, createdMaterials );
+							mesh = new THREE.Mesh( buffergeometry, createdMaterials );
+
+						}
 
 					} else {
 
-						mesh = new THREE.Mesh( buffergeometry, createdMaterials );
+						if ( isLine ) {
 
-					}
+							mesh = new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] );
 
-				} else {
+						} else if ( isPoints ) {
 
-					if ( isLine ) {
+							mesh = new THREE.Points( buffergeometry, createdMaterials[ 0 ] );
 
-						mesh = new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] );
+						} else {
 
-					} else if ( isPoints ) {
+							mesh = new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] );
 
-						mesh = new THREE.Points( buffergeometry, createdMaterials[ 0 ] );
+						}
 
-					} else {
+					}
 
-						mesh = new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] );
+					mesh.name = object.name;
 
-					}
+					container.add( mesh );
 
 				}
 
-				mesh.name = object.name;
+			} else {
+
+				// if there is only the default parser state object with no geometry data, interpret data as point cloud
+
+				if ( state.vertices.length > 0 ) {
+
+					var material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
+
+					var buffergeometry = new THREE.BufferGeometry();
+
+					buffergeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( state.vertices, 3 ) );
 
-				container.add( mesh );
+					if ( state.colors.length > 0 ) {
+
+						buffergeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( state.colors, 3 ) );
+						material.vertexColors = true;
+
+					}
+
+					var points = new THREE.Points( buffergeometry, material );
+					container.add( points );
+
+				}
 
 			}
 

+ 3 - 3
examples/jsm/exporters/DRACOExporter.d.ts

@@ -1,6 +1,6 @@
 import {
-	BufferGeometry,
-	Geometry
+	Mesh,
+	Points
 } from '../../../src/Three';
 
 export interface DRACOExporterOptions {
@@ -17,6 +17,6 @@ export class DRACOExporter {
 
 	constructor();
 
-	parse( geometry: BufferGeometry | Geometry, options: DRACOExporterOptions ): Int8Array;
+	parse( object: Mesh | Points, options: DRACOExporterOptions ): Int8Array;
 
 }

+ 84 - 32
examples/jsm/exporters/DRACOExporter.js

@@ -24,8 +24,13 @@ DRACOExporter.prototype = {
 
 	constructor: DRACOExporter,
 
-	parse: function ( geometry, options ) {
+	parse: function ( object, options ) {
 
+		if ( object.isBufferGeometry === true || object.isGeometry === true ) {
+
+			throw new Error( 'DRACOExporter: The first parameter of parse() is now an instance of Mesh or Points.' );
+
+		}
 
 		if ( DracoEncoderModule === undefined ) {
 
@@ -49,15 +54,17 @@ DRACOExporter.prototype = {
 
 		}
 
+		var geometry = object.geometry;
+
 		var dracoEncoder = DracoEncoderModule();
 		var encoder = new dracoEncoder.Encoder();
-		var builder = new dracoEncoder.MeshBuilder();
-		var mesh = new dracoEncoder.Mesh();
+		var builder;
+		var dracoObject;
 
 		if ( geometry.isGeometry === true ) {
 
 			var bufferGeometry = new BufferGeometry();
-			bufferGeometry.fromGeometry( geometry );
+			bufferGeometry.setFromObject( object );
 			geometry = bufferGeometry;
 
 		}
@@ -68,63 +75,94 @@ DRACOExporter.prototype = {
 
 		}
 
-		var vertices = geometry.getAttribute( 'position' );
-		builder.AddFloatAttributeToMesh( mesh, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
+		if ( object.isMesh === true ) {
 
-		var faces = geometry.getIndex();
+			builder = new dracoEncoder.MeshBuilder();
+			dracoObject = new dracoEncoder.Mesh();
 
-		if ( faces !== null ) {
+			var vertices = geometry.getAttribute( 'position' );
+			builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
 
-			builder.AddFacesToMesh( mesh, faces.count / 3, faces.array );
+			var faces = geometry.getIndex();
 
-		} else {
+			if ( faces !== null ) {
 
-			var faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count );
+				builder.AddFacesToMesh( dracoObject, faces.count / 3, faces.array );
 
-			for ( var i = 0; i < faces.length; i ++ ) {
+			} else {
+
+				var faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count );
+
+				for ( var i = 0; i < faces.length; i ++ ) {
+
+					faces[ i ] = i;
+
+				}
 
-				faces[ i ] = i;
+				builder.AddFacesToMesh( dracoObject, vertices.count, faces );
 
 			}
 
-			builder.AddFacesToMesh( mesh, vertices.count, faces );
+			if ( options.exportNormals === true ) {
 
-		}
+				var normals = geometry.getAttribute( 'normal' );
+
+				if ( normals !== undefined ) {
 
-		if ( options.exportNormals === true ) {
+					builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array );
 
-			var normals = geometry.getAttribute( 'normal' );
+				}
+
+			}
 
-			if ( normals !== undefined ) {
+			if ( options.exportUvs === true ) {
 
-				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array );
+				var uvs = geometry.getAttribute( 'uv' );
+
+				if ( uvs !== undefined ) {
+
+					builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array );
+
+				}
 
 			}
 
-		}
+			if ( options.exportColor === true ) {
 
-		if ( options.exportUvs === true ) {
+				var colors = geometry.getAttribute( 'color' );
 
-			var uvs = geometry.getAttribute( 'uv' );
+				if ( colors !== undefined ) {
 
-			if ( uvs !== undefined ) {
+					builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
 
-				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array );
+				}
 
 			}
 
-		}
+		} else if ( object.isPoints === true ) {
+
+			builder = new dracoEncoder.PointCloudBuilder();
+			dracoObject = new dracoEncoder.PointCloud();
+
+			var vertices = geometry.getAttribute( 'position' );
+			builder.AddFloatAttribute( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
 
-		if ( options.exportColor === true ) {
+			if ( options.exportColor === true ) {
 
-			var colors = geometry.getAttribute( 'color' );
+				var colors = geometry.getAttribute( 'color' );
 
-			if ( colors !== undefined ) {
+				if ( colors !== undefined ) {
 
-				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
+					builder.AddFloatAttribute( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
+
+				}
 
 			}
 
+		} else {
+
+			throw new Error( 'DRACOExporter: Unsupported object type.' );
+
 		}
 
 		//Compress using draco encoder
@@ -133,7 +171,10 @@ DRACOExporter.prototype = {
 
 		//Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression).
 
-		encoder.SetSpeedOptions( options.encodeSpeed || 5, options.decodeSpeed || 5 );
+		var encodeSpeed = ( options.encodeSpeed !== undefined ) ? options.encodeSpeed : 5;
+		var decodeSpeed = ( options.decodeSpeed !== undefined ) ? options.decodeSpeed : 5;
+
+		encoder.SetSpeedOptions( encodeSpeed, decodeSpeed );
 
 		// Sets the desired encoding method for a given geometry.
 
@@ -159,8 +200,19 @@ DRACOExporter.prototype = {
 
 		}
 
-		var length = encoder.EncodeMeshToDracoBuffer( mesh, encodedData );
-		dracoEncoder.destroy( mesh );
+		var length;
+
+		if ( object.isMesh === true ) {
+
+			length = encoder.EncodeMeshToDracoBuffer( dracoObject, encodedData );
+
+		} else {
+
+			length = encoder.EncodePointCloudToDracoBuffer( dracoObject, true, encodedData );
+
+		}
+
+		dracoEncoder.destroy( dracoObject );
 
 		if ( length === 0 ) {
 

+ 7 - 4
examples/jsm/loaders/3DMLoader.js

@@ -830,12 +830,17 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 
 		//Handle objects
 
-		for ( var i = 0; i < doc.objects().count; i ++ ) {
+		var objs = doc.objects();
+		var cnt = objs.count;
 
-			var _object = doc.objects().get( i );
+		for ( var i = 0; i < cnt; i ++ ) {
+
+			var _object = objs.get( i );
 
 			var object = extractObjectData( _object, doc );
 
+			_object.delete();
+
 			if ( object !== undefined ) {
 
 				if ( object.attributes.materialIndex >= 0 ) {
@@ -849,8 +854,6 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 
 			}
 
-			_object.delete();
-
 		}
 
 		// Handle instance definitions

+ 4 - 0
examples/jsm/loaders/GLTFLoader.d.ts

@@ -13,6 +13,7 @@ import {
 
 import { DRACOLoader } from './DRACOLoader';
 import { DDSLoader } from './DDSLoader';
+import { KTX2Loader } from './KTX2Loader';
 
 export interface GLTF {
 	animations: AnimationClip[];
@@ -44,6 +45,9 @@ export class GLTFLoader extends Loader {
 	register( callback: ( parser: GLTFParser ) => GLTFLoaderPlugin ): GLTFLoader;
 	unregister( callback: ( parser: GLTFParser ) => GLTFLoaderPlugin ): GLTFLoader;
 
+  setKTX2Loader( ktx2Loader: KTX2Loader ): GLTFLoader;
+	setMeshoptDecoder( meshoptDecoder: /* MeshoptDecoder */ any ): GLTFLoader;
+
 	parse( data: ArrayBuffer | string, path: string, onLoad: ( gltf: GLTF ) => void, onError?: ( event: ErrorEvent ) => void ) : void;
 
 }

+ 109 - 79
examples/jsm/loaders/OBJLoader.js

@@ -727,151 +727,181 @@ var OBJLoader = ( function () {
 			var container = new Group();
 			container.materialLibraries = [].concat( state.materialLibraries );
 
-			for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
+			var hasPrimitives = ! ( state.objects.length === 1 && state.objects[ 0 ].geometry.vertices.length === 0 );
 
-				var object = state.objects[ i ];
-				var geometry = object.geometry;
-				var materials = object.materials;
-				var isLine = ( geometry.type === 'Line' );
-				var isPoints = ( geometry.type === 'Points' );
-				var hasVertexColors = false;
+			if ( hasPrimitives === true ) {
 
-				// Skip o/g line declarations that did not follow with any faces
-				if ( geometry.vertices.length === 0 ) continue;
+				for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
 
-				var buffergeometry = new BufferGeometry();
+					var object = state.objects[ i ];
+					var geometry = object.geometry;
+					var materials = object.materials;
+					var isLine = ( geometry.type === 'Line' );
+					var isPoints = ( geometry.type === 'Points' );
+					var hasVertexColors = false;
 
-				buffergeometry.setAttribute( 'position', new Float32BufferAttribute( geometry.vertices, 3 ) );
+					// Skip o/g line declarations that did not follow with any faces
+					if ( geometry.vertices.length === 0 ) continue;
 
-				if ( geometry.normals.length > 0 ) {
+					var buffergeometry = new BufferGeometry();
 
-					buffergeometry.setAttribute( 'normal', new Float32BufferAttribute( geometry.normals, 3 ) );
+					buffergeometry.setAttribute( 'position', new Float32BufferAttribute( geometry.vertices, 3 ) );
 
-				}
+					if ( geometry.normals.length > 0 ) {
 
-				if ( geometry.colors.length > 0 ) {
+						buffergeometry.setAttribute( 'normal', new Float32BufferAttribute( geometry.normals, 3 ) );
 
-					hasVertexColors = true;
-					buffergeometry.setAttribute( 'color', new Float32BufferAttribute( geometry.colors, 3 ) );
+					}
 
-				}
+					if ( geometry.colors.length > 0 ) {
 
-				if ( geometry.hasUVIndices === true ) {
+						hasVertexColors = true;
+						buffergeometry.setAttribute( 'color', new Float32BufferAttribute( geometry.colors, 3 ) );
 
-					buffergeometry.setAttribute( 'uv', new Float32BufferAttribute( geometry.uvs, 2 ) );
+					}
 
-				}
+					if ( geometry.hasUVIndices === true ) {
 
-				// Create materials
+						buffergeometry.setAttribute( 'uv', new Float32BufferAttribute( geometry.uvs, 2 ) );
 
-				var createdMaterials = [];
+					}
 
-				for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
+					// Create materials
 
-					var sourceMaterial = materials[ mi ];
-					var materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;
-					var material = state.materials[ materialHash ];
+					var createdMaterials = [];
 
-					if ( this.materials !== null ) {
+					for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
 
-						material = this.materials.create( sourceMaterial.name );
+						var sourceMaterial = materials[ mi ];
+						var materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;
+						var material = state.materials[ materialHash ];
+
+						if ( this.materials !== null ) {
+
+							material = this.materials.create( sourceMaterial.name );
 
-						// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
-						if ( isLine && material && ! ( material instanceof LineBasicMaterial ) ) {
+							// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
+							if ( isLine && material && ! ( material instanceof LineBasicMaterial ) ) {
 
-							var materialLine = new LineBasicMaterial();
-							Material.prototype.copy.call( materialLine, material );
-							materialLine.color.copy( material.color );
-							material = materialLine;
+								var materialLine = new LineBasicMaterial();
+								Material.prototype.copy.call( materialLine, material );
+								materialLine.color.copy( material.color );
+								material = materialLine;
 
-						} else if ( isPoints && material && ! ( material instanceof PointsMaterial ) ) {
+							} else if ( isPoints && material && ! ( material instanceof PointsMaterial ) ) {
 
-							var materialPoints = new PointsMaterial( { size: 10, sizeAttenuation: false } );
-							Material.prototype.copy.call( materialPoints, material );
-							materialPoints.color.copy( material.color );
-							materialPoints.map = material.map;
-							material = materialPoints;
+								var materialPoints = new PointsMaterial( { size: 10, sizeAttenuation: false } );
+								Material.prototype.copy.call( materialPoints, material );
+								materialPoints.color.copy( material.color );
+								materialPoints.map = material.map;
+								material = materialPoints;
+
+							}
 
 						}
 
-					}
+						if ( material === undefined ) {
 
-					if ( material === undefined ) {
+							if ( isLine ) {
 
-						if ( isLine ) {
+								material = new LineBasicMaterial();
 
-							material = new LineBasicMaterial();
+							} else if ( isPoints ) {
 
-						} else if ( isPoints ) {
+								material = new PointsMaterial( { size: 1, sizeAttenuation: false } );
 
-							material = new PointsMaterial( { size: 1, sizeAttenuation: false } );
+							} else {
 
-						} else {
+								material = new MeshPhongMaterial();
 
-							material = new MeshPhongMaterial();
+							}
 
-						}
+							material.name = sourceMaterial.name;
+							material.flatShading = sourceMaterial.smooth ? false : true;
+							material.vertexColors = hasVertexColors;
 
-						material.name = sourceMaterial.name;
-						material.flatShading = sourceMaterial.smooth ? false : true;
-						material.vertexColors = hasVertexColors;
+							state.materials[ materialHash ] = material;
 
-						state.materials[ materialHash ] = material;
+						}
+
+						createdMaterials.push( material );
 
 					}
 
-					createdMaterials.push( material );
+					// Create mesh
 
-				}
+					var mesh;
 
-				// Create mesh
+					if ( createdMaterials.length > 1 ) {
 
-				var mesh;
+						for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
 
-				if ( createdMaterials.length > 1 ) {
+							var sourceMaterial = materials[ mi ];
+							buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
 
-					for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
+						}
 
-						var sourceMaterial = materials[ mi ];
-						buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
+						if ( isLine ) {
 
-					}
+							mesh = new LineSegments( buffergeometry, createdMaterials );
 
-					if ( isLine ) {
+						} else if ( isPoints ) {
 
-						mesh = new LineSegments( buffergeometry, createdMaterials );
+							mesh = new Points( buffergeometry, createdMaterials );
 
-					} else if ( isPoints ) {
+						} else {
 
-						mesh = new Points( buffergeometry, createdMaterials );
+							mesh = new Mesh( buffergeometry, createdMaterials );
+
+						}
 
 					} else {
 
-						mesh = new Mesh( buffergeometry, createdMaterials );
+						if ( isLine ) {
 
-					}
+							mesh = new LineSegments( buffergeometry, createdMaterials[ 0 ] );
 
-				} else {
+						} else if ( isPoints ) {
 
-					if ( isLine ) {
+							mesh = new Points( buffergeometry, createdMaterials[ 0 ] );
 
-						mesh = new LineSegments( buffergeometry, createdMaterials[ 0 ] );
+						} else {
 
-					} else if ( isPoints ) {
+							mesh = new Mesh( buffergeometry, createdMaterials[ 0 ] );
 
-						mesh = new Points( buffergeometry, createdMaterials[ 0 ] );
+						}
 
-					} else {
+					}
 
-						mesh = new Mesh( buffergeometry, createdMaterials[ 0 ] );
+					mesh.name = object.name;
 
-					}
+					container.add( mesh );
 
 				}
 
-				mesh.name = object.name;
+			} else {
+
+				// if there is only the default parser state object with no geometry data, interpret data as point cloud
+
+				if ( state.vertices.length > 0 ) {
+
+					var material = new PointsMaterial( { size: 1, sizeAttenuation: false } );
+
+					var buffergeometry = new BufferGeometry();
+
+					buffergeometry.setAttribute( 'position', new Float32BufferAttribute( state.vertices, 3 ) );
 
-				container.add( mesh );
+					if ( state.colors.length > 0 ) {
+
+						buffergeometry.setAttribute( 'color', new Float32BufferAttribute( state.colors, 3 ) );
+						material.vertexColors = true;
+
+					}
+
+					var points = new Points( buffergeometry, material );
+					container.add( points );
+
+				}
 
 			}
 

+ 1 - 1
examples/misc_exporter_draco.html

@@ -163,7 +163,7 @@
 
 			function exportFile() {
 
-				const result = exporter.parse( mesh.geometry );
+				const result = exporter.parse( mesh );
 				saveArrayBuffer( result, 'file.drc' );
 
 			}

BIN
examples/screenshots/webgl_loader_gltf_variant.jpg


BIN
examples/screenshots/webgl_loader_gltf_variants.jpg


+ 27 - 66
examples/webgl_loader_gltf_variant.html → examples/webgl_loader_gltf_variants.html

@@ -23,14 +23,11 @@
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 			import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
 			import { RGBELoader } from './jsm/loaders/RGBELoader.js';
-			import { RoughnessMipmapper } from './jsm/utils/RoughnessMipmapper.js';
 
 			let camera, scene, renderer;
 			let gui;
-			
-			const state = {
-				variant: "midnight"
-			};
+
+			const state = { variant: 'midnight' };
 
 			init();
 			render();
@@ -41,14 +38,14 @@
 				document.body.appendChild( container );
 
 				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 );
-				camera.position.set( 3.0, 3.0, - 3.0);
+				camera.position.set( 2.5, 1.5, 3.0 );
 
 				scene = new THREE.Scene();
 
 				new RGBELoader()
 					.setDataType( THREE.UnsignedByteType )
 					.setPath( 'textures/equirectangular/' )
-					.load( 'royal_esplanade_1k.hdr', function ( texture ) {
+					.load( 'quarry_01_1k.hdr', function ( texture ) {
 
 						const envMap = pmremGenerator.fromEquirectangular( texture ).texture;
 
@@ -62,25 +59,11 @@
 
 						// model
 
-						// use of RoughnessMipmapper is optional
-						const roughnessMipmapper = new RoughnessMipmapper( renderer );
-
 						const loader = new GLTFLoader().setPath( 'models/gltf/MaterialsVariantsShoe/glTF/' );
 						loader.load( 'MaterialsVariantsShoe.gltf', function ( gltf ) {
 
 							gltf.scene.scale.set( 10.0, 10.0, 10.0 );
 
-							gltf.scene.traverse( function ( child ) {
-
-								if ( child.isMesh ) {
-
-									// TOFIX RoughnessMipmapper seems to be broken with WebGL 2.0
-									// roughnessMipmapper.generateMipmaps( child.material );
-
-								}
-
-							} );
-
 							scene.add( gltf.scene );
 
 							// GUI
@@ -88,31 +71,14 @@
 
 							// Details of the KHR_materials_variants extension used here can be found below
 							// https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_variants
-							
-							if ( gltf.userData.gltfExtensions !== undefined ) {
-
-								let parser = gltf.parser;
-
-								const extension = gltf.userData.gltfExtensions[ 'KHR_materials_variants' ];
-
-								if ( extension !== undefined ) {
-
-									let variants = extension.variants.map( variant => variant.name );
-									let guiVariants = gui.add( state, 'variant', variants ).name( 'Variant' );
-									
-									changeMaterialByVariantName(scene, parser, extension, state.variant);
+							const parser = gltf.parser;
+							const variantsExtension = gltf.userData.gltfExtensions[ 'KHR_materials_variants' ];
+							const variants = variantsExtension.variants.map( ( variant ) => variant.name );
+							const variantsCtrl = gui.add( state, 'variant', variants ).name( 'Variant' );
 
-									guiVariants.onChange( function ( value ) {
+							selectVariant( scene, parser, variantsExtension, state.variant );
 
-										changeMaterialByVariantName(scene, parser, extension, value);
-
-									});
-
-								}
-
-							}
-
-							roughnessMipmapper.dispose();
+							variantsCtrl.onChange( ( value ) => selectVariant( scene, parser, variantsExtension, value ) );
 
 							render();
 
@@ -135,52 +101,47 @@
 				controls.addEventListener( 'change', render ); // use if there is no animation loop
 				controls.minDistance = 2;
 				controls.maxDistance = 10;
-				controls.target.set( 0, 0, - 0.2 );
+				controls.target.set( 0, 0.5, - 0.2 );
 				controls.update();
 
 				window.addEventListener( 'resize', onWindowResize, false );
 
 			}
 
-			function changeMaterialByVariantName( scene, parser, extension, variantName ) {
+			function selectVariant( scene, parser, extension, variantName ) {
 
 				const variantIndex = extension.variants.findIndex( ( v ) => v.name.includes( variantName ) );
 
 				scene.traverse( async ( object ) => {
 
-					if ( !object.isMesh ) return;
-
-					const meshVariantData = object.userData.gltfExtensions[ 'KHR_materials_variants' ];
-
-					if ( meshVariantData !== undefined ) {
+					if ( ! object.isMesh || ! object.userData.gltfExtensions ) return;
 
-						let materialIndex = - 1;
+					const meshVariantDef = object.userData.gltfExtensions[ 'KHR_materials_variants' ];
 
-						for ( let i = 0; i < meshVariantData.mappings.length; i ++ ) {
+					if ( ! meshVariantDef ) return;
 
-							const mapping = meshVariantData.mappings[ i ];
+					if ( ! object.userData.originalMaterial ) {
 
-							if ( mapping.variants.indexOf( variantIndex ) != - 1 ) {
+						object.userData.originalMaterial = object.material;
 
-								materialIndex = mapping.material;
-
-								break;
+					}
 
-							}
+					const mapping = meshVariantDef.mappings
+						.find( ( mapping ) => mapping.variants.includes( variantIndex ) );
 
-						}
+					if ( mapping ) {
 
-						if ( materialIndex != - 1 ) {
+						object.material = await parser.getDependency( 'material', mapping.material );
 
-							object.material = await parser.getDependency( 'material', materialIndex );
+					} else {
 
-							render();
-
-						}
+						object.material = object.originalMaterial;
 
 					}
 
-				});
+					render();
+
+				} );
 
 			}
 

+ 52 - 4
test/unit/src/core/Object3D.tests.js

@@ -275,15 +275,63 @@ export default QUnit.module( 'Core', () => {
 
 		} );
 
-		QUnit.todo( "localToWorld", ( assert ) => {
+		QUnit.test( "localToWorld", ( assert ) => {
 
-			assert.ok( false, "everything's gonna be alright" );
+			const v = new Vector3();
+			const expectedPosition = new Vector3( 5, - 1, - 4 );
+
+			const parent = new Object3D();
+			const child = new Object3D();
+
+			parent.position.set( 1, 0, 0 );
+			parent.rotation.set( 0, Math.PI / 2, 0 );
+			parent.scale.set( 2, 1, 1 );
+
+			child.position.set( 0, 1, 0 );
+			child.rotation.set( Math.PI / 2, 0, 0 );
+			child.scale.set( 1, 2, 1 );
+
+			parent.add( child );
+			parent.updateMatrixWorld();
+
+			child.localToWorld( v.set( 2, 2, 2 ) );
+
+			assert.ok(
+				Math.abs( v.x - expectedPosition.x ) <= eps &&
+				Math.abs( v.y - expectedPosition.y ) <= eps &&
+				Math.abs( v.z - expectedPosition.z ) <= eps,
+				"local vector is converted to world"
+			);
 
 		} );
 
-		QUnit.todo( "worldToLocal", ( assert ) => {
+		QUnit.test( "worldToLocal", ( assert ) => {
 
-			assert.ok( false, "everything's gonna be alright" );
+			const v = new Vector3();
+			const expectedPosition = new Vector3( - 1, 0.5, - 1 );
+
+			const parent = new Object3D();
+			const child = new Object3D();
+
+			parent.position.set( 1, 0, 0 );
+			parent.rotation.set( 0, Math.PI / 2, 0 );
+			parent.scale.set( 2, 1, 1 );
+
+			child.position.set( 0, 1, 0 );
+			child.rotation.set( Math.PI / 2, 0, 0 );
+			child.scale.set( 1, 2, 1 );
+
+			parent.add( child );
+			parent.updateMatrixWorld();
+
+			child.worldToLocal( v.set( 2, 2, 2 ) );
+
+			assert.ok(
+				Math.abs( v.x - expectedPosition.x ) <= eps &&
+				Math.abs( v.y - expectedPosition.y ) <= eps &&
+				Math.abs( v.z - expectedPosition.z ) <= eps,
+				"world vector is converted to local"
+			);
 
 		} );