Browse Source

Merge pull request #20300 from mcneel/wip/3DMLoader

3DMLoader: Updates
Mr.doob 4 years ago
parent
commit
3899751cfe

+ 177 - 0
docs/examples/en/loaders/3DMLoader.html

@@ -0,0 +1,177 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:Loader] &rarr;
+		<h1>[name]</h1>
+
+		<p class="desc">
+			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, solids, 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].
+
+		</p>
+
+		<h2>Supported Conversions</h2>
+
+		<p>
+			[name] loads the following objects to a respective three.js type:
+		</p>
+
+		<table>
+			<tr>
+				<th>3dm type</th>
+				<th>three.js type</th>
+			</tr>
+			<tr>
+				<td>Point</td>
+				<td>[page:Points Points]</td>
+			</tr>
+			<tr>
+				<td>PointSet</td>
+				<td>[page:Points Points]</td>
+			</tr>
+			<tr>
+				<td>TextDot</td>
+				<td>[page:Sprite Sprite]</td>
+			</tr>
+			<tr>
+				<td>Curve</td>
+				<td>[page:Line Line]</td>
+			</tr>
+			<tr>
+				<td>Mesh</td>
+				<td>[page:Mesh Mesh]</td>
+			</tr>
+			<tr>
+				<td>Extrusion</td>
+				<td>[page:Mesh Mesh]</td>
+			</tr>
+			<tr>
+				<td>BREP</td>
+				<td>[page:Object3D Object3D]</td>
+			</tr>
+			<tr>
+				<td>InstanceReferences</td>
+				<td>[page:Object3D Object3D]</td>
+			</tr>
+			<tr>
+				<td>DirectionalLight</td>
+				<td>[page:DirectionalLight DirectionalLight]</td>
+			</tr>
+			<tr>
+				<td>PointLight</td>
+				<td>[page:PointLight PointLight]</td>
+			</tr>
+			<tr>
+				<td>RectangularLight</td>
+				<td>[page:RectAreaLight RectAreaLight]</td>
+			</tr>
+			<tr>
+				<td>SpotLight</td>
+				<td>[page:SpotLight SpotLight]</td>
+			</tr>
+		</table>
+
+		<h2>Code Example</h2>
+
+		<code>
+		// Instantiate a loader
+		var loader = new Rhino3dmLoader();
+
+		// Specify path to a folder containing WASM/JS libraries.
+		loader.setLibraryPath( '/examples/jsm/libs/rhino3dm/' );
+
+		// 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>
+
+		<h2>Examples</h2>
+
+		<p>
+			[example:webgl_loader_3dm]
+		</p>
+
+		<hr>
+
+		<h2>Constructor</h2>
+
+		<h3>Rhino3dmLoader( [param:LoadingManager manager] )</h3>
+		<p>
+		[page:LoadingManager manager] — The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].
+		</p>
+		<p>
+		Creates a new Rhino3dmLoader.
+		</p>
+
+		<h2>Properties</h2>
+		<p>See the base [page:Loader] class for common properties.</p>
+
+		<h2>Methods</h2>
+		<p>See the base [page:Loader] class for common methods.</p>
+
+		<h3>[method:null 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 <em>.3dm</em> file.<br />
+		[page:Function onLoad] — A function to be called after the loading is successfully completed.<br />
+		[page:Function onProgress] — (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, that contains .[page:Integer total] and .[page:Integer loaded] bytes.<br />
+		[page:Function onError] — (optional) A function to be called if an error occurs during loading. The function receives error as an argument.<br />
+		</p>
+		<p>
+		Begin loading from url and call the <em>onLoad</em> function with the geometry.
+		</p>
+
+		<h3>[method:this setLibraryPath]( [param:String value] )</h3>
+		<p>
+		[page:String value] — Path to folder containing the JS and WASM libraries.
+		</p>
+
+		<h3>[method:this setWorkerLimit]( [param:Number workerLimit] )</h3>
+		<p>
+			[page:Number workerLimit] - Maximum number of workers to be allocated. Default is 4.<br />
+		</p>
+		<p>
+		Sets the maximum number of [link:https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers Web Workers]
+		to be used during decoding. A lower limit may be preferable if workers are also for other tasks
+		in the application.
+		</p>
+
+		<h3>[method:this dispose]()</h3>
+		<p>
+		Disposes of the loader resources and deallocates memory.
+		</p>
+
+		<h2>Source</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/3DMLoader.js examples/jsm/loaders/3DMLoader.js]
+		</p>
+	</body>
+</html>

+ 1 - 0
docs/list.js

@@ -389,6 +389,7 @@ var list = {
 			},
 
 			"Loaders": {
+				"3DMLoader": "examples/en/loaders/3DMLoader",
 				"BasisTextureLoader": "examples/en/loaders/BasisTextureLoader",
 				"DRACOLoader": "examples/en/loaders/DRACOLoader",
 				"GLTFLoader": "examples/en/loaders/GLTFLoader",

+ 1 - 0
examples/jsm/loaders/3DMLoader.d.ts

@@ -9,6 +9,7 @@ export class Rhino3dmLoader extends Loader {
 	constructor( manager?: LoadingManager );
 
 	load( url: string, onLoad: ( object: Object3D ) => void, onProgress?: ( event: ProgressEvent ) => void, onError?: ( event: ErrorEvent ) => void ): void;
+	parse( data: ArrayBufferLike, onLoad: ( object: Object3D ) => void, onError?: ( event: ErrorEvent ) => void ): void;
 	setLibraryPath( path: string ): Rhino3dmLoader;
 	setWorkerLimit( workerLimit: number ): Rhino3dmLoader;
 	dispose(): Rhino3dmLoader;

+ 213 - 22
examples/jsm/loaders/3DMLoader.js

@@ -20,7 +20,8 @@ import {
 	SpriteMaterial,
 	CanvasTexture,
 	LinearFilter,
-	ClampToEdgeWrapping
+	ClampToEdgeWrapping,
+	TextureLoader
 } from "../../../build/three.module.js";
 
 var Rhino3dmLoader = function ( manager ) {
@@ -221,13 +222,61 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 		}
 
-		return new MeshStandardMaterial( {
+		// console.log( material );
+
+		var mat = new MeshStandardMaterial( {
 			color: diffusecolor,
-			metalness: 0.8,
 			name: material.name,
-			side: 2
+			side: 2,
+			transparent: material.transparency > 0 ? true : false,
+			opacity: 1.0 - material.transparency
 		} );
 
+		var textureLoader = new TextureLoader();
+
+		for ( var i = 0; i < material.textures.length; i ++ ) {
+
+			var texture = material.textures[ i ];
+
+			if ( texture.image !== null ) {
+
+				var map = textureLoader.load( texture.image );
+
+				switch ( texture.type ) {
+
+					case 'Diffuse':
+
+						mat.map = map;
+
+						break;
+
+					case 'Bump':
+
+						mat.bumpMap = map;
+
+						break;
+
+					case 'Transparency':
+
+						mat.alphaMap = map;
+						mat.transparent = true;
+
+						break;
+
+					case 'Emap':
+
+						mat.envMap = map;
+
+						break;
+
+				}
+
+			}
+
+		}
+
+		return mat;
+
 	},
 
 	_createGeometry: function ( data ) {
@@ -241,6 +290,7 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 		object.userData[ 'layers' ] = data.layers;
 		object.userData[ 'groups' ] = data.groups;
+		object.userData[ 'settings' ] = data.settings;
 
 		var objects = data.objects;
 		var materials = data.materials;
@@ -266,11 +316,18 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 				default:
 
-					var material = this._createMaterial( materials[ attributes.materialIndex ] );
+					if ( attributes.hasOwnProperty( 'materialUUID' ) ) {
+
+						var rMaterial = materials.find( m => m.id === attributes.materialUUID );
+						var material = this._createMaterial( rMaterial );
+						material = this._compareMaterials( material );
+						var _object = this._createObject( obj, material );
 
-					material = this._compareMaterials( material );
+					} else {
+
+						var _object = this._createObject( obj, null );
 
-					var _object = this._createObject( obj, material );
+					}
 
 					if ( _object === undefined ) {
 
@@ -278,7 +335,9 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 					}
 
-					_object.visible = data.layers[ attributes.layerIndex ].visible;
+					var layer = data.layers[ attributes.layerIndex ];
+
+					_object.visible = layer ? data.layers[ attributes.layerIndex ].visible : true;
 
 					if ( attributes.isInstanceDefinitionObject ) {
 
@@ -368,7 +427,15 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 			case 'PointSet':
 
 				var geometry = loader.parse( obj.geometry );
-				var material = new PointsMaterial( { sizeAttenuation: true, vertexColors: true } );
+				var _color = attributes.drawColor;
+				var color = new Color( _color.r / 255.0, _color.g / 255.0, _color.b / 255.0 );
+				var material = new PointsMaterial( { color: color, sizeAttenuation: false, size: 2 } );
+
+				if ( geometry.attributes.hasOwnProperty( 'color' ) ) {
+
+					material.vertexColors = true;
+
+				}
 
 				material = this._compareMaterials( material );
 
@@ -382,6 +449,12 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 				var geometry = loader.parse( obj.geometry );
 
+				if ( geometry.attributes.hasOwnProperty( 'color' ) ) {
+
+					mat.vertexColors = true;
+
+				}
+
 				var mesh = new Mesh( geometry, mat );
 				mesh.castShadow = attributes.castsShadows;
 				mesh.receiveShadow = attributes.receivesShadows;
@@ -735,6 +808,13 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 
 			if ( object !== undefined ) {
 
+				if ( object.attributes.materialIndex >= 0 ) {
+
+					var mId = doc.materials().findIndex( object.attributes.materialIndex ).id;
+					object.attributes.materialUUID = mId;
+
+				}
+
 				objects.push( object );
 
 			}
@@ -744,6 +824,7 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 		}
 
 		// Handle instance definitions
+		// console.log( `Instance Definitions Count: ${doc.instanceDefinitions().count()}` );
 
 		for ( var i = 0; i < doc.instanceDefinitions().count(); i ++ ) {
 
@@ -757,17 +838,110 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 
 		// Handle materials
 
+		var textureTypes = [
+			// rhino.TextureType.Bitmap,
+			rhino.TextureType.Diffuse,
+			rhino.TextureType.Bump,
+			rhino.TextureType.Transparency,
+			rhino.TextureType.Opacity,
+			rhino.TextureType.Emap
+		];
+
+		var pbrTextureTypes = [
+			rhino.TextureType.PBR_BaseColor,
+			rhino.TextureType.PBR_Subsurface,
+			rhino.TextureType.PBR_SubsurfaceScattering,
+			rhino.TextureType.PBR_SubsurfaceScatteringRadius,
+			rhino.TextureType.PBR_Metallic,
+			rhino.TextureType.PBR_Specular,
+			rhino.TextureType.PBR_SpecularTint,
+			rhino.TextureType.PBR_Roughness,
+			rhino.TextureType.PBR_Anisotropic,
+			rhino.TextureType.PBR_Anisotropic_Rotation,
+			rhino.TextureType.PBR_Sheen,
+			rhino.TextureType.PBR_SheenTint,
+			rhino.TextureType.PBR_Clearcoat,
+			rhino.TextureType.PBR_ClearcoatBump,
+			rhino.TextureType.PBR_ClearcoatRoughness,
+			rhino.TextureType.PBR_OpacityIor,
+			rhino.TextureType.PBR_OpacityRoughness,
+			rhino.TextureType.PBR_Emission,
+			rhino.TextureType.PBR_AmbientOcclusion,
+			rhino.TextureType.PBR_Displacement
+		];
+
 		for ( var i = 0; i < doc.materials().count(); i ++ ) {
 
 			var _material = doc.materials().get( i );
-			var materialProperties = extractProperties( _material );
-			var pbMaterialProperties = extractProperties( _material.physicallyBased() );
+			var _pbrMaterial = _material.physicallyBased();
+
+			var material = extractProperties( _material );
 
-			var material = Object.assign( materialProperties, pbMaterialProperties );
+			var textures = [];
+
+			for ( var j = 0; j < textureTypes.length; j ++ ) {
+
+				var _texture = _material.getTexture( textureTypes[ j ] );
+				if ( _texture ) {
+
+					var textureType = textureTypes[ j ].constructor.name;
+					textureType = textureType.substring( 12, textureType.length );
+					var texture = { type: textureType };
+
+					var image = doc.getEmbeddedFileAsBase64( _texture.fileName );
+
+					if ( image ) {
+
+						texture.image = 'data:image/png;base64,' + image;
+
+					} else {
+
+						console.warn( `THREE.3DMLoader: Image for ${textureType} texture not embedded in file.` );
+						texture.image = null;
+
+					}
+
+					textures.push( texture );
+
+					_texture.delete();
+
+				}
+
+			}
+
+			material.textures = textures;
+
+			if ( _pbrMaterial.supported ) {
+
+				console.log( 'pbr true' );
+
+				for ( var j = 0; j < pbrTextureTypes.length; j ++ ) {
+
+					var _texture = _material.getTexture( textureTypes[ j ] );
+					if ( _texture ) {
+
+						var image = doc.getEmbeddedFileAsBase64( _texture.fileName );
+						var textureType = textureTypes[ j ].constructor.name;
+						textureType = textureType.substring( 12, textureType.length );
+						var texture = { type: textureType, image: 'data:image/png;base64,' + image };
+						textures.push( texture );
+
+						_texture.delete();
+
+					}
+
+				}
+
+				var pbMaterialProperties = extractProperties( _material.physicallyBased() );
+
+				material = Object.assign( pbMaterialProperties, material );
+
+			}
 
 			materials.push( material );
 
 			_material.delete();
+			_pbrMaterial.delete();
 
 		}
 
@@ -830,16 +1004,14 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 		//TODO: Handle other document stuff like dimstyles, instance definitions, bitmaps etc.
 
 		// Handle dimstyles
-		// console.log(`Dimstyle Count: ${doc.dimstyles().count()}`);
+		// console.log( `Dimstyle Count: ${doc.dimstyles().count()}` );
 
 		// Handle bitmaps
-		// console.log(`Bitmap Count: ${doc.bitmaps().count()}`);
-
-		// Handle instance definitions
-		// console.log(`Instance Definitions Count: ${doc.instanceDefinitions().count()}`);
+		// console.log( `Bitmap Count: ${doc.bitmaps().count()}` );
 
 		// Handle strings -- this seems to be broken at the moment in rhino3dm
-		// console.log(`Strings Count: ${doc.strings().count()}`);
+		// console.log( `Document Strings Count: ${doc.strings().count()}` );
+
 		/*
 		for( var i = 0; i < doc.strings().count(); i++ ){
 
@@ -880,7 +1052,6 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 				var pts = curveToPoints( _geometry, 100 );
 
 				var position = {};
-				var color = {};
 				var attributes = {};
 				var data = {};
 
@@ -1011,6 +1182,7 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 		if ( geometry ) {
 
 			var attributes = extractProperties( _attributes );
+			attributes.geometry = extractProperties( _geometry );
 
 			if ( _attributes.groupCount > 0 ) {
 
@@ -1018,10 +1190,17 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 
 			}
 
+			if ( _attributes.userStringCount > 0 ) {
+
+				attributes.userStrings = _attributes.getUserStrings();
+
+			}
+
 			attributes.drawColor = _attributes.drawColor( doc );
 
 			objectType = objectType.constructor.name;
 			objectType = objectType.substring( 11, objectType.length );
+			attributes.geometry.objectType = objectType;
 
 			return { geometry, attributes, objectType };
 
@@ -1041,7 +1220,7 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 
 			} else {
 
-				// console.log(`${property}: ${object[property]}`);
+				// console.log( `${property}: ${object[ property ]}` );
 
 			}
 
@@ -1083,7 +1262,7 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 			for ( var i = 0; i < segmentCount; i ++ ) {
 
 				var segment = curve.segmentCurve( i );
-				var segmentArray = curveToPoints( segment );
+				var segmentArray = curveToPoints( segment, pointCount );
 				rc = rc.concat( segmentArray );
 				segment.delete();
 
@@ -1093,9 +1272,21 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 
 		}
 
+		if ( curve instanceof rhino.ArcCurve ) {
+
+			pointCount = Math.floor( curve.angleDegrees / 5 );
+			pointCount = pointCount < 1 ? 2 : pointCount;
+			// alternative to this hardcoded version: https://stackoverflow.com/a/18499923/2179399
+
+		}
+
 		if ( curve instanceof rhino.NurbsCurve && curve.degree === 1 ) {
 
-			// console.info( 'degree 1 curve' );
+			if ( curve.segmentCount === undefined || curve.segmentCount === 1 ) {
+
+				return [ curve.pointAtStart, curve.pointAtEnd ];
+
+			}
 
 		}
 

BIN
examples/models/3dm/Rhino_Logo.3dm


BIN
examples/screenshots/webgl_loader_3dm.jpg


+ 0 - 2
examples/webgl_loader_3dm.html

@@ -39,8 +39,6 @@
 
 				scene = new THREE.Scene();
 
-				scene.add( new THREE.AmbientLight( { intensity: 0.0001 } ) );
-
 				var directionalLight = new THREE.DirectionalLight( 0xffffff );
 				directionalLight.position.set( 0, 0, 2 );
 				directionalLight.castShadow = true;