소스 검색

3DMLoader: Update to support Rhino 8 files. (#26745)

* Fixed eslint errors for examples

* first RDK additions to 3dmLoader

* updated PBR material properties conversion

* compromises for glass

* extractProperties now recursive

* update table count to properties

* update 3dmLoader

* update 3dmLoader

* Added material conversion

* add renderSettings, remove test _initLibrary

* added missing semis

* cleanup 3dmLoader update
Luis E. Fraguada 1 년 전
부모
커밋
67cece3e1f

+ 66 - 60
docs/examples/en/loaders/3DMLoader.html

@@ -14,7 +14,7 @@
 			A loader for Rhinoceros 3d files and objects. <br /><br />
 			Rhinoceros is a 3D modeler used to create, edit, analyze, document, render, animate, and translate NURBS curves, surfaces, breps, extrusions, point clouds, as well as polygon meshes and SubD objects.
 			[link:https://github.com/mcneel/rhino3dm rhino3dm.js] is compiled to WebAssembly from the open source geometry library [link:https://github.com/mcneel/opennurbs openNURBS].
-			The loader currently uses [link:https://www.npmjs.com/package/rhino3dm/v/0.15.0-beta rhino3dm.js 0.15.0-beta.]
+			The loader currently uses [link:https://www.npmjs.com/package/rhino3dm/v/8.0.0-beta2 rhino3dm.js 8.0.0-beta2.]
 		</p>
 
 		<h2>Import</h2>
@@ -44,7 +44,7 @@
 				<td>[page:Points Points]</td>
 			</tr>
 			<tr>
-				<td>PointSet</td>
+				<td>PointSet / PointCloud</td>
 				<td>[page:Points Points]</td>
 			</tr>
 			<tr>
@@ -95,58 +95,34 @@
 				<td>File3dm</td>
 				<td>[page:Object3D Object3D]<sup> 4</sup></td>
 			</tr>
+			<tr>
+				<td>Material / Physically Based Material</td>
+				<td>[page:MeshPhysicalMaterial MeshPhysicalMaterial]</td>
+			</tr>
 		</table>
 
+		<h3>Notes:</h3>
+
 		<p><i>
 			<sup>1</sup> NURBS curves are discretized to a hardcoded resolution.
 		</i></p>
 		<p><i>
-			<sup>2</sup> Types which are based on BREPs and NURBS surfaces are represented with their "Render Mesh". Render meshes might not be associated with these objects if they have not been displayed in an appropriate display mode in Rhino (i.e. "Shaded", "Rendered", etc), or are created programmatically, for example, via Grasshopper or directly with the rhino3dm library.
+			<sup>2</sup> Types which are based on BREPs and NURBS surfaces are represented with their "Render Mesh". Render meshes might not be associated with these objects if they have not been displayed in an appropriate display mode in Rhino (i.e. "Shaded", "Rendered", etc), or are created programmatically, for example, via Grasshopper or directly with the rhino3dm library. As of [email protected], BrepFace and Extrusions can be assigned a mesh representation, but these must be generated by the user.
 		</i></p>
 		<p><i>
 			<sup>3</sup> SubD objects are represented by subdividing their control net.
 		</i></p>
 		<p><i>
-			<sup>4</sup> Whether a Rhino Document (File3dm) is loaded or parsed, the returned object is an [page:Object3D Object3D] with all Rhino objects (File3dmObject) as children.
+			<sup>4</sup> Whether a Rhino Document (File3dm) is loaded or parsed, the returned object is an [page:Object3D Object3D] with all Rhino objects (File3dmObject) as children. File3dm layers and other file level properties are added to the resulting object's userData.
 		</i></p>
 		<p><i>
 			<sup>5</sup> All resulting three.js objects have useful properties from the Rhino object (i.e. layer index, name, etc.) populated in their userData object.
 		</i></p>
-
-		<h2>Code Example</h2>
-
-		<code>
-		// Instantiate a loader
-		const loader = new Rhino3dmLoader();
-
-		// Specify path to a folder containing WASM/JS libraries or a CDN.
-		//loader.setLibraryPath( '/path_to_library/rhino3dm/' );
-		loader.setLibraryPath( 'https://cdn.jsdelivr.net/npm/[email protected]/' );
-
-		// Load a 3DM file
-		loader.load(
-			// resource URL
-			'model.3dm',
-			// called when the resource is loaded
-			function ( object ) {
-
-				scene.add( object );
-
-			},
-			// called as loading progresses
-			function ( xhr ) {
-
-				console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
-
-			},
-			// called when loading has errors
-			function ( error ) {
-
-				console.log( 'An error happened' );
-
-			}
-		);
-		</code>
+		<p><i>
+			<sup>6</sup> Rhino and Three.js have a different coordinate system. Upon import, you should rotate the resulting [page:Object3D Object3D] by -90º in x or set the THREE.Object3D.DEFAULT_UP at the beginning of your application: 
+			<code>THREE.Object3D.DEFAULT_UP.set( 0, 0, 1 );</code>
+			Keep in mind that this will affect the orientation of all of the Object3Ds in your application.
+		</i></p>
 
 		<h2>Examples</h2>
 
@@ -172,7 +148,7 @@
 		<h2>Methods</h2>
 		<p>See the base [page:Loader] class for common methods.</p>
 
-		<h3>[method:undefined load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
+		<h3>[method:Object3D load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
 		<p>
 		[page:String url] — A string containing the path/URL of the `.3dm` file.<br />
 		[page:Function onLoad] — A function to be called after the loading is successfully completed.<br />
@@ -183,7 +159,41 @@
 		Begin loading from url and call the `onLoad` function with the resulting Object3d.
 		</p>
 
-		<h3>[method:undefined parse]( [param:ArrayBuffer buffer], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
+		<code>
+			// Instantiate a loader
+			const loader = new Rhino3dmLoader();
+	
+			// Specify path to a folder containing WASM/JS libraries or a CDN.
+			// For example, /jsm/libs/rhino3dm/ is the location of the library inside the three.js repository
+			// loader.setLibraryPath( '/path_to_library/rhino3dm/' );
+			loader.setLibraryPath( 'https://unpkg.com/[email protected]/' );
+	
+			// Load a 3DM file
+			loader.load(
+				// resource URL
+				'model.3dm',
+				// called when the resource is loaded
+				function ( object ) {
+	
+					scene.add( object );
+	
+				},
+				// called as loading progresses
+				function ( xhr ) {
+	
+					console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
+	
+				},
+				// called when loading has errors
+				function ( error ) {
+	
+					console.log( 'An error happened' );
+	
+				}
+			);
+			</code>
+
+		<h3>[method:Object3D parse]( [param:ArrayBuffer buffer], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
 		<p>
 		[page:ArrayBuffer buffer] — An ArrayBuffer representing the Rhino `File3dm` document.<br />
 		[page:Function onLoad] — A function to be called after the loading is successfully completed.<br />
@@ -195,35 +205,31 @@
 		</p>
 
 		<code>
-		import rhino3dm from 'https://cdn.jsdelivr.net/npm/[email protected]/rhino3dm.module.js'
+		import rhino3dm from 'https://unpkg.com/[email protected]/'
 
 		// Instantiate a loader
 		const loader = new Rhino3dmLoader();
 
 		// Specify path to a folder containing WASM/JS libraries or a CDN.
-		loader.setLibraryPath( 'https://cdn.jsdelivr.net/npm/[email protected]/' );
-
-		rhino3dm().then(async m => {
-
-			console.log('Loaded rhino3dm.');
-			const rhino = m; // global
+		loader.setLibraryPath( 'https://unpkg.com/[email protected]/' );
 
-			// create Rhino Document and add a point to it
-			const doc = new rhino.File3dm();
-			const ptA = [0, 0, 0];
-			const point = new rhino.Point( ptA );
-			doc.objects().add( point, null );
+		const rhino = await rhino3dm();
+		console.log('Loaded rhino3dm.');
 
-			// create a copy of the doc.toByteArray data to get an ArrayBuffer
-			const buffer = new Uint8Array( doc.toByteArray() ).buffer;
+		// create Rhino Document and add a point to it
+		const doc = new rhino.File3dm();
+		const ptA = [0, 0, 0];
+		const point = new rhino.Point( ptA );
+		doc.objects().add( point, null );
 
-			loader.parse( buffer, function ( object ) {
+		// create a copy of the doc.toByteArray data to get an ArrayBuffer
+		const buffer = new Uint8Array( doc.toByteArray() ).buffer;
 
-				scene.add( object );
+		loader.parse( buffer, function ( object ) {
 
-			} );
+			scene.add( object );
 
-		})
+		} );
 
 		</code>
 
@@ -238,7 +244,7 @@
 		// Specify path to a folder containing the WASM/JS library:
 		loader.setLibraryPath( '/path_to_library/rhino3dm/' );
 		// or from a CDN:
-		loader.setLibraryPath( 'https://cdn.jsdelivr.net/npm/[email protected]/' );
+		loader.setLibraryPath( 'https://unpkg.com/[email protected]/' );
 		</code>
 
 		<h3>[method:this setWorkerLimit]( [param:Number workerLimit] )</h3>

+ 6 - 0
editor/js/Loader.js

@@ -93,8 +93,14 @@ function Loader( editor ) {
 					loader.setLibraryPath( '../examples/jsm/libs/rhino3dm/' );
 					loader.parse( contents, function ( object ) {
 
+						object.name = filename;
+
 						editor.execute( new AddObjectCommand( editor, object ) );
 
+					}, function ( error ) {
+
+						console.error( error )
+
 					} );
 
 				}, false );

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 2048 - 3
examples/jsm/libs/rhino3dm/rhino3dm.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 2059 - 3
examples/jsm/libs/rhino3dm/rhino3dm.module.js


BIN
examples/jsm/libs/rhino3dm/rhino3dm.wasm


+ 378 - 104
examples/jsm/loaders/3DMLoader.js

@@ -4,6 +4,7 @@ import {
 	Loader,
 	Object3D,
 	MeshStandardMaterial,
+	MeshPhysicalMaterial,
 	Mesh,
 	Color,
 	Points,
@@ -25,6 +26,8 @@ import {
 	DoubleSide
 } from 'three';
 
+import { EXRLoader } from '../loaders/EXRLoader.js';
+
 const _taskCache = new WeakMap();
 
 class Rhino3dmLoader extends Loader {
@@ -217,7 +220,7 @@ class Rhino3dmLoader extends Loader {
 
 	}
 
-	_createMaterial( material ) {
+	_createMaterial( material, renderEnvironment ) {
 
 		if ( material === undefined ) {
 
@@ -230,27 +233,51 @@ class Rhino3dmLoader extends Loader {
 
 		}
 
-		const _diffuseColor = material.diffuseColor;
+		//console.log(material)
+
+		let mat = new MeshPhysicalMaterial( {
+
+			color: new Color( material.diffuseColor.r / 255.0, material.diffuseColor.g / 255.0, material.diffuseColor.b / 255.0 ),
+			emissive: new Color( material.emissionColor.r, material.emissionColor.g, material.emissionColor.b ),
+			flatShading: material.disableLighting,
+			ior: material.indexOfRefraction,
+			name: material.name,
+			reflectivity: material.reflectivity,
+			opacity: 1.0 - material.transparency,
+			side: DoubleSide,
+			specularColor: material.specularColor,
+			transparent: material.transparency > 0 ? true : false
+
+		} );
+
+		mat.userData.id = material.id;
 
-		const diffusecolor = new Color( _diffuseColor.r / 255.0, _diffuseColor.g / 255.0, _diffuseColor.b / 255.0 );
+		if ( material.pbrSupported ) {
 
-		if ( _diffuseColor.r === 0 && _diffuseColor.g === 0 && _diffuseColor.b === 0 ) {
+			const pbr = material.pbr;
 
-			diffusecolor.r = 1;
-			diffusecolor.g = 1;
-			diffusecolor.b = 1;
+			mat.anisotropy = pbr.anisotropy;
+			mat.anisotropyRotation = pbr.anisotropicRotation;
+			mat.color = new Color( pbr.baseColor.r, pbr.baseColor.g, pbr.baseColor.b );
+			mat.clearCoat = pbr.clearCoat;
+			mat.clearCoatRoughness = pbr.clearCoatRoughness;
+			mat.metalness = pbr.metallic;
+			mat.transmission = 1 - pbr.opacity;
+			mat.roughness = pbr.roughness;
+			mat.sheen = pbr.sheen;
+			mat.specularIntensity = pbr.specular;
+			mat.thickness = pbr.subsurface;
 
 		}
 
-		// console.log( material );
+		if ( material.pbrSupported && material.pbr.opacity === 0 && material.transparency === 1 ) {
 
-		const mat = new MeshStandardMaterial( {
-			color: diffusecolor,
-			name: material.name,
-			side: DoubleSide,
-			transparent: material.transparency > 0 ? true : false,
-			opacity: 1.0 - material.transparency
-		} );
+			//some compromises
+
+			mat.opacity = 0.2;
+			mat.transmission = 1.00;
+
+		}
 
 		const textureLoader = new TextureLoader();
 
@@ -262,17 +289,31 @@ class Rhino3dmLoader extends Loader {
 
 				const map = textureLoader.load( texture.image );
 
+				//console.log(texture.type )
+
 				switch ( texture.type ) {
 
+					case 'Bump':
+
+						mat.bumpMap = map;
+
+						break;
+
 					case 'Diffuse':
 
 						mat.map = map;
 
 						break;
 
-					case 'Bump':
+					case 'Emap':
 
-						mat.bumpMap = map;
+						mat.envMap = map;
+
+						break;
+					
+					case 'Opacity':
+
+						mat.transmissionMap = map;
 
 						break;
 
@@ -283,9 +324,97 @@ class Rhino3dmLoader extends Loader {
 
 						break;
 
-					case 'Emap':
+					case 'PBR_Alpha':
 
-						mat.envMap = map;
+						mat.alphaMap = map;
+						mat.transparent = true;
+
+						break;
+					
+					case 'PBR_AmbientOcclusion':
+
+						mat.aoMap = map;
+
+						break;
+
+					case 'PBR_Anisotropic':
+
+						mat.anisotropyMap = map;
+
+						break;
+
+					case 'PBR_BaseColor':
+
+						mat.map = map;
+
+						break;
+
+					case 'PBR_Clearcoat':
+
+						mat.clearcoatMap = map;
+
+						break;
+
+					case 'PBR_ClearcoatBump':
+
+						mat.clearcoatNormalMap = map;
+
+						break;
+
+					case 'PBR_ClearcoatRoughness':
+
+						mat.clearcoatRoughnessMap = map;
+
+						break;
+
+					case 'PBR_Displacement':
+
+						mat.displacementMap = map;
+
+						break;
+
+					case 'PBR_Emission':
+
+						mat.emissiveMap = map;
+
+						break;
+
+					case 'PBR_Metallic':
+
+						mat.metalnessMap = map;
+
+						break;
+
+					case 'PBR_Roughness':
+
+						mat.roughnessMap = map;
+
+						break;
+
+					case 'PBR_Sheen':
+
+						mat.sheenColorMap = map;
+
+						break;
+
+					case 'PBR_Specular':
+
+						mat.specularColorMap = map;
+
+						break;
+
+					case 'PBR_Subsurface':
+
+						mat.thicknessMap = map;
+
+						break;
+
+					default:
+
+						this.warnings.push( {
+							message: `THREE.3DMLoader: No conversion exists for 3dm ${texture.type}.`,
+							type: 'no conversion'
+						} );
 
 						break;
 
@@ -293,20 +422,34 @@ class Rhino3dmLoader extends Loader {
 
 				map.wrapS = texture.wrapU === 0 ? RepeatWrapping : ClampToEdgeWrapping;
 				map.wrapT = texture.wrapV === 0 ? RepeatWrapping : ClampToEdgeWrapping;
-				map.repeat.set( texture.repeat[ 0 ], texture.repeat[ 1 ] );
+
+				if ( texture.repeat ) {
+
+					map.repeat.set( texture.repeat[ 0 ], texture.repeat[ 1 ] );
+
+				}
 
 			}
 
 		}
 
+		if ( renderEnvironment ) {
+
+			new EXRLoader().load( renderEnvironment.image, function ( texture ) {
+
+				texture.mapping = THREE.EquirectangularReflectionMapping;
+				mat.envMap = texture;
+
+			} );
+
+		}
+
 		return mat;
 
 	}
 
 	_createGeometry( data ) {
 
-		// console.log(data);
-
 		const object = new Object3D();
 		const instanceDefinitionObjects = [];
 		const instanceDefinitions = [];
@@ -315,8 +458,10 @@ class Rhino3dmLoader extends Loader {
 		object.userData[ 'layers' ] = data.layers;
 		object.userData[ 'groups' ] = data.groups;
 		object.userData[ 'settings' ] = data.settings;
+		object.userData.settings[ 'renderSettings' ] = data.renderSettings;
 		object.userData[ 'objectType' ] = 'File3dm';
 		object.userData[ 'materials' ] = null;
+		
 		object.name = this.url;
 
 		let objects = data.objects;
@@ -343,22 +488,56 @@ class Rhino3dmLoader extends Loader {
 
 				default:
 
-					let _object;
+					let matId;
+
+					switch( attributes.materialSource.name ) {
+						case 'ObjectMaterialSource_MaterialFromLayer':
+							//check layer index
+							if ( attributes.layerIndex >= 0 ) {
+
+								matId = data.layers[ attributes.layerIndex ].renderMaterialIndex;
+
+							} else {
+
+								matId = null;
+
+							}
+
+							break;
+
+						case 'ObjectMaterialSource_MaterialFromObject':
 
-					if ( attributes.materialIndex >= 0 ) {
+							if ( attributes.materialIndex >= 0 ) {
+
+								matId = attributes.materialIndex;
+
+							} else {
+
+								matId = null;
+
+							}
+
+							break;
+
+					}
+
+					let material;
+
+					if ( matId >= 0 ) {
+
+						const rMaterial = materials[ matId ];
+						material = this._createMaterial( rMaterial, data.renderEnvironment );
 
-						const rMaterial = materials[ attributes.materialIndex ];
-						let material = this._createMaterial( rMaterial );
-						material = this._compareMaterials( material );
-						_object = this._createObject( obj, material );
 
 					} else {
 
-						const material = this._createMaterial();
-						_object = this._createObject( obj, material );
+						material = this._createMaterial();
 
 					}
 
+					material = this._compareMaterials( material );
+					const _object = this._createObject( obj, material );
+
 					if ( _object === undefined ) {
 
 						continue;
@@ -440,6 +619,7 @@ class Rhino3dmLoader extends Loader {
 		}
 
 		object.userData[ 'materials' ] = this.materials;
+		object.name = '';
 		return object;
 
 	}
@@ -642,7 +822,7 @@ class Rhino3dmLoader extends Loader {
 						break;
 
 					case 'LightStyle_WorldLinear':
-						// not conversion exists, warning has already been printed to the console
+						// no conversion exists, warning has already been printed to the console
 						break;
 
 					default:
@@ -821,7 +1001,6 @@ function Rhino3dmWorker() {
 
 			case 'init':
 
-				// console.log(message)
 				libraryConfig = message.libraryConfig;
 				const wasmBinary = libraryConfig.wasmBinary;
 				let RhinoModule;
@@ -902,7 +1081,7 @@ function Rhino3dmWorker() {
 		// Handle instance definitions
 		// console.log( `Instance Definitions Count: ${doc.instanceDefinitions().count()}` );
 
-		for ( let i = 0; i < doc.instanceDefinitions().count(); i ++ ) {
+		for ( let i = 0; i < doc.instanceDefinitions().count; i ++ ) {
 
 			const idef = doc.instanceDefinitions().get( i );
 			const idefAttributes = extractProperties( idef );
@@ -946,94 +1125,36 @@ function Rhino3dmWorker() {
 			rhino.TextureType.PBR_Displacement
 		];
 
-		for ( let i = 0; i < doc.materials().count(); i ++ ) {
+		for ( let i = 0; i < doc.materials().count; i ++ ) {
 
 			const _material = doc.materials().get( i );
-			const _pbrMaterial = _material.physicallyBased();
 
 			let material = extractProperties( _material );
 
 			const textures = [];
 
-			for ( let j = 0; j < textureTypes.length; j ++ ) {
-
-				const _texture = _material.getTexture( textureTypes[ j ] );
-				if ( _texture ) {
-
-					let textureType = textureTypes[ j ].constructor.name;
-					textureType = textureType.substring( 12, textureType.length );
-					const texture = { type: textureType };
+			textures.push( ...extractTextures( _material, textureTypes, doc ) );
 
-					const image = doc.getEmbeddedFileAsBase64( _texture.fileName );
+			material.pbrSupported = _material.physicallyBased().supported;
 
-					texture.wrapU = _texture.wrapU;
-					texture.wrapV = _texture.wrapV;
-					texture.wrapW = _texture.wrapW;
-					const uvw = _texture.uvwTransform.toFloatArray( true );
-					texture.repeat = [ uvw[ 0 ], uvw[ 5 ] ];
+			if ( material.pbrSupported ) {
 
-					if ( image ) {
-
-						texture.image = 'data:image/png;base64,' + image;
-
-					} else {
-
-						self.postMessage( { type: 'warning', id: taskID, data: {
-							message: `THREE.3DMLoader: Image for ${textureType} texture not embedded in file.`,
-							type: 'missing resource'
-						}
-
-						} );
-
-						texture.image = null;
-
-					}
-
-					textures.push( texture );
-
-					_texture.delete();
-
-				}
+				textures.push( ...extractTextures( _material, pbrTextureTypes, doc ) );
+				material.pbr = extractProperties( _material.physicallyBased() );
 
 			}
 
 			material.textures = textures;
 
-			if ( _pbrMaterial.supported ) {
-
-				for ( let j = 0; j < pbrTextureTypes.length; j ++ ) {
-
-					const _texture = _material.getTexture( pbrTextureTypes[ j ] );
-					if ( _texture ) {
-
-						const image = doc.getEmbeddedFileAsBase64( _texture.fileName );
-						let textureType = pbrTextureTypes[ j ].constructor.name;
-						textureType = textureType.substring( 12, textureType.length );
-						const texture = { type: textureType, image: 'data:image/png;base64,' + image };
-						textures.push( texture );
-
-						_texture.delete();
-
-					}
-
-				}
-
-				const pbMaterialProperties = extractProperties( _material.physicallyBased() );
-
-				material = Object.assign( pbMaterialProperties, material );
-
-			}
-
 			materials.push( material );
 
 			_material.delete();
-			_pbrMaterial.delete();
 
 		}
 
 		// Handle layers
 
-		for ( let i = 0; i < doc.layers().count(); i ++ ) {
+		for ( let i = 0; i < doc.layers().count; i ++ ) {
 
 			const _layer = doc.layers().get( i );
 			const layer = extractProperties( _layer );
@@ -1046,7 +1167,7 @@ function Rhino3dmWorker() {
 
 		// Handle views
 
-		for ( let i = 0; i < doc.views().count(); i ++ ) {
+		for ( let i = 0; i < doc.views().count; i ++ ) {
 
 			const _view = doc.views().get( i );
 			const view = extractProperties( _view );
@@ -1059,7 +1180,7 @@ function Rhino3dmWorker() {
 
 		// Handle named views
 
-		for ( let i = 0; i < doc.namedViews().count(); i ++ ) {
+		for ( let i = 0; i < doc.namedViews().count; i ++ ) {
 
 			const _namedView = doc.namedViews().get( i );
 			const namedView = extractProperties( _namedView );
@@ -1072,7 +1193,7 @@ function Rhino3dmWorker() {
 
 		// Handle groups
 
-		for ( let i = 0; i < doc.groups().count(); i ++ ) {
+		for ( let i = 0; i < doc.groups().count; i ++ ) {
 
 			const _group = doc.groups().get( i );
 			const group = extractProperties( _group );
@@ -1098,9 +1219,9 @@ function Rhino3dmWorker() {
 		// Handle strings
 		// console.log( `Document Strings Count: ${doc.strings().count()}` );
 		// Note: doc.strings().documentUserTextCount() counts any doc.strings defined in a section
-		//console.log( `Document User Text Count: ${doc.strings().documentUserTextCount()}` );
+		// console.log( `Document User Text Count: ${doc.strings().documentUserTextCount()}` );
 
-		const strings_count = doc.strings().count();
+		const strings_count = doc.strings().count;
 
 		for ( let i = 0; i < strings_count; i ++ ) {
 
@@ -1108,9 +1229,146 @@ function Rhino3dmWorker() {
 
 		}
 
+		// Handle Render Environments for Material Environment
+
+		// get the id of the active render environment skylight, which we'll use for environment texture
+		const reflectionId = doc.settings().renderSettings().renderEnvironments.reflectionId
+
+		const rc = doc.renderContent()
+
+		let renderEnvironment = null
+
+		for( let i = 0; i < rc.count; i++ ) {
+
+			const content = rc.get(i)
+
+			switch( content.kind ) {
+
+				case 'environment':
+
+					const id = content.id
+
+					// there could be multiple render environments in a 3dm file
+					if ( id !== reflectionId ) break;
+
+					const renderTexture = content.findChild( 'texture' )
+					const fileName = renderTexture.fileName
+
+					for ( let j = 0; j < doc.embeddedFiles().count; j ++ ) {
+
+						const _fileName = doc.embeddedFiles().get( j ).fileName
+
+						if ( fileName === _fileName ) {
+
+							const background = doc.getEmbeddedFileAsBase64( fileName )
+							const backgroundImage = 'data:image/png;base64,' + background
+							renderEnvironment = { type: 'renderEnvironment', image: backgroundImage, name: fileName };
+
+						}
+
+					}
+					
+					break;
+
+			}
+
+		}
+
+		// Handle Render Settings
+
+		const renderSettings = {
+			ambientLight: doc.settings().renderSettings().ambientLight,
+			backgroundColorTop: doc.settings().renderSettings().backgroundColorTop,
+			backgroundColorBottom: doc.settings().renderSettings().backgroundColorBottom,
+			useHiddenLights: doc.settings().renderSettings().useHiddenLights,
+			depthCue: doc.settings().renderSettings().depthCue,
+			flatShade: doc.settings().renderSettings().flatShade,
+			renderBackFaces: doc.settings().renderSettings().renderBackFaces,
+			renderPoints: doc.settings().renderSettings().renderPoints,
+			renderCurves: doc.settings().renderSettings().renderCurves,
+			renderIsoParams: doc.settings().renderSettings().renderIsoParams,
+			renderMeshEdges: doc.settings().renderSettings().renderMeshEdges,
+			renderAnnotations: doc.settings().renderSettings().renderAnnotations,
+			useViewportSize: doc.settings().renderSettings().useViewportSize,
+			scaleBackgroundToFit: doc.settings().renderSettings().scaleBackgroundToFit,
+			transparentBackground: doc.settings().renderSettings().transparentBackground,
+			imageDpi: doc.settings().renderSettings().imageDpi,
+			shadowMapLevel: doc.settings().renderSettings().shadowMapLevel,
+			namedView: doc.settings().renderSettings().namedView,
+			snapShot: doc.settings().renderSettings().snapShot,
+			specificViewport: doc.settings().renderSettings().specificViewport,
+			groundPlane: extractProperties( doc.settings().renderSettings().groundPlane ),
+			safeFrame: extractProperties( doc.settings().renderSettings().safeFrame ),
+			dithering: extractProperties( doc.settings().renderSettings().dithering ),
+			skylight: extractProperties( doc.settings().renderSettings().skylight ),
+			linearWorkflow: extractProperties( doc.settings().renderSettings().linearWorkflow ),
+			renderChannels: extractProperties( doc.settings().renderSettings().renderChannels ),
+			sun: extractProperties( doc.settings().renderSettings().sun ),
+			renderEnvironments: extractProperties( doc.settings().renderSettings().renderEnvironments ),
+			postEffects: extractProperties( doc.settings().renderSettings().postEffects ),
+
+		}
+
 		doc.delete();
 
-		return { objects, materials, layers, views, namedViews, groups, strings, settings };
+		return { objects, materials, layers, views, namedViews, groups, strings, settings, renderSettings, renderEnvironment };
+
+	}
+
+	function extractTextures( m, tTypes, d ) {
+
+		const textures = []
+
+		for ( let i = 0; i < tTypes.length; i ++ ) {
+
+			const _texture = m.getTexture( tTypes[ i ] );
+			if ( _texture ) {
+
+				let textureType = tTypes[ i ].constructor.name;
+				textureType = textureType.substring( 12, textureType.length );
+				const texture = extractTextureData( _texture, textureType, d );
+				textures.push( texture );
+				_texture.delete();
+
+			}
+
+		}
+
+		return textures;
+
+	}
+
+	function extractTextureData( t, tType, d ) {
+
+		const texture = { type: tType };
+
+		const image = d.getEmbeddedFileAsBase64( t.fileName );
+
+		texture.wrapU = t.wrapU;
+		texture.wrapV = t.wrapV;
+		texture.wrapW = t.wrapW;
+		const uvw = t.uvwTransform.toFloatArray( true );
+
+		texture.repeat = [ uvw[ 0 ], uvw[ 5 ] ];
+
+		if ( image ) {
+
+			texture.image = 'data:image/png;base64,' + image;
+
+		} else {
+
+			self.postMessage( { type: 'warning', id: taskID, data: {
+				message: `THREE.3DMLoader: Image for ${tType} texture not embedded in file.`,
+				type: 'missing resource'
+			}
+
+			} );
+
+			texture.image = null;
+
+		}
+
+		return texture;
 
 	}
 
@@ -1323,6 +1581,18 @@ function Rhino3dmWorker() {
 
 			}
 
+			if ( _attributes.decals().count > 0 ) {
+
+				self.postMessage( { type: 'warning', id: taskID, data: {
+					message: `THREE.3DMLoader: No conversion exists for the decals associated with this object.`,
+					type: 'no conversion',
+					guid: _attributes.id
+				}
+	
+				} );
+				
+			}
+
 			attributes.drawColor = _attributes.drawColor( doc );
 
 			objectType = objectType.constructor.name;
@@ -1358,6 +1628,10 @@ function Rhino3dmWorker() {
 
 					result[ property ] = { name: value.constructor.name, value: value.value };
 
+				} else if ( typeof value === 'object' && value !== null ) {
+
+					result[ property ] = extractProperties( value );
+
 				} else {
 
 					result[ property ] = value;

+ 2 - 1
examples/webgl_loader_3dm.html

@@ -79,7 +79,8 @@
 				scene.add( directionalLight );
 
 				const loader = new Rhino3dmLoader();
-				loader.setLibraryPath( 'https://cdn.jsdelivr.net/npm/[email protected]/' );
+				//generally, use this for the Library Path: https://cdn.jsdelivr.net/npm/[email protected]/
+				loader.setLibraryPath( 'jsm/libs/rhino3dm/' );
 				loader.load( 'models/3dm/Rhino_Logo.3dm', function ( object ) {
 
 					scene.add( object );

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.