2 * @author technohippy / https://github.com/technohippy
5 THREE.ThreeMFLoader = function ( manager ) {
7 this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
8 this.availableExtensions = [];
12 THREE.ThreeMFLoader.prototype = {
14 constructor: THREE.ThreeMFLoader,
16 load: function ( url, onLoad, onProgress, onError ) {
19 var loader = new THREE.FileLoader( scope.manager );
20 loader.setResponseType( 'arraybuffer' );
21 loader.load( url, function( buffer ) {
23 onLoad( scope.parse( buffer ) );
25 }, onProgress, onError );
29 parse: function ( data ) {
33 function loadDocument( data ) {
39 var modelPartNames = [];
40 var printTicketPartNames = [];
41 var texturesPartNames = [];
42 var otherPartNames = [];
46 var printTicketParts = {};
47 var texturesParts = {};
52 zip = new JSZip( data );
56 if ( e instanceof ReferenceError ) {
58 console.error( 'THREE.ThreeMFLoader: jszip missing and file is compressed.' );
65 for ( file in zip.files ) {
67 if ( file.match( /\.rels$/ ) ) {
71 } else if ( file.match(/^3D\/.*\.model$/) ) {
73 modelPartNames.push( file );
75 } else if ( file.match(/^3D\/Metadata\/.*\.xml$/) ) {
77 printTicketPartNames.push( file );
79 } else if ( file.match(/^3D\/Textures\/.*/) ) {
81 texturesPartNames.push( file );
83 } else if ( file.match(/^3D\/Other\/.*/) ) {
85 otherPartNames.push( file );
91 var relsView = new DataView( zip.file( relsName ).asArrayBuffer() );
92 var relsFileText = new TextDecoder( 'utf-8' ).decode( relsView );
93 rels = parseRelsXml( relsFileText );
95 for ( var i = 0; i < modelPartNames.length; i++ ) {
97 var modelPart = modelPartNames[ i ];
98 var view = new DataView( zip.file( modelPart ).asArrayBuffer() );
100 if ( TextDecoder === undefined ) {
102 console.error( 'THREE.ThreeMFLoader: TextDecoder not present. Please use a TextDecoder polyfill.' );
107 var fileText = new TextDecoder( 'utf-8' ).decode( view );
108 var xmlData = new DOMParser().parseFromString( fileText, 'application/xml' );
110 if ( xmlData.documentElement.nodeName.toLowerCase() !== 'model' ) {
112 console.error( 'THREE.ThreeMFLoader: Error loading 3MF - no 3MF document found: ', modelPart );
116 var modelNode = xmlData.querySelector( 'model' );
119 for ( var i = 0; i < modelNode.attributes.length; i++ ) {
121 var attr = modelNode.attributes[ i ];
122 if ( attr.name.match( /^xmlns:(.+)$/ ) ) {
124 extensions[ attr.value ] = RegExp.$1;
130 var modelData = parseModelNode( modelNode );
131 modelData[ 'xml' ] = modelNode;
133 if ( 0 < Object.keys( extensions ).length ) {
135 modelData[ 'extensions' ] = extensions;
139 modelParts[ modelPart ] = modelData;
143 for ( var i = 0; i < texturesPartNames.length; i++ ) {
145 var texturesPartName = texturesPartNames[ i ];
146 texturesParts[ texturesPartName ] = zip.file( texturesPartName ).asBinary();
153 printTicket: printTicketParts,
154 texture: texturesParts,
159 function parseRelsXml( relsFileText ) {
161 var relsXmlData = new DOMParser().parseFromString( relsFileText, 'application/xml' );
162 var relsNode = relsXmlData.querySelector( 'Relationship' );
163 var target = relsNode.getAttribute( 'Target' );
164 var id = relsNode.getAttribute( 'Id' );
165 var type = relsNode.getAttribute( 'Type' );
175 function parseMetadataNodes( metadataNodes ) {
177 var metadataData = {};
179 for ( var i = 0; i < metadataNodes.length; i++ ) {
181 var metadataNode = metadataNodes[ i ];
182 var name = metadataNode.getAttribute('name');
194 if ( 0 <= validNames.indexOf( name ) ) {
196 metadataData[ name ] = metadataNode.textContent;
206 function parseBasematerialsNode( basematerialsNode ) {
209 function parseMeshNode( meshNode, extensions ) {
214 var vertexNodes = meshNode.querySelectorAll( 'vertices vertex' );
216 for ( var i = 0; i < vertexNodes.length; i++ ) {
218 var vertexNode = vertexNodes[ i ];
219 var x = vertexNode.getAttribute( 'x' );
220 var y = vertexNode.getAttribute( 'y' );
221 var z = vertexNode.getAttribute( 'z' );
223 vertices.push( parseFloat( x ), parseFloat( y ), parseFloat( z ) );
227 meshData[ 'vertices' ] = new Float32Array( vertices.length );
229 for ( var i = 0; i < vertices.length; i++ ) {
231 meshData[ 'vertices' ][ i ] = vertices[ i ];
235 var triangleProperties = [];
237 var triangleNodes = meshNode.querySelectorAll( 'triangles triangle' );
239 for ( var i = 0; i < triangleNodes.length; i++ ) {
241 var triangleNode = triangleNodes[ i ];
242 var v1 = triangleNode.getAttribute( 'v1' );
243 var v2 = triangleNode.getAttribute( 'v2' );
244 var v3 = triangleNode.getAttribute( 'v3' );
245 var p1 = triangleNode.getAttribute( 'p1' );
246 var p2 = triangleNode.getAttribute( 'p2' );
247 var p3 = triangleNode.getAttribute( 'p3' );
248 var pid = triangleNode.getAttribute( 'pid' );
250 triangles.push( parseInt( v1, 10 ), parseInt( v2, 10 ), parseInt( v3, 10 ) );
252 var triangleProperty = {};
256 triangleProperty[ 'p1' ] = parseInt( p1, 10 );
262 triangleProperty[ 'p2' ] = parseInt( p2, 10 );
268 triangleProperty[ 'p3' ] = parseInt( p3, 10 );
274 triangleProperty[ 'pid' ] = pid;
278 if ( 0 < Object.keys( triangleProperty ).length ) {
280 triangleProperties.push( triangleProperty );
285 meshData[ 'triangleProperties' ] = triangleProperties;
286 meshData[ 'triangles' ] = new Uint32Array( triangles.length );
288 for ( var i = 0; i < triangles.length; i++ ) {
290 meshData[ 'triangles' ][ i ] = triangles[ i ];
298 function parseComponentsNode( componentsNode ) {
302 function parseObjectNode( objectNode ) {
305 type: objectNode.getAttribute( 'type' )
308 var id = objectNode.getAttribute( 'id' );
312 objectData[ 'id' ] = id;
316 var pid = objectNode.getAttribute( 'pid' );
320 objectData[ 'pid' ] = pid;
324 var pindex = objectNode.getAttribute( 'pindex' );
328 objectData[ 'pindex' ] = pindex;
332 var thumbnail = objectNode.getAttribute( 'thumbnail' );
336 objectData[ 'thumbnail' ] = thumbnail;
340 var partnumber = objectNode.getAttribute( 'partnumber' );
344 objectData[ 'partnumber' ] = partnumber;
348 var name = objectNode.getAttribute( 'name' );
352 objectData[ 'name' ] = name;
356 var meshNode = objectNode.querySelector( 'mesh' );
360 objectData[ 'mesh' ] = parseMeshNode( meshNode );
364 var componentsNode = objectNode.querySelector( 'components' );
366 if ( componentsNode ) {
368 objectData[ 'components' ] = parseComponentsNode( componentsNode );
376 function parseResourcesNode( resourcesNode ) {
378 var resourcesData = {};
379 var basematerialsNode = resourcesNode.querySelector( 'basematerials' );
381 if ( basematerialsNode ) {
383 resourcesData[ 'basematerial' ] = parseBasematerialsNode( basematerialsNode );
387 resourcesData[ 'object' ] = {};
388 var objectNodes = resourcesNode.querySelectorAll( 'object' );
390 for ( var i = 0; i < objectNodes.length; i++ ) {
392 var objectNode = objectNodes[ i ];
393 var objectData = parseObjectNode( objectNode );
394 resourcesData[ 'object' ][ objectData[ 'id' ] ] = objectData;
398 return resourcesData;
402 function parseBuildNode( buildNode ) {
405 var itemNodes = buildNode.querySelectorAll( 'item' );
407 for ( var i = 0; i < itemNodes.length; i++ ) {
409 var itemNode = itemNodes[ i ];
411 objectid: itemNode.getAttribute( 'objectid' )
413 var transform = itemNode.getAttribute( 'transform' );
418 transform.split( ' ' ).forEach( function( s ) {
419 t.push( parseFloat( s ) );
421 var mat4 = new THREE.Matrix4();
422 buildItem[ 'transform' ] = mat4.set(
423 t[ 0 ], t[ 3 ], t[ 6 ], t[ 9 ],
424 t[ 1 ], t[ 4 ], t[ 7 ], t[ 10 ],
425 t[ 2 ], t[ 5 ], t[ 8 ], t[ 11 ],
431 buildData.push( buildItem );
439 function parseModelNode( modelNode ) {
441 var modelData = { unit: modelNode.getAttribute( 'unit' ) || 'millimeter' };
442 var metadataNodes = modelNode.querySelectorAll( 'metadata' );
444 if ( metadataNodes ) {
446 modelData[ 'metadata' ] = parseMetadataNodes( metadataNodes );
450 var resourcesNode = modelNode.querySelector( 'resources' );
452 if ( resourcesNode ) {
454 modelData[ 'resources' ] = parseResourcesNode( resourcesNode );
458 var buildNode = modelNode.querySelector( 'build' );
462 modelData[ 'build' ] = parseBuildNode( buildNode );
470 function buildMesh( meshData, data3mf ) {
472 var geometry = new THREE.BufferGeometry();
473 geometry.setIndex( new THREE.BufferAttribute( meshData[ 'triangles' ], 1 ) );
474 geometry.addAttribute( 'position', new THREE.BufferAttribute( meshData[ 'vertices' ], 3 ) );
476 if ( meshData[ 'colors' ] ) {
478 geometry.addAttribute( 'color', new THREE.BufferAttribute( meshData[ 'colors' ], 3 ) );
482 geometry.computeBoundingSphere();
488 if ( meshData[ 'colors' ] && 0 < meshData[ 'colors' ].length ) {
490 materialOpts[ 'vertexColors' ] = THREE.VertexColors;
494 materialOpts[ 'color' ] = 0xaaaaff;
498 var material = new THREE.MeshPhongMaterial( materialOpts );
499 return new THREE.Mesh( geometry, material );
503 function applyExtensions( extensions, meshData, modelXml, data3mf ) {
505 if ( ! extensions ) {
511 var availableExtensions = [];
512 var keys = Object.keys( extensions );
514 for ( var i = 0; i < keys.length; i++ ) {
518 for ( var j = 0; j < scope.availableExtensions.length; j++ ) {
520 var extension = scope.availableExtensions[ j ];
522 if ( extension.ns === ns ) {
524 availableExtensions.push( extension );
532 for ( var i = 0; i < availableExtensions.length; i++ ) {
534 var extension = availableExtensions[ i ];
535 extension.apply( modelXml, extensions[ extension[ 'ns' ] ], meshData );
541 function buildMeshes( data3mf ) {
543 var modelsData = data3mf.model;
545 var modelsKeys = Object.keys( modelsData );
547 for ( var i = 0; i < modelsKeys.length; i++ ) {
549 var modelsKey = modelsKeys[ i ];
550 var modelData = modelsData[ modelsKey ];
551 var modelXml = modelData[ 'xml' ];
552 var extensions = modelData[ 'extensions' ];
554 var objectIds = Object.keys( modelData[ 'resources' ][ 'object' ] );
556 for ( var j = 0; j < objectIds.length; j++ ) {
558 var objectId = objectIds[ j ];
559 var objectData = modelData[ 'resources' ][ 'object' ][ objectId ];
560 var meshData = objectData[ 'mesh' ];
561 applyExtensions( extensions, meshData, modelXml, data3mf );
562 meshes[ objectId ] = buildMesh( meshData, data3mf );
572 function build( meshes, refs, data3mf ) {
574 var group = new THREE.Group();
575 var buildData = data3mf.model[ refs[ 'target' ].substring( 1 ) ][ 'build' ];
577 for ( var i = 0; i < buildData.length; i++ ) {
579 var buildItem = buildData[ i ];
580 var mesh = meshes[ buildItem[ 'objectid' ] ];
582 if ( buildItem[ 'transform' ] ) {
584 mesh.geometry.applyMatrix( buildItem[ 'transform' ] );
596 var data3mf = loadDocument( data );
597 var meshes = buildMeshes( data3mf );
599 return build( meshes, data3mf[ 'rels' ], data3mf );
603 addExtension: function( extension ) {
605 this.availableExtensions.push( extension );