Browse Source

Updated examples builds.

Mr.doob 3 years ago
parent
commit
9c57b2e117

+ 2 - 2
examples/js/csm/CSM.js

@@ -2,7 +2,7 @@
 
 	const _cameraToLightMatrix = new THREE.Matrix4();
 
-	const _lightSpaceFrustum = new THREE.Frustum();
+	const _lightSpaceFrustum = new THREE.CSMFrustum();
 
 	const _center = new THREE.Vector3();
 
@@ -29,7 +29,7 @@
 			this.lightMargin = data.lightMargin || 200;
 			this.customSplitsCallback = data.customSplitsCallback;
 			this.fade = false;
-			this.mainFrustum = new THREE.Frustum();
+			this.mainFrustum = new THREE.CSMFrustum();
 			this.frustums = [];
 			this.breaks = [];
 			this.lights = [];

+ 133 - 0
examples/js/csm/CSMFrustum.js

@@ -0,0 +1,133 @@
+( function () {
+
+	const inverseProjectionMatrix = new THREE.Matrix4();
+
+	class CSMFrustum {
+
+		constructor( data ) {
+
+			data = data || {};
+			this.vertices = {
+				near: [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ],
+				far: [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]
+			};
+
+			if ( data.projectionMatrix !== undefined ) {
+
+				this.setFromProjectionMatrix( data.projectionMatrix, data.maxFar || 10000 );
+
+			}
+
+		}
+
+		setFromProjectionMatrix( projectionMatrix, maxFar ) {
+
+			const isOrthographic = projectionMatrix.elements[ 2 * 4 + 3 ] === 0;
+			inverseProjectionMatrix.copy( projectionMatrix ).invert(); // 3 --- 0  vertices.near/far order
+			// |     |
+			// 2 --- 1
+			// clip space spans from [-1, 1]
+
+			this.vertices.near[ 0 ].set( 1, 1, - 1 );
+			this.vertices.near[ 1 ].set( 1, - 1, - 1 );
+			this.vertices.near[ 2 ].set( - 1, - 1, - 1 );
+			this.vertices.near[ 3 ].set( - 1, 1, - 1 );
+			this.vertices.near.forEach( function ( v ) {
+
+				v.applyMatrix4( inverseProjectionMatrix );
+
+			} );
+			this.vertices.far[ 0 ].set( 1, 1, 1 );
+			this.vertices.far[ 1 ].set( 1, - 1, 1 );
+			this.vertices.far[ 2 ].set( - 1, - 1, 1 );
+			this.vertices.far[ 3 ].set( - 1, 1, 1 );
+			this.vertices.far.forEach( function ( v ) {
+
+				v.applyMatrix4( inverseProjectionMatrix );
+				const absZ = Math.abs( v.z );
+
+				if ( isOrthographic ) {
+
+					v.z *= Math.min( maxFar / absZ, 1.0 );
+
+				} else {
+
+					v.multiplyScalar( Math.min( maxFar / absZ, 1.0 ) );
+
+				}
+
+			} );
+			return this.vertices;
+
+		}
+
+		split( breaks, target ) {
+
+			while ( breaks.length > target.length ) {
+
+				target.push( new CSMFrustum() );
+
+			}
+
+			target.length = breaks.length;
+
+			for ( let i = 0; i < breaks.length; i ++ ) {
+
+				const cascade = target[ i ];
+
+				if ( i === 0 ) {
+
+					for ( let j = 0; j < 4; j ++ ) {
+
+						cascade.vertices.near[ j ].copy( this.vertices.near[ j ] );
+
+					}
+
+				} else {
+
+					for ( let j = 0; j < 4; j ++ ) {
+
+						cascade.vertices.near[ j ].lerpVectors( this.vertices.near[ j ], this.vertices.far[ j ], breaks[ i - 1 ] );
+
+					}
+
+				}
+
+				if ( i === breaks.length - 1 ) {
+
+					for ( let j = 0; j < 4; j ++ ) {
+
+						cascade.vertices.far[ j ].copy( this.vertices.far[ j ] );
+
+					}
+
+				} else {
+
+					for ( let j = 0; j < 4; j ++ ) {
+
+						cascade.vertices.far[ j ].lerpVectors( this.vertices.near[ j ], this.vertices.far[ j ], breaks[ i ] );
+
+					}
+
+				}
+
+			}
+
+		}
+
+		toSpace( cameraMatrix, target ) {
+
+			for ( var i = 0; i < 4; i ++ ) {
+
+				target.vertices.near[ i ].copy( this.vertices.near[ i ] ).applyMatrix4( cameraMatrix );
+				target.vertices.far[ i ].copy( this.vertices.far[ i ] ).applyMatrix4( cameraMatrix );
+
+			}
+
+		}
+
+	}
+
+	THREE.CSMFrustum = CSMFrustum;
+
+} )();

+ 146 - 62
examples/js/exporters/GLTFExporter.js

@@ -30,6 +30,11 @@
 				return new GLTFMaterialsVolumeExtension( writer );
 
 			} );
+			this.register( function ( writer ) {
+
+				return new GLTFMaterialsClearcoatExtension( writer );
+
+			} );
 
 		}
 
@@ -60,11 +65,19 @@
    * Parse scenes and generate GLTF output
    * @param  {Scene or [THREE.Scenes]} input   THREE.Scene or Array of THREE.Scenes
    * @param  {Function} onDone  Callback on completed
+   * @param  {Function} onError  Callback on errors
    * @param  {Object} options options
    */
 
 
-		parse( input, onDone, options ) {
+		parse( input, onDone, onError, options ) {
+
+			if ( typeof onError === 'object' ) {
+
+				console.warn( 'THREE.GLTFExporter: parse() expects options as the fourth argument now.' );
+				options = onError;
+
+			}
 
 			const writer = new GLTFWriter();
 			const plugins = [];
@@ -76,7 +89,18 @@
 			}
 
 			writer.setPlugins( plugins );
-			writer.write( input, onDone, options );
+			writer.write( input, onDone, options ).catch( onError );
+
+		}
+
+		parseAsync( input, options ) {
+
+			const scope = this;
+			return new Promise( function ( resolve, reject ) {
+
+				scope.parse( input, resolve, reject, options );
+
+			} );
 
 		}
 
@@ -337,7 +361,7 @@
    */
 
 
-		write( input, onDone, options ) {
+		async write( input, onDone, options ) {
 
 			this.options = Object.assign( {}, {
 				// default options
@@ -359,86 +383,83 @@
 			}
 
 			this.processInput( input );
+			await Promise.all( this.pending );
 			const writer = this;
-			Promise.all( this.pending ).then( function () {
-
-				const buffers = writer.buffers;
-				const json = writer.json;
-				const options = writer.options;
-				const extensionsUsed = writer.extensionsUsed; // Merge buffers.
-
-				const blob = new Blob( buffers, {
-					type: 'application/octet-stream'
-				} ); // Declare extensions.
-
-				const extensionsUsedList = Object.keys( extensionsUsed );
-				if ( extensionsUsedList.length > 0 ) json.extensionsUsed = extensionsUsedList; // Update bytelength of the single buffer.
-
-				if ( json.buffers && json.buffers.length > 0 ) json.buffers[ 0 ].byteLength = blob.size;
-
-				if ( options.binary === true ) {
-
-					// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification
-					const reader = new window.FileReader();
-					reader.readAsArrayBuffer( blob );
+			const buffers = writer.buffers;
+			const json = writer.json;
+			options = writer.options;
+			const extensionsUsed = writer.extensionsUsed; // Merge buffers.
 
-					reader.onloadend = function () {
+			const blob = new Blob( buffers, {
+				type: 'application/octet-stream'
+			} ); // Declare extensions.
 
-						// Binary chunk.
-						const binaryChunk = getPaddedArrayBuffer( reader.result );
-						const binaryChunkPrefix = new DataView( new ArrayBuffer( GLB_CHUNK_PREFIX_BYTES ) );
-						binaryChunkPrefix.setUint32( 0, binaryChunk.byteLength, true );
-						binaryChunkPrefix.setUint32( 4, GLB_CHUNK_TYPE_BIN, true ); // JSON chunk.
+			const extensionsUsedList = Object.keys( extensionsUsed );
+			if ( extensionsUsedList.length > 0 ) json.extensionsUsed = extensionsUsedList; // Update bytelength of the single buffer.
 
-						const jsonChunk = getPaddedArrayBuffer( stringToArrayBuffer( JSON.stringify( json ) ), 0x20 );
-						const jsonChunkPrefix = new DataView( new ArrayBuffer( GLB_CHUNK_PREFIX_BYTES ) );
-						jsonChunkPrefix.setUint32( 0, jsonChunk.byteLength, true );
-						jsonChunkPrefix.setUint32( 4, GLB_CHUNK_TYPE_JSON, true ); // GLB header.
+			if ( json.buffers && json.buffers.length > 0 ) json.buffers[ 0 ].byteLength = blob.size;
 
-						const header = new ArrayBuffer( GLB_HEADER_BYTES );
-						const headerView = new DataView( header );
-						headerView.setUint32( 0, GLB_HEADER_MAGIC, true );
-						headerView.setUint32( 4, GLB_VERSION, true );
-						const totalByteLength = GLB_HEADER_BYTES + jsonChunkPrefix.byteLength + jsonChunk.byteLength + binaryChunkPrefix.byteLength + binaryChunk.byteLength;
-						headerView.setUint32( 8, totalByteLength, true );
-						const glbBlob = new Blob( [ header, jsonChunkPrefix, jsonChunk, binaryChunkPrefix, binaryChunk ], {
-							type: 'application/octet-stream'
-						} );
-						const glbReader = new window.FileReader();
-						glbReader.readAsArrayBuffer( glbBlob );
+			if ( options.binary === true ) {
 
-						glbReader.onloadend = function () {
+				// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification
+				const reader = new window.FileReader();
+				reader.readAsArrayBuffer( blob );
 
-							onDone( glbReader.result );
+				reader.onloadend = function () {
 
-						};
+					// Binary chunk.
+					const binaryChunk = getPaddedArrayBuffer( reader.result );
+					const binaryChunkPrefix = new DataView( new ArrayBuffer( GLB_CHUNK_PREFIX_BYTES ) );
+					binaryChunkPrefix.setUint32( 0, binaryChunk.byteLength, true );
+					binaryChunkPrefix.setUint32( 4, GLB_CHUNK_TYPE_BIN, true ); // JSON chunk.
+
+					const jsonChunk = getPaddedArrayBuffer( stringToArrayBuffer( JSON.stringify( json ) ), 0x20 );
+					const jsonChunkPrefix = new DataView( new ArrayBuffer( GLB_CHUNK_PREFIX_BYTES ) );
+					jsonChunkPrefix.setUint32( 0, jsonChunk.byteLength, true );
+					jsonChunkPrefix.setUint32( 4, GLB_CHUNK_TYPE_JSON, true ); // GLB header.
+
+					const header = new ArrayBuffer( GLB_HEADER_BYTES );
+					const headerView = new DataView( header );
+					headerView.setUint32( 0, GLB_HEADER_MAGIC, true );
+					headerView.setUint32( 4, GLB_VERSION, true );
+					const totalByteLength = GLB_HEADER_BYTES + jsonChunkPrefix.byteLength + jsonChunk.byteLength + binaryChunkPrefix.byteLength + binaryChunk.byteLength;
+					headerView.setUint32( 8, totalByteLength, true );
+					const glbBlob = new Blob( [ header, jsonChunkPrefix, jsonChunk, binaryChunkPrefix, binaryChunk ], {
+						type: 'application/octet-stream'
+					} );
+					const glbReader = new window.FileReader();
+					glbReader.readAsArrayBuffer( glbBlob );
+
+					glbReader.onloadend = function () {
+
+						onDone( glbReader.result );
 
 					};
 
-				} else {
+				};
 
-					if ( json.buffers && json.buffers.length > 0 ) {
+			} else {
 
-						const reader = new window.FileReader();
-						reader.readAsDataURL( blob );
+				if ( json.buffers && json.buffers.length > 0 ) {
 
-						reader.onloadend = function () {
+					const reader = new window.FileReader();
+					reader.readAsDataURL( blob );
 
-							const base64data = reader.result;
-							json.buffers[ 0 ].uri = base64data;
-							onDone( json );
+					reader.onloadend = function () {
 
-						};
+						const base64data = reader.result;
+						json.buffers[ 0 ].uri = base64data;
+						onDone( json );
 
-					} else {
+					};
 
-						onDone( json );
+				} else {
 
-					}
+					onDone( json );
 
 				}
 
-			} );
+			}
 
 		}
 		/**
@@ -2066,6 +2087,69 @@
 
 		}
 
+	}
+	/**
+ * Clearcoat Materials Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat
+ */
+
+
+	class GLTFMaterialsClearcoatExtension {
+
+		constructor( writer ) {
+
+			this.writer = writer;
+			this.name = 'KHR_materials_clearcoat';
+
+		}
+
+		writeMaterial( material, materialDef ) {
+
+			if ( ! material.isMeshPhysicalMaterial ) return;
+			const writer = this.writer;
+			const extensionsUsed = writer.extensionsUsed;
+			const extensionDef = {};
+			extensionDef.clearcoatFactor = material.clearcoat;
+
+			if ( material.clearcoatMap ) {
+
+				const clearcoatMapDef = {
+					index: writer.processTexture( material.clearcoatMap )
+				};
+				writer.applyTextureTransform( clearcoatMapDef, material.clearcoatMap );
+				extensionDef.clearcoatTexture = clearcoatMapDef;
+
+			}
+
+			extensionDef.clearcoatRoughnessFactor = material.clearcoatRoughness;
+
+			if ( material.clearcoatRoughnessMap ) {
+
+				const clearcoatRoughnessMapDef = {
+					index: writer.processTexture( material.clearcoatRoughnessMap )
+				};
+				writer.applyTextureTransform( clearcoatRoughnessMapDef, material.clearcoatRoughnessMap );
+				extensionDef.clearcoatRoughnessTexture = clearcoatRoughnessMapDef;
+
+			}
+
+			if ( material.clearcoatNormalMap ) {
+
+				const clearcoatNormalMapDef = {
+					index: writer.processTexture( material.clearcoatNormalMap )
+				};
+				writer.applyTextureTransform( clearcoatNormalMapDef, material.clearcoatNormalMap );
+				extensionDef.clearcoatNormalTexture = clearcoatNormalMapDef;
+
+			}
+
+			materialDef.extensions = materialDef.extensions || {};
+			materialDef.extensions[ this.name ] = extensionDef;
+			extensionsUsed[ this.name ] = true;
+
+		}
+
 	}
 	/**
  * Transmission Materials Extension
@@ -2126,7 +2210,7 @@
 
 		writeMaterial( material, materialDef ) {
 
-			if ( ! material.isMeshPhysicalMaterial || material.thickness === 0 ) return;
+			if ( ! material.isMeshPhysicalMaterial || material.transmission === 0 ) return;
 			const writer = this.writer;
 			const extensionsUsed = writer.extensionsUsed;
 			const extensionDef = {};

+ 20 - 12
examples/js/exporters/USDZExporter.js

@@ -13,26 +13,34 @@
 			const textures = {};
 			scene.traverseVisible( object => {
 
-				if ( object.isMesh && object.material.isMeshStandardMaterial ) {
+				if ( object.isMesh ) {
 
-					const geometry = object.geometry;
-					const material = object.material;
-					const geometryFileName = 'geometries/Geometry_' + geometry.id + '.usd';
+					if ( object.material.isMeshStandardMaterial ) {
 
-					if ( ! ( geometryFileName in files ) ) {
+						const geometry = object.geometry;
+						const material = object.material;
+						const geometryFileName = 'geometries/Geometry_' + geometry.id + '.usd';
 
-						const meshObject = buildMeshObject( geometry );
-						files[ geometryFileName ] = buildUSDFileAsString( meshObject );
+						if ( ! ( geometryFileName in files ) ) {
 
-					}
+							const meshObject = buildMeshObject( geometry );
+							files[ geometryFileName ] = buildUSDFileAsString( meshObject );
 
-					if ( ! ( material.uuid in materials ) ) {
+						}
 
-						materials[ material.uuid ] = material;
+						if ( ! ( material.uuid in materials ) ) {
 
-					}
+							materials[ material.uuid ] = material;
+
+						}
 
-					output += buildXform( object, geometry, material );
+						output += buildXform( object, geometry, material );
+
+					} else {
+
+						console.warn( 'THREE.USDZExporter: Unsupported material type (USDZ only supports MeshStandardMaterial)', object );
+
+					}
 
 				}
 

+ 6 - 1
examples/js/lines/LineMaterial.js

@@ -7,6 +7,7 @@
  *  dashed: <boolean>,
  *  dashScale: <float>,
  *  dashSize: <float>,
+ *  dashOffset: <float>,
  *  gapSize: <float>,
  *  resolution: <Vector2>, // to be set by renderer
  * }
@@ -21,6 +22,9 @@
 		resolution: {
 			value: new THREE.Vector2( 1, 1 )
 		},
+		dashOffset: {
+			value: 0
+		},
 		dashScale: {
 			value: 1
 		},
@@ -274,6 +278,7 @@
 
 		#ifdef USE_DASH
 
+			uniform float dashOffset;
 			uniform float dashSize;
 			uniform float gapSize;
 
@@ -342,7 +347,7 @@
 
 				if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps
 
-				if ( mod( vLineDistance, dashSize + gapSize ) > dashSize ) discard; // todo - FIX
+				if ( mod( vLineDistance + dashOffset, dashSize + gapSize ) > dashSize ) discard; // todo - FIX
 
 			#endif
 

+ 29 - 24
examples/js/lines/LineSegments2.js

@@ -22,7 +22,29 @@
 
 	const _sphere = new THREE.Sphere();
 
-	const _clipToWorldVector = new THREE.Vector4();
+	const _clipToWorldVector = new THREE.Vector4(); // Returns the margin required to expand by in world space given the distance from the camera,
+	// line width, resolution, and camera projection
+
+
+	function getWorldSpaceHalfWidth( camera, distance, lineWidth, resolution ) {
+
+		// transform into clip space, adjust the x and y values by the pixel width offset, then
+		// transform back into world space to get world offset. Note clip space is [-1, 1] so full
+		// width does not need to be halved.
+		_clipToWorldVector.set( 0, 0, - distance, 1.0 ).applyMatrix4( camera.projectionMatrix );
+
+		_clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w );
+
+		_clipToWorldVector.x = lineWidth / resolution.width;
+		_clipToWorldVector.y = lineWidth / resolution.height;
+
+		_clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse );
+
+		_clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w );
+
+		return Math.abs( Math.max( _clipToWorldVector.x, _clipToWorldVector.y ) );
+
+	}
 
 	class LineSegments2 extends THREE.Mesh {
 
@@ -84,10 +106,7 @@
 			const instanceStart = geometry.attributes.instanceStart;
 			const instanceEnd = geometry.attributes.instanceEnd; // camera forward is negative
 
-			const near = - camera.near; // clip space is [ - 1, 1 ] so multiply by two to get the full
-			// width in clip space
-
-			const ssMaxWidth = 2.0 * Math.max( lineWidth / resolution.width, lineWidth / resolution.height ); //
+			const near = - camera.near; //
 			// check if we intersect the sphere bounds
 
 			if ( geometry.boundingSphere === null ) {
@@ -98,16 +117,9 @@
 
 			_sphere.copy( geometry.boundingSphere ).applyMatrix4( matrixWorld );
 
-			const distanceToSphere = Math.max( camera.near, _sphere.distanceToPoint( ray.origin ) ); // get the w component to scale the world space line width
-
-			_clipToWorldVector.set( 0, 0, - distanceToSphere, 1.0 ).applyMatrix4( camera.projectionMatrix );
-
-			_clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w );
-
-			_clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse ); // increase the sphere bounds by the worst case line screen space width
-
+			const distanceToSphere = Math.max( camera.near, _sphere.distanceToPoint( ray.origin ) ); // increase the sphere bounds by the worst case line screen space width
 
-			const sphereMargin = Math.abs( ssMaxWidth / _clipToWorldVector.w ) * 0.5;
+			const sphereMargin = getWorldSpaceHalfWidth( camera, distanceToSphere, lineWidth, resolution );
 			_sphere.radius += sphereMargin;
 
 			if ( raycaster.ray.intersectsSphere( _sphere ) === false ) {
@@ -126,16 +138,9 @@
 
 			_box.copy( geometry.boundingBox ).applyMatrix4( matrixWorld );
 
-			const distanceToBox = Math.max( camera.near, _box.distanceToPoint( ray.origin ) ); // get the w component to scale the world space line width
-
-			_clipToWorldVector.set( 0, 0, - distanceToBox, 1.0 ).applyMatrix4( camera.projectionMatrix );
-
-			_clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w );
-
-			_clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse ); // increase the sphere bounds by the worst case line screen space width
-
+			const distanceToBox = Math.max( camera.near, _box.distanceToPoint( ray.origin ) ); // increase the box bounds by the worst case line screen space width
 
-			const boxMargin = Math.abs( ssMaxWidth / _clipToWorldVector.w ) * 0.5;
+			const boxMargin = getWorldSpaceHalfWidth( camera, distanceToBox, lineWidth, resolution );
 			_box.max.x += boxMargin;
 			_box.max.y += boxMargin;
 			_box.max.z += boxMargin;
@@ -276,7 +281,7 @@
 
 	}
 
-	LineSegments2.prototype.LineSegments2 = true;
+	LineSegments2.prototype.isLineSegments2 = true;
 
 	THREE.LineSegments2 = LineSegments2;
 

+ 1 - 1
examples/js/loaders/3MFLoader.js

@@ -968,7 +968,7 @@
 				geometry.setIndex( new THREE.BufferAttribute( meshData[ 'triangles' ], 1 ) );
 				geometry.setAttribute( 'position', new THREE.BufferAttribute( meshData[ 'vertices' ], 3 ) );
 				const material = new THREE.MeshPhongMaterial( {
-					color: 0xaaaaff,
+					color: 0xffffff,
 					flatShading: true
 				} );
 				const mesh = new THREE.Mesh( geometry, material );

+ 11 - 1
examples/js/loaders/GLTFLoader.js

@@ -296,6 +296,17 @@
 
 		}
 
+		parseAsync( data, path ) {
+
+			const scope = this;
+			return new Promise( function ( resolve, reject ) {
+
+				scope.parse( data, path, resolve, reject );
+
+			} );
+
+		}
+
 	}
 	/* GLTFREGISTRY */
 
@@ -3308,7 +3319,6 @@
 
 					if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) {
 
-						// Node may be a THREE.Group (glTF mesh with several primitives) or a THREE.Mesh.
 						node.traverse( function ( object ) {
 
 							if ( object.morphTargetInfluences ) {

+ 8 - 6
examples/js/loaders/LWOLoader.js

@@ -6,10 +6,10 @@
  * @desc Load files in LWO3 and LWO2 format on Three.js
  *
  * LWO3 format specification:
- * 	http://static.lightwave3d.com/sdk/2018/html/filefmts/lwo3.html
+ *  https://static.lightwave3d.com/sdk/2019/html/filefmts/lwo3.html
  *
  * LWO2 format specification:
- * 	http://static.lightwave3d.com/sdk/2018/html/filefmts/lwo2.html
+ *  https://static.lightwave3d.com/sdk/2019/html/filefmts/lwo2.html
  *
  **/
 
@@ -391,7 +391,7 @@
 
 					case 'Roughness':
 						maps.roughnessMap = texture;
-						maps.roughness = 0.5;
+						maps.roughness = 1;
 						break;
 
 					case 'Specular':
@@ -410,7 +410,7 @@
 
 					case 'Metallic':
 						maps.metalnessMap = texture;
-						maps.metalness = 0.5;
+						maps.metalness = 1;
 						break;
 
 					case 'Transparency':
@@ -525,7 +525,7 @@
 			}
 
 			if ( attributes[ 'Bump Height' ] ) params.bumpScale = attributes[ 'Bump Height' ].value * 0.1;
-			if ( attributes[ 'Refraction Index' ] ) params.refractionRatio = 1 / attributes[ 'Refraction Index' ].value;
+			if ( attributes[ 'Refraction Index' ] ) params.refractionRatio = 0.98 / attributes[ 'Refraction Index' ].value;
 			this.parsePhysicalAttributes( params, attributes, maps );
 			this.parseStandardAttributes( params, attributes, maps );
 			this.parsePhongAttributes( params, attributes, maps );
@@ -639,10 +639,12 @@
 
 					if ( attributes.metalness !== undefined ) {
 
-						delete attributes.metalness;
+						attributes.metalness = 1; // For most transparent materials metalness should be set to 1 if not otherwise defined. If set to 0 no refraction will be visible
 
 					}
 
+					attributes.opacity = 1; // transparency fades out refraction, forcing opacity to 1 ensures a closer visual match to the material in Lightwave.
+
 				} else envMap.mapping = THREE.EquirectangularReflectionMapping;
 
 				maps.envMap = envMap;

+ 32 - 7
examples/js/loaders/PLYLoader.js

@@ -343,25 +343,50 @@
 
 			function handleElement( buffer, elementName, element ) {
 
+				function findAttrName( names ) {
+
+					for ( let i = 0, l = names.length; i < l; i ++ ) {
+
+						const name = names[ i ];
+						if ( name in element ) return name;
+
+					}
+
+					return null;
+
+				}
+
+				const attrX = findAttrName( [ 'x', 'px', 'posx' ] ) || 'x';
+				const attrY = findAttrName( [ 'y', 'py', 'posy' ] ) || 'y';
+				const attrZ = findAttrName( [ 'z', 'pz', 'posz' ] ) || 'z';
+				const attrNX = findAttrName( [ 'nx', 'normalx' ] );
+				const attrNY = findAttrName( [ 'ny', 'normaly' ] );
+				const attrNZ = findAttrName( [ 'nz', 'normalz' ] );
+				const attrS = findAttrName( [ 's', 'u', 'texture_u', 'tx' ] );
+				const attrT = findAttrName( [ 't', 'v', 'texture_v', 'ty' ] );
+				const attrR = findAttrName( [ 'red', 'diffuse_red', 'r', 'diffuse_r' ] );
+				const attrG = findAttrName( [ 'green', 'diffuse_green', 'g', 'diffuse_g' ] );
+				const attrB = findAttrName( [ 'blue', 'diffuse_blue', 'b', 'diffuse_b' ] );
+
 				if ( elementName === 'vertex' ) {
 
-					buffer.vertices.push( element.x, element.y, element.z );
+					buffer.vertices.push( element[ attrX ], element[ attrY ], element[ attrZ ] );
 
-					if ( 'nx' in element && 'ny' in element && 'nz' in element ) {
+					if ( attrNX !== null && attrNY !== null && attrNZ !== null ) {
 
-						buffer.normals.push( element.nx, element.ny, element.nz );
+						buffer.normals.push( element[ attrNX ], element[ attrNY ], element[ attrNZ ] );
 
 					}
 
-					if ( 's' in element && 't' in element ) {
+					if ( attrS !== null && attrT !== null ) {
 
-						buffer.uvs.push( element.s, element.t );
+						buffer.uvs.push( element[ attrS ], element[ attrT ] );
 
 					}
 
-					if ( 'red' in element && 'green' in element && 'blue' in element ) {
+					if ( attrR !== null && attrG !== null && attrB !== null ) {
 
-						buffer.colors.push( element.red / 255.0, element.green / 255.0, element.blue / 255.0 );
+						buffer.colors.push( element[ attrR ] / 255.0, element[ attrG ] / 255.0, element[ attrB ] / 255.0 );
 
 					}
 

+ 7 - 3
examples/js/loaders/SVGLoader.js

@@ -1887,6 +1887,7 @@
 				}
 
 				return {
+					curves: p.curves,
 					points: points,
 					isCW: THREE.ShapeUtils.isClockWise( points ),
 					identifier: identifier ++,
@@ -1904,12 +1905,15 @@
 
 				if ( ! amIAHole.isHole ) {
 
-					const shape = new THREE.Shape( p.points );
+					const shape = new THREE.Shape();
+					shape.curves = p.curves;
 					const holes = isAHole.filter( h => h.isHole && h.for === p.identifier );
 					holes.forEach( h => {
 
-						const path = simplePaths[ h.identifier ];
-						shape.holes.push( new THREE.Path( path.points ) );
+						const hole = simplePaths[ h.identifier ];
+						const path = new THREE.Path();
+						path.curves = hole.curves;
+						shape.holes.push( path );
 
 					} );
 					shapesToReturn.push( shape );

+ 1 - 0
examples/js/loaders/lwo/LWO2Parser.js

@@ -105,6 +105,7 @@
 				case 'WRPH': // image wrap h
 
 				case 'NMOD':
+				case 'NSEL':
 				case 'NPRW':
 				case 'NPLA':
 				case 'NODS':

+ 4 - 2
examples/js/loaders/lwo/LWO3Parser.js

@@ -38,8 +38,10 @@
 
 				case 'NORM': // ENVL FORM skipped
 
-				case 'PRE ':
-				case 'POST':
+				case 'PRE ': // Pre-loop behavior for the keyframe
+
+				case 'POST': // Post-loop behavior for the keyframe
+
 				case 'KEY ':
 				case 'SPAN': // CLIP FORM skipped