|
@@ -72,7 +72,7 @@ THREE.ThreeMFLoader.prototype = {
|
|
|
|
|
|
for ( file in zip.files ) {
|
|
|
|
|
|
- if ( file.match( /\.rels$/ ) ) {
|
|
|
+ if ( file.match( /\_rels\/.rels$/ ) ) {
|
|
|
|
|
|
relsName = file;
|
|
|
|
|
@@ -206,9 +206,38 @@ THREE.ThreeMFLoader.prototype = {
|
|
|
}
|
|
|
|
|
|
function parseBasematerialsNode( basematerialsNode ) {
|
|
|
+
|
|
|
+ var basematerialsData = {
|
|
|
+ id: basematerialsNode.getAttribute( 'id' ), // required
|
|
|
+ basematerials: []
|
|
|
+ };
|
|
|
+
|
|
|
+ var basematerialNodes = basematerialsNode.querySelectorAll( 'base' );
|
|
|
+
|
|
|
+ for ( var i = 0; i < basematerialNodes.length; i ++ ) {
|
|
|
+
|
|
|
+ var basematerialNode = basematerialNodes[ i ];
|
|
|
+ var basematerialData = parseBasematerialNode( basematerialNode );
|
|
|
+ basematerialsData.basematerials.push( basematerialData );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return basematerialsData;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
- function parseMeshNode( meshNode, extensions ) {
|
|
|
+ function parseBasematerialNode( basematerialNode ) {
|
|
|
+
|
|
|
+ var basematerialData = {};
|
|
|
+
|
|
|
+ basematerialData[ 'name' ] = basematerialNode.getAttribute( 'name' ); // required
|
|
|
+ basematerialData[ 'displaycolor' ] = basematerialNode.getAttribute( 'displaycolor' ); // required
|
|
|
+
|
|
|
+ return basematerialData;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function parseMeshNode( meshNode ) {
|
|
|
|
|
|
var meshData = {};
|
|
|
|
|
@@ -300,6 +329,59 @@ THREE.ThreeMFLoader.prototype = {
|
|
|
|
|
|
function parseComponentsNode( componentsNode ) {
|
|
|
|
|
|
+ var components = [];
|
|
|
+
|
|
|
+ var componentNodes = componentsNode.querySelectorAll( 'component' );
|
|
|
+
|
|
|
+ for ( var i = 0; i < componentNodes.length; i ++ ) {
|
|
|
+
|
|
|
+ var componentNode = componentNodes[ i ];
|
|
|
+ var componentData = parseComponentNode( componentNode );
|
|
|
+ components.push( componentData );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return components;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function parseComponentNode( componentNode ) {
|
|
|
+
|
|
|
+ var componentData = {};
|
|
|
+
|
|
|
+ componentData[ 'objectId' ] = componentNode.getAttribute( 'objectid' ); // required
|
|
|
+
|
|
|
+ var transform = componentNode.getAttribute( 'transform' );
|
|
|
+
|
|
|
+ if ( transform ) {
|
|
|
+
|
|
|
+ componentData[ 'transform' ] = parseTransform( transform );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return componentData;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function parseTransform( transform ) {
|
|
|
+
|
|
|
+ var t = [];
|
|
|
+ transform.split( ' ' ).forEach( function ( s ) {
|
|
|
+
|
|
|
+ t.push( parseFloat( s ) );
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ var matrix = new THREE.Matrix4();
|
|
|
+ matrix.set(
|
|
|
+ t[ 0 ], t[ 3 ], t[ 6 ], t[ 9 ],
|
|
|
+ t[ 1 ], t[ 4 ], t[ 7 ], t[ 10 ],
|
|
|
+ t[ 2 ], t[ 5 ], t[ 8 ], t[ 11 ],
|
|
|
+ 0.0, 0.0, 0.0, 1.0
|
|
|
+ );
|
|
|
+
|
|
|
+ return matrix;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
function parseObjectNode( objectNode ) {
|
|
@@ -383,7 +465,7 @@ THREE.ThreeMFLoader.prototype = {
|
|
|
|
|
|
if ( basematerialsNode ) {
|
|
|
|
|
|
- resourcesData[ 'basematerial' ] = parseBasematerialsNode( basematerialsNode );
|
|
|
+ resourcesData[ 'basematerials' ] = parseBasematerialsNode( basematerialsNode );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -411,25 +493,13 @@ THREE.ThreeMFLoader.prototype = {
|
|
|
|
|
|
var itemNode = itemNodes[ i ];
|
|
|
var buildItem = {
|
|
|
- objectid: itemNode.getAttribute( 'objectid' )
|
|
|
+ objectId: itemNode.getAttribute( 'objectid' )
|
|
|
};
|
|
|
var transform = itemNode.getAttribute( 'transform' );
|
|
|
|
|
|
if ( transform ) {
|
|
|
|
|
|
- var t = [];
|
|
|
- transform.split( ' ' ).forEach( function ( s ) {
|
|
|
-
|
|
|
- t.push( parseFloat( s ) );
|
|
|
-
|
|
|
- } );
|
|
|
- var mat4 = new THREE.Matrix4();
|
|
|
- buildItem[ 'transform' ] = mat4.set(
|
|
|
- t[ 0 ], t[ 3 ], t[ 6 ], t[ 9 ],
|
|
|
- t[ 1 ], t[ 4 ], t[ 7 ], t[ 10 ],
|
|
|
- t[ 2 ], t[ 5 ], t[ 8 ], t[ 11 ],
|
|
|
- 0.0, 0.0, 0.0, 1.0
|
|
|
- );
|
|
|
+ buildItem[ 'transform' ] = parseTransform( transform );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -472,40 +542,172 @@ THREE.ThreeMFLoader.prototype = {
|
|
|
|
|
|
}
|
|
|
|
|
|
- function buildMesh( meshData, data3mf ) {
|
|
|
+ function buildMesh( meshData, objects, modelData, objectData ) {
|
|
|
+
|
|
|
+ // geometry
|
|
|
|
|
|
var geometry = new THREE.BufferGeometry();
|
|
|
geometry.setIndex( new THREE.BufferAttribute( meshData[ 'triangles' ], 1 ) );
|
|
|
geometry.addAttribute( 'position', new THREE.BufferAttribute( meshData[ 'vertices' ], 3 ) );
|
|
|
|
|
|
- if ( meshData[ 'colors' ] ) {
|
|
|
+ // groups
|
|
|
+
|
|
|
+ var basematerialsData = modelData[ 'resources' ][ 'basematerials' ];
|
|
|
+ var triangleProperties = meshData[ 'triangleProperties' ];
|
|
|
|
|
|
- geometry.addAttribute( 'color', new THREE.BufferAttribute( meshData[ 'colors' ], 3 ) );
|
|
|
+ var start = 0;
|
|
|
+ var count = 0;
|
|
|
+ var currentMaterialIndex = - 1;
|
|
|
+
|
|
|
+ for ( var i = 0, l = triangleProperties.length; i < l; i ++ ) {
|
|
|
+
|
|
|
+ var triangleProperty = triangleProperties[ i ];
|
|
|
+ var pid = triangleProperty.pid;
|
|
|
+
|
|
|
+ // only proceed if the triangle refers to a basematerials definition
|
|
|
+
|
|
|
+ if ( basematerialsData && ( basematerialsData.id === pid ) ) {
|
|
|
+
|
|
|
+ if ( currentMaterialIndex === - 1 ) currentMaterialIndex = triangleProperty.p1;
|
|
|
+
|
|
|
+ if ( currentMaterialIndex === triangleProperty.p1 ) {
|
|
|
+
|
|
|
+ count += 3; // primitves per triangle
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ geometry.addGroup( start, count, currentMaterialIndex );
|
|
|
+
|
|
|
+ start += count;
|
|
|
+ count = 3;
|
|
|
+ currentMaterialIndex = triangleProperty.p1;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
+ if ( geometry.groups.length > 0 ) mergeGroups( geometry );
|
|
|
+
|
|
|
geometry.computeBoundingSphere();
|
|
|
|
|
|
- var materialOpts = {
|
|
|
- flatShading: true
|
|
|
- };
|
|
|
+ // material
|
|
|
+
|
|
|
+ var material;
|
|
|
+
|
|
|
+ if ( basematerialsData && ( basematerialsData.id === objectData.pid ) ) {
|
|
|
+
|
|
|
+ var materialIndex = objectData.pindex;
|
|
|
+ var basematerialData = basematerialsData.basematerials[ materialIndex ];
|
|
|
+
|
|
|
+ material = getBuild( basematerialData, objects, modelData, objectData, buildBasematerial );
|
|
|
+
|
|
|
+ } else if ( geometry.groups.length > 0 ) {
|
|
|
+
|
|
|
+ var groups = geometry.groups;
|
|
|
+ material = [];
|
|
|
|
|
|
- if ( meshData[ 'colors' ] && 0 < meshData[ 'colors' ].length ) {
|
|
|
+ for ( var i = 0, l = groups.length; i < l; i ++ ) {
|
|
|
|
|
|
- materialOpts[ 'vertexColors' ] = THREE.VertexColors;
|
|
|
+ var group = groups[ i ];
|
|
|
+ var basematerialData = basematerialsData.basematerials[ group.materialIndex ];
|
|
|
+
|
|
|
+ material.push( getBuild( basematerialData, objects, modelData, objectData, buildBasematerial ) );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- materialOpts[ 'color' ] = 0xaaaaff;
|
|
|
+ // default material
|
|
|
+
|
|
|
+ material = new THREE.MeshPhongMaterial( { color: 0xaaaaff, flatShading: true } );
|
|
|
|
|
|
}
|
|
|
|
|
|
- var material = new THREE.MeshPhongMaterial( materialOpts );
|
|
|
return new THREE.Mesh( geometry, material );
|
|
|
|
|
|
}
|
|
|
|
|
|
- function applyExtensions( extensions, meshData, modelXml, data3mf ) {
|
|
|
+ function mergeGroups( geometry ) {
|
|
|
+
|
|
|
+ // sort by material index
|
|
|
+
|
|
|
+ var groups = geometry.groups.sort( function ( a, b ) {
|
|
|
+
|
|
|
+ if ( a.materialIndex !== b.materialIndex ) return a.materialIndex - b.materialIndex;
|
|
|
+
|
|
|
+ return a.start - b.start;
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ // reorganize index buffer
|
|
|
+
|
|
|
+ var index = geometry.index;
|
|
|
+
|
|
|
+ var itemSize = index.itemSize;
|
|
|
+ var srcArray = index.array;
|
|
|
+
|
|
|
+ var targetOffset = 0;
|
|
|
+
|
|
|
+ var targetArray = new srcArray.constructor( srcArray.length );
|
|
|
+
|
|
|
+ for ( var i = 0; i < groups.length; i ++ ) {
|
|
|
+
|
|
|
+ var group = groups[ i ];
|
|
|
+
|
|
|
+ var groupLength = group.count * itemSize;
|
|
|
+ var groupStart = group.start * itemSize;
|
|
|
+
|
|
|
+ var sub = srcArray.subarray( groupStart, groupStart + groupLength );
|
|
|
+
|
|
|
+ targetArray.set( sub, targetOffset );
|
|
|
+
|
|
|
+ targetOffset += groupLength;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ srcArray.set( targetArray );
|
|
|
+
|
|
|
+ // update groups
|
|
|
+
|
|
|
+ var start = 0;
|
|
|
+
|
|
|
+ for ( i = 0; i < groups.length; i ++ ) {
|
|
|
+
|
|
|
+ group = groups[ i ];
|
|
|
+
|
|
|
+ group.start = start;
|
|
|
+ start += group.count;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // merge groups
|
|
|
+
|
|
|
+ var lastGroup = groups[ 0 ];
|
|
|
+
|
|
|
+ geometry.groups = [ lastGroup ];
|
|
|
+
|
|
|
+ for ( i = 1; i < groups.length; i ++ ) {
|
|
|
+
|
|
|
+ group = groups[ i ];
|
|
|
+
|
|
|
+ if ( lastGroup.materialIndex === group.materialIndex ) {
|
|
|
+
|
|
|
+ lastGroup.count += group.count;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ lastGroup = group;
|
|
|
+ geometry.groups.push( lastGroup );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function applyExtensions( extensions, meshData, modelXml ) {
|
|
|
|
|
|
if ( ! extensions ) {
|
|
|
|
|
@@ -543,38 +745,131 @@ THREE.ThreeMFLoader.prototype = {
|
|
|
|
|
|
}
|
|
|
|
|
|
- function buildMeshes( data3mf ) {
|
|
|
+ function getBuild( data, objects, modelData, objectData, builder ) {
|
|
|
+
|
|
|
+ if ( data.build !== undefined ) return data.build;
|
|
|
+
|
|
|
+ data.build = builder( data, objects, modelData, objectData );
|
|
|
+
|
|
|
+ return data.build;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function buildBasematerial( materialData ) {
|
|
|
+
|
|
|
+ var material = new THREE.MeshPhongMaterial( { flatShading: true } );
|
|
|
+
|
|
|
+ material.name = materialData.name;
|
|
|
+
|
|
|
+ // displaycolor MUST be specified with a value of a 6 or 8 digit hexadecimal number, e.g. "#RRGGBB" or "#RRGGBBAA"
|
|
|
+
|
|
|
+ var displaycolor = materialData.displaycolor;
|
|
|
+
|
|
|
+ var color = displaycolor.substring( 0, 7 );
|
|
|
+ material.color.setStyle( color );
|
|
|
+ material.color.convertSRGBToLinear(); // displaycolor is in sRGB
|
|
|
+
|
|
|
+ // process alpha if set
|
|
|
+
|
|
|
+ if ( displaycolor.length === 9 ) {
|
|
|
+
|
|
|
+ material.opacity = parseInt( displaycolor.charAt( 7 ) + displaycolor.charAt( 8 ), 16 ) / 255;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return material;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function buildComposite( compositeData, objects, modelData ) {
|
|
|
+
|
|
|
+ var composite = new THREE.Group();
|
|
|
+
|
|
|
+ for ( var j = 0; j < compositeData.length; j ++ ) {
|
|
|
+
|
|
|
+ var component = compositeData[ j ];
|
|
|
+ var build = objects[ component.objectId ];
|
|
|
+
|
|
|
+ if ( build === undefined ) {
|
|
|
+
|
|
|
+ buildObject( component.objectId, objects, modelData );
|
|
|
+ build = objects[ component.objectId ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ var object3D = build.clone();
|
|
|
+
|
|
|
+ // apply component transfrom
|
|
|
+
|
|
|
+ var transform = component.transform;
|
|
|
+
|
|
|
+ if ( transform ) {
|
|
|
+
|
|
|
+ object3D.applyMatrix( transform );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ composite.add( object3D );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return composite;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function buildObject( objectId, objects, modelData ) {
|
|
|
+
|
|
|
+ var objectData = modelData[ 'resources' ][ 'object' ][ objectId ];
|
|
|
+
|
|
|
+ if ( objectData[ 'mesh' ] ) {
|
|
|
+
|
|
|
+ var meshData = objectData[ 'mesh' ];
|
|
|
+
|
|
|
+ var extensions = modelData[ 'extensions' ];
|
|
|
+ var modelXml = modelData[ 'xml' ];
|
|
|
+
|
|
|
+ applyExtensions( extensions, meshData, modelXml );
|
|
|
+
|
|
|
+ objects[ objectData.id ] = getBuild( meshData, objects, modelData, objectData, buildMesh );
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ var compositeData = objectData[ 'components' ];
|
|
|
+
|
|
|
+ objects[ objectData.id ] = getBuild( compositeData, objects, modelData, objectData, buildComposite );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function buildObjects( data3mf ) {
|
|
|
|
|
|
var modelsData = data3mf.model;
|
|
|
- var meshes = {};
|
|
|
+ var objects = {};
|
|
|
var modelsKeys = Object.keys( modelsData );
|
|
|
|
|
|
for ( var i = 0; i < modelsKeys.length; i ++ ) {
|
|
|
|
|
|
var modelsKey = modelsKeys[ i ];
|
|
|
var modelData = modelsData[ modelsKey ];
|
|
|
- var modelXml = modelData[ 'xml' ];
|
|
|
- var extensions = modelData[ 'extensions' ];
|
|
|
|
|
|
var objectIds = Object.keys( modelData[ 'resources' ][ 'object' ] );
|
|
|
|
|
|
for ( var j = 0; j < objectIds.length; j ++ ) {
|
|
|
|
|
|
var objectId = objectIds[ j ];
|
|
|
- var objectData = modelData[ 'resources' ][ 'object' ][ objectId ];
|
|
|
- var meshData = objectData[ 'mesh' ];
|
|
|
- applyExtensions( extensions, meshData, modelXml, data3mf );
|
|
|
- meshes[ objectId ] = buildMesh( meshData, data3mf );
|
|
|
+
|
|
|
+ buildObject( objectId, objects, modelData );
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- return meshes;
|
|
|
+ return objects;
|
|
|
|
|
|
}
|
|
|
|
|
|
- function build( meshes, refs, data3mf ) {
|
|
|
+ function build( objects, refs, data3mf ) {
|
|
|
|
|
|
var group = new THREE.Group();
|
|
|
var buildData = data3mf.model[ refs[ 'target' ].substring( 1 ) ][ 'build' ];
|
|
@@ -582,15 +877,19 @@ THREE.ThreeMFLoader.prototype = {
|
|
|
for ( var i = 0; i < buildData.length; i ++ ) {
|
|
|
|
|
|
var buildItem = buildData[ i ];
|
|
|
- var mesh = meshes[ buildItem[ 'objectid' ] ];
|
|
|
+ var object3D = objects[ buildItem[ 'objectId' ] ];
|
|
|
+
|
|
|
+ // apply transform
|
|
|
|
|
|
- if ( buildItem[ 'transform' ] ) {
|
|
|
+ var transform = buildItem[ 'transform' ];
|
|
|
+
|
|
|
+ if ( transform ) {
|
|
|
|
|
|
- mesh.geometry.applyMatrix( buildItem[ 'transform' ] );
|
|
|
+ object3D.applyMatrix( transform );
|
|
|
|
|
|
}
|
|
|
|
|
|
- group.add( mesh );
|
|
|
+ group.add( object3D );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -599,9 +898,9 @@ THREE.ThreeMFLoader.prototype = {
|
|
|
}
|
|
|
|
|
|
var data3mf = loadDocument( data );
|
|
|
- var meshes = buildMeshes( data3mf );
|
|
|
+ var objects = buildObjects( data3mf );
|
|
|
|
|
|
- return build( meshes, data3mf[ 'rels' ], data3mf );
|
|
|
+ return build( objects, data3mf[ 'rels' ], data3mf );
|
|
|
|
|
|
},
|
|
|
|