Forráskód Böngészése

Merge pull request #1 from fraguada/fraguada-Rhino3dmLoader

Fraguada rhino3dm loader
Luis E. Fraguada 5 éve
szülő
commit
6a6d2fcef1

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 8 - 0
examples/jsm/libs/rhino3dm/rhino3dm.js


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


+ 471 - 0
examples/jsm/loaders/3DMLoader.js

@@ -0,0 +1,471 @@
+/**
+ * @author Luis Fraguada / https://github.com/fraguada
+ */
+
+import {
+	BufferAttribute,
+	BufferGeometry,
+	BufferGeometryLoader,
+	FileLoader,
+	Loader,
+	Object3D,
+	MeshStandardMaterial,
+	Mesh,
+	Color
+} from "../../../build/three.module.js";
+
+var Rhino3dmLoader = function ( manager ) {
+
+    Loader.call( this, manager );
+
+	this.libraryPath = '';
+	this.libraryPending = null;
+	this.libraryBinary = null;
+	this.libraryConfig = {};
+
+	this.workerLimit = 4;
+	this.workerPool = [];
+	this.workerNextTaskID = 1;
+	this.workerSourceURL = '';
+	this.workerConfig = {};
+
+};
+
+Rhino3dmLoader.taskCache = new WeakMap();
+
+Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
+
+    constructor: Rhino3dmLoader,
+
+    setLibraryPath: function ( path ) {
+
+		this.libraryPath = path;
+
+		return this;
+
+	},
+	
+	setWorkerLimit: function ( workerLimit ) {
+
+		this.workerLimit = workerLimit;
+
+		return this;
+
+	},
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var loader = new FileLoader( this.manager );
+
+		loader.setPath( this.path );
+		loader.setResponseType( 'arraybuffer' );
+
+		loader.load( url, ( buffer ) => {
+
+			// Check for an existing task using this buffer. A transferred buffer cannot be transferred
+			// again from this thread.
+			if ( Rhino3dmLoader.taskCache.has( buffer ) ) {
+
+				var cachedTask = Rhino3dmLoader.taskCache.get( buffer );
+
+				return cachedTask.promise.then( onLoad ).catch( onError );
+
+			}
+
+			this.decodeObjects( buffer, url )
+				.then( onLoad )
+				.catch( onError );
+
+		}, onProgress, onError );
+
+
+	},
+
+	debug: function () {
+
+		console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) );
+
+	},
+
+	decodeObjects: function ( buffer, url ) {
+
+		var worker;
+		var taskID;
+
+		var taskCost = buffer.byteLength;
+
+		var objectPending = this._getWorker( taskCost )
+			.then( ( _worker ) => {
+
+				worker = _worker;
+				taskID = this.workerNextTaskID ++; //hmmm
+
+				return new Promise( ( resolve, reject ) => {
+
+					worker._callbacks[ taskID ] = { resolve, reject };
+
+					worker.postMessage( { type: 'decode', id: taskID, buffer }, [ buffer ] );
+
+					//this.debug();
+
+				} );
+
+			} )
+			.then( ( message ) => this._createGeometry( message.data ) );
+
+		// Remove task from the task list.
+		// Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
+		objectPending
+			.catch( () => true )
+			.then( () => {
+
+				if ( worker && taskID ) {
+
+					this._releaseTask( worker, taskID );
+
+					//this.debug();
+
+				}
+
+			} );
+
+		// Cache the task result.
+		Rhino3dmLoader.taskCache.set( buffer, {
+
+			url: url,
+			promise: objectPending
+
+		} );
+
+		return objectPending;
+	},
+
+	parse: function ( ) {
+
+		// parsing logic goes here
+		console.log('3dm parsing');
+
+	},
+
+	_createGeometry: function ( data ) {
+
+		// console.log(data);
+
+		var object = new Object3D();
+		let loader = new BufferGeometryLoader();
+
+		var objects = data.objects;
+		var materials = data.materials;
+
+		for( var i = 0; i < objects.length; i++ ){
+
+			var obj = objects[i];
+
+			var geometry = loader.parse( obj.geometry );
+			var attributes = obj.attributes;
+			var mat = materials[attributes.materialIndex];
+
+			// console.log(mat);
+
+			var material = new MeshStandardMaterial( { color: new Color(mat.diffuseColor.r, mat.diffuseColor.g, mat.diffuseColor.b) } );
+
+			var mesh = new Mesh(geometry, material);
+
+			object.add(mesh)
+
+		}
+		
+		return object;
+
+	},
+
+	_initLibrary: function () {
+
+		if ( ! this.libraryPending ) {
+
+			// Load rhino3dm wrapper.
+			var jsLoader = new FileLoader( this.manager );
+			jsLoader.setPath( this.libraryPath );
+			var jsContent = new Promise( ( resolve, reject ) => {
+
+				jsLoader.load( 'rhino3dm.js', resolve, undefined, reject );
+
+			} );
+
+			// Load rhino3dm WASM binary.
+			var binaryLoader = new FileLoader( this.manager );
+			binaryLoader.setPath( this.libraryPath );
+			binaryLoader.setResponseType( 'arraybuffer' );
+			var binaryContent = new Promise( ( resolve, reject ) => {
+
+				binaryLoader.load( 'rhino3dm.wasm', resolve, undefined, reject );
+
+			} );
+
+			this.libraryPending = Promise.all( [ jsContent, binaryContent ] )
+				.then( ( [ jsContent, binaryContent ] ) => {
+
+					//this.libraryBinary = binaryContent;
+					this.libraryConfig.wasmBinary = binaryContent;
+
+					var fn = Rhino3dmLoader.Rhino3dmWorker.toString();
+
+					var body = [
+						'/* rhino3dm.js */',
+						jsContent,
+						'/* worker */',
+						fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
+					].join( '\n' );
+
+					this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
+
+				} );
+
+		}
+
+		return this.libraryPending;
+
+	},
+
+	_getWorker: function ( taskCost ) {
+
+		return this._initLibrary().then( () => {
+
+			if ( this.workerPool.length < this.workerLimit ) {
+
+				var worker = new Worker( this.workerSourceURL );
+
+				worker._callbacks = {};
+				worker._taskCosts = {};
+				worker._taskLoad = 0;
+
+				worker.postMessage( {
+					type: 'init',
+					libraryConfig: this.libraryConfig
+				} );
+
+				worker.onmessage = function ( e ) {
+
+					var message = e.data;
+
+					switch ( message.type ) {
+
+						case 'decode':
+							worker._callbacks[ message.id ].resolve( message );
+							break;
+
+						case 'error':
+							worker._callbacks[ message.id ].reject( message );
+							break;
+
+						default:
+							console.error( 'THREE.Rhino3dmLoader: Unexpected message, "' + message.type + '"' );
+
+					}
+
+				};
+
+				this.workerPool.push( worker );
+
+			} else {
+
+				this.workerPool.sort( function ( a, b ) {
+
+					return a._taskLoad > b._taskLoad ? - 1 : 1;
+
+				} );
+
+			}
+
+			var worker = this.workerPool[ this.workerPool.length - 1 ];
+
+			worker._taskLoad += taskCost;
+
+			return worker;
+
+		} );
+	},
+
+	_releaseTask: function ( worker, taskID ) {
+
+		worker._taskLoad -= worker._taskCosts[ taskID ];
+		delete worker._callbacks[ taskID ];
+		delete worker._taskCosts[ taskID ];
+
+	},
+
+	dispose: function () {
+
+		for ( var i = 0; i < this.workerPool.length; ++ i ) {
+
+			this.workerPool[ i ].terminate();
+
+		}
+
+		this.workerPool.length = 0;
+
+		return this;
+
+	}
+
+} );
+
+/* WEB WORKER */
+
+Rhino3dmLoader.Rhino3dmWorker = function () {
+
+	var libraryPending;
+	var libraryConfig;
+	var rhino;
+
+	onmessage = function ( e ) {
+
+		var message = e.data;
+
+		switch ( message.type ) {
+
+			case 'init':
+
+				libraryConfig = message.libraryConfig;
+				var wasmBinary = libraryConfig.wasmBinary;
+				var RhinoModule;
+				libraryPending = new Promise( function ( resolve ) { 
+
+					/* Like Basis Loader */
+					RhinoModule = { wasmBinary, onRuntimeInitialized: resolve };
+
+					rhino3dm( RhinoModule );
+
+				 } ).then( () => {
+
+					rhino = RhinoModule;
+
+				 });
+				 
+				break;
+
+			case 'decode':
+
+				var buffer = message.buffer;
+				libraryPending.then( () => {
+
+					var data = decodeObjects( rhino, buffer );
+
+					self.postMessage( { type: 'decode', id: message.id, data } );
+
+					/*
+					var arr = new Uint8Array(buffer);
+					var doc = rhino.File3dm.fromByteArray(arr);
+
+					var objects = doc.objects();
+					var geometryList = [];
+
+					
+					
+					for ( var i = 0; i < objects.count; i++ ) {
+						var obj = objects.get(i).geometry();
+						if( obj instanceof rhino.Mesh ) {
+
+							var geometry = obj.toThreejsJSON();
+							geometryList.push( geometry );
+
+						}
+
+						
+					
+						if(obj instanceof rhino.PointCloud){
+							let threePts = pointsToThreejs(obj, ptMat);
+							scene.add(threePts);
+						}
+
+						
+						
+					}
+
+					*/
+
+				} );
+				
+			break;
+		}
+	};
+
+	function decodeObjects( rhino, buffer ) {
+
+		var arr = new Uint8Array(buffer);
+		var doc = rhino.File3dm.fromByteArray(arr);
+
+		var objects = doc.objects();
+		var materials = doc.materials();
+
+		var objs = [];
+		var mats = [];
+
+		for( var i = 0; i < objects.count; i++ ) {
+
+			var obj = objects.get(i);
+
+			var geo = obj.geometry();
+
+			// TODO: handle other geometry types
+			if( geo instanceof rhino.Mesh ) {
+
+				var attr = obj.attributes();
+				var attributes = {};
+
+				for ( var property in attr ) {
+
+					// console.log(`${property}: ${attr[property]}`);
+
+					if( typeof attr[property] !== 'function' ){
+
+						attributes[property] = attr[property];
+
+					} else {
+
+						// TODO: extract data from functions such as user strings
+
+					}
+				}
+
+				objs.push( { geometry: geo.toThreejsJSON(), attributes } );
+
+			}
+			
+		}
+
+		for( var i = 0; i < materials.count(); i++) {
+
+			var mat = materials.get( i );
+
+			var material = {};
+
+			// console.log(mat);
+
+			for ( var property in mat ) { 
+
+				// console.log(`${property}: ${mat[property]}`);
+
+				if( typeof mat[property] !== 'function' ){
+
+					material[property] = mat[property];
+
+				} else {
+
+					// TODO: extract data from functions
+
+				}
+
+			}
+
+			mats.push( material );
+
+		}
+
+		return { objects: objs, materials: mats };
+
+	}
+
+};
+
+export { Rhino3dmLoader };

BIN
examples/models/3dm/hello_mesh.3dm


+ 86 - 0
examples/webgl_loader_3dm.html

@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - loaders - Rhino 3DM loader</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+
+	<body>
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - Rhino 3DM loader
+		</div>
+
+		<script type="module">
+
+			import * as THREE from '../build/three.module.js';
+
+			import { OrbitControls } from './jsm/controls/OrbitControls.js';
+			import { Rhino3dmLoader } from './jsm/loaders/3DMLoader.js';
+
+			var container, controls;
+			var camera, scene, renderer;
+
+			init();
+			animate();
+
+			function init() {
+
+				THREE.Object3D.DefaultUp = new THREE.Vector3(0,0,1);
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.set(26,-40,5)
+
+				scene = new THREE.Scene();
+				scene.add( new THREE.HemisphereLight() );
+				scene.add( new THREE.AmbientLight() );
+
+				var directionalLight = new THREE.DirectionalLight( 0xffeedd );
+				directionalLight.position.set( 0, 0, 2 );
+				scene.add( directionalLight );
+
+				var loader = new Rhino3dmLoader( );
+				loader.setLibraryPath('jsm/libs/rhino3dm/');
+				
+				loader.load( 'models/3dm/hello_mesh.3dm', function ( object ) {
+
+					scene.add( object );
+
+                } );
+                
+                
+				renderer = new THREE.WebGLRenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				controls = new OrbitControls( camera, renderer.domElement );
+
+				window.addEventListener( 'resize', resize, false );
+
+			}
+
+			function resize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				controls.update();
+				renderer.render( scene, camera );
+
+				requestAnimationFrame( animate );
+
+			}
+		</script>
+
+	</body>
+</html>

+ 1 - 1
examples/webgl_loader_draco.html

@@ -26,7 +26,7 @@
 		// Configure and create Draco decoder.
 		var dracoLoader = new DRACOLoader();
 		dracoLoader.setDecoderPath( 'js/libs/draco/' );
-		dracoLoader.setDecoderConfig( { type: 'js' } );
+		//dracoLoader.setDecoderConfig( { type: 'js' } );
 
 		init();
 		animate();

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott