Skip to content

Commit

Permalink
Merge pull request #21248 from mcneel/wip/3DMLoader
Browse files Browse the repository at this point in the history
3dmLoader: Updates
  • Loading branch information
mrdoob committed Feb 10, 2021
2 parents 6a51509 + 293476e commit a43d238
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 68 deletions.
87 changes: 74 additions & 13 deletions docs/examples/en/loaders/3DMLoader.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ <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.
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].
Currently uses rhino3dm.js 0.13.0
The loader currently uses <a href="https://www.npmjs.com/package/rhino3dm/v/0.15.0-beta">rhino3dm.js 0.15.0-beta.</a>
</p>

<h2>Supported Conversions</h2>

<p>
[name] loads the following objects to a respective three.js type:
The [name] converts Rhino objects to the following three.js types:
</p>

<table>
Expand All @@ -42,23 +42,23 @@ <h2>Supported Conversions</h2>
</tr>
<tr>
<td>Curve</td>
<td>[page:Line Line] <sup>1</sup></td>
<td>[page:Line Line] <sup> 1</sup></td>
</tr>
<tr>
<td>Mesh</td>
<td>[page:Mesh Mesh]</td>
</tr>
<tr>
<td>Extrusion</td>
<td>[page:Mesh Mesh] <sup> 2</sup></td>
<td>[page:Mesh Mesh]<sup> 2</sup></td>
</tr>
<tr>
<td>BREP</td>
<td>[page:Object3D Object3D] <sup>2, 3</sup></td>
<td>[page:Mesh Mesh]<sup> 2</sup></td>
</tr>
<tr>
<td>SubD</td>
<td>[page:Mesh Mesh] <sup>4</sup></td>
<td>[page:Mesh Mesh]<sup> 3</sup></td>
</tr>
<tr>
<td>InstanceReferences</td>
Expand All @@ -80,19 +80,26 @@ <h2>Supported Conversions</h2>
<td>SpotLight</td>
<td>[page:SpotLight SpotLight]</td>
</tr>
<tr>
<td>File3dm</td>
<td>[page:Object3D Object3D]<sup> 4</sup></td>
</tr>
</table>

<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".
<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.
</i></p>
<p><i>
<sup>3</sup> SubD objects are represented by subdividing their control net.
</i></p>
<p><i>
<sup>3</sup> BREPS are converted to an Object3D with it's children array populated with BREP Faces
<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.
</i></p>
<p><i>
<sup>4</sup> SubD objects are represented by subdividing their control net.
<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>
Expand All @@ -101,8 +108,9 @@ <h2>Code Example</h2>
// Instantiate a loader
const loader = new Rhino3dmLoader();

// Specify path to a folder containing WASM/JS libraries.
loader.setLibraryPath( '/examples/jsm/libs/rhino3dm/' );
// 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/rhino3dm@0.15.0-beta/' );

// Load a 3DM file
loader.load(
Expand Down Expand Up @@ -161,13 +169,66 @@ <h3>[method:null load]( [param:String url], [param:Function onLoad], [param:Func
[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.
Begin loading from url and call the <em>onLoad</em> function with the resulting Object3d.
</p>

<h3>[method:null parse]( [param:ArrayBuffer buffer], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
<p>
[page:ArrayBuffer buffer] — An ArrayBuffer representing the Rhino <em>File3dm</em> document.<br />
[page:Function onLoad] — A function to be called after the loading is successfully completed.<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>
Parse a File3dm ArrayBuffer and call the <em>onLoad</em> function with the resulting Object3d.
See <a href="https://github.com/mcneel/rhino-developer-samples/tree/7/rhino3dm/js/SampleParse3dmObjects">this example</a> for further reference.
</p>

<code>
import rhino3dm from 'https://cdn.jsdelivr.net/npm/rhino3dm@0.15.0-beta/rhino3dm.module.js'

// 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/rhino3dm@0.15.0-beta/' );

rhino3dm().then(async m => {

console.log('Loaded rhino3dm.');
const rhino = m; // global

// 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 );

// create a copy of the doc.toByteArray data to get an ArrayBuffer
const buffer = new Uint8Array( doc.toByteArray() ).buffer;

loader.parse( buffer, function ( object ) {

scene.add( object );

} );

})

</code>

<h3>[method:this setLibraryPath]( [param:String value] )</h3>
<p>
[page:String value] — Path to folder containing the JS and WASM libraries.
</p>
<code>
// Instantiate a loader
const loader = new Rhino3dmLoader();

// 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/rhino3dm@0.15.0-beta/' );
</code>

<h3>[method:this setWorkerLimit]( [param:Number workerLimit] )</h3>
<p>
Expand Down
15 changes: 7 additions & 8 deletions examples/jsm/libs/rhino3dm/rhino3dm.js

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions examples/jsm/libs/rhino3dm/rhino3dm.module.js

Large diffs are not rendered by default.

Binary file modified examples/jsm/libs/rhino3dm/rhino3dm.wasm
Binary file not shown.
76 changes: 30 additions & 46 deletions examples/jsm/loaders/3DMLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ var Rhino3dmLoader = function ( manager ) {
this.libraryBinary = null;
this.libraryConfig = {};

this.url = '';

this.workerLimit = 4;
this.workerPool = [];
this.workerNextTaskID = 1;
Expand Down Expand Up @@ -73,6 +75,8 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
loader.setResponseType( 'arraybuffer' );
loader.setRequestHeader( this.requestHeader );

this.url = url;

loader.load( url, ( buffer ) => {

// Check for an existing task using this buffer. A transferred buffer cannot be transferred
Expand Down Expand Up @@ -291,6 +295,9 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
object.userData[ 'layers' ] = data.layers;
object.userData[ 'groups' ] = data.groups;
object.userData[ 'settings' ] = data.settings;
object.userData[ 'objectType' ] = 'File3dm';
object.userData[ 'materials' ] = null;
object.name = this.url;

var objects = data.objects;
var materials = data.materials;
Expand All @@ -316,9 +323,9 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {

default:

if ( attributes.hasOwnProperty( 'materialUUID' ) ) {
if ( attributes.materialIndex >= 0 ) {

var rMaterial = materials.find( m => m.id === attributes.materialUUID );
var rMaterial = materials[ attributes.materialIndex ];
var material = this._createMaterial( rMaterial );
material = this._compareMaterials( material );
var _object = this._createObject( obj, material );
Expand Down Expand Up @@ -410,8 +417,7 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {

}

this.materials = [];

object.userData[ 'materials' ] = this.materials;
return object;

},
Expand Down Expand Up @@ -459,6 +465,9 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
case 'Mesh':
case 'Extrusion':
case 'SubD':
case 'Brep':

if ( obj.geometry === null ) return;

var geometry = loader.parse( obj.geometry );

Expand Down Expand Up @@ -489,32 +498,6 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {

return mesh;

case 'Brep':

var brepObject = new Object3D();

for ( var j = 0; j < obj.geometry.length; j ++ ) {

geometry = loader.parse( obj.geometry[ j ] );
var mesh = new Mesh( geometry, mat );
mesh.castShadow = attributes.castsShadows;
mesh.receiveShadow = attributes.receivesShadows;

brepObject.add( mesh );

}

brepObject.userData[ 'attributes' ] = attributes;
brepObject.userData[ 'objectType' ] = obj.objectType;

if ( attributes.name ) {

brepObject.name = attributes.name;

}

return brepObject;

case 'Curve':

geometry = loader.parse( obj.geometry );
Expand Down Expand Up @@ -862,14 +845,7 @@ Rhino3dmLoader.Rhino3dmWorker = function () {

_object.delete();

if ( object !== undefined ) {

if ( object.attributes.materialIndex >= 0 ) {

var mId = doc.materials().findIndex( object.attributes.materialIndex ).id;
object.attributes.materialUUID = mId;

}
if ( object ) {

objects.push( object );

Expand Down Expand Up @@ -1165,25 +1141,33 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
case rhino.ObjectType.Brep:

var faces = _geometry.faces();
geometry = [];
var mesh = new rhino.Mesh();

for ( var faceIndex = 0; faceIndex < faces.count; faceIndex ++ ) {

var face = faces.get( faceIndex );
var mesh = face.getMesh( rhino.MeshType.Any );
var _mesh = face.getMesh( rhino.MeshType.Any );

if ( mesh ) {
if ( _mesh ) {

geometry.push( mesh.toThreejsJSON() );
mesh.delete();
mesh.append( _mesh );
_mesh.delete();

}

face.delete();

}

faces.delete();
if ( mesh.faces().count > 0 ) {

mesh.compact();
geometry = mesh.toThreejsJSON();
faces.delete();

}

mesh.delete();

break;

Expand Down Expand Up @@ -1246,7 +1230,7 @@ Rhino3dmLoader.Rhino3dmWorker = function () {

}

if ( Array.isArray( geometry ) === false || geometry.length > 0 ) {
if ( geometry ) {

var attributes = extractProperties( _attributes );
attributes.geometry = extractProperties( _geometry );
Expand All @@ -1273,7 +1257,7 @@ Rhino3dmLoader.Rhino3dmWorker = function () {

} else {

console.warn( 'THREE.3DMLoader: Object has no mesh geometry. Consider opening this in Rhino, using a shaded display mode, and exporting again.' );
console.warn( `THREE.3DMLoader: ${objectType.constructor.name} has no associated mesh geometry.` );

}

Expand Down
25 changes: 24 additions & 1 deletion examples/webgl_loader_3dm.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,33 @@
<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">
<style>
#loader {
border: 5px solid #f3f3f3; /* Light grey */
border-top: 5px solid #3d3d3d; /* Grey */
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
position: absolute;
top: 50%;
left: 50%;
z-index: 2;
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>

<body>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - Rhino 3DM loader
</div>

<div id="loader"></div>
<script type="module">

import * as THREE from '../build/three.module.js';
Expand Down Expand Up @@ -46,13 +66,16 @@
scene.add( directionalLight );

const loader = new Rhino3dmLoader();
loader.setLibraryPath( 'jsm/libs/rhino3dm/' );
loader.setLibraryPath( 'https://cdn.jsdelivr.net/npm/rhino3dm@0.15.0-beta/' );

loader.load( 'models/3dm/Rhino_Logo.3dm', function ( object ) {

scene.add( object );
initGUI( object.userData.layers );

// hide spinner
document.getElementById( 'loader' ).style.display = 'none';

} );

const width = window.innerWidth;
Expand Down

0 comments on commit a43d238

Please sign in to comment.