diff --git a/examples/files.js b/examples/files.js index e4154ac2737611334313dee5d9d242ce9ca6caa4..70ddde841ded3dcbe008c3a90f2de8df190e3330 100644 --- a/examples/files.js +++ b/examples/files.js @@ -75,6 +75,7 @@ var files = { "webgl_lines_sphere", "webgl_loader_3ds", "webgl_loader_3mf", + "webgl_loader_3mf_materials", "webgl_loader_amf", "webgl_loader_assimp", "webgl_loader_assimp2json", diff --git a/examples/js/loaders/3MFLoader.js b/examples/js/loaders/3MFLoader.js index 1921fa515ee2ca5de889332094a338bcd407481b..e330c29ec2fca2cbe26621759134e7771c721df9 100644 --- a/examples/js/loaders/3MFLoader.js +++ b/examples/js/loaders/3MFLoader.js @@ -205,7 +205,35 @@ THREE.ThreeMFLoader.prototype = { } - function parseBasematerialsNode( /* basematerialsNode */ ) { + 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 parseBasematerialNode( basematerialNode ) { + + var basematerialData = {}; + + basematerialData[ 'name' ] = basematerialNode.getAttribute( 'name' ); // required + basematerialData[ 'displaycolor' ] = basematerialNode.getAttribute( 'displaycolor' ); // required + + return basematerialData; } @@ -437,7 +465,7 @@ THREE.ThreeMFLoader.prototype = { if ( basematerialsNode ) { - resourcesData[ 'basematerial' ] = parseBasematerialsNode( basematerialsNode ); + resourcesData[ 'basematerials' ] = parseBasematerialsNode( basematerialsNode ); } @@ -514,39 +542,171 @@ THREE.ThreeMFLoader.prototype = { } - function buildMesh( meshData ) { + 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' ]; + + 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.addAttribute( 'color', new THREE.BufferAttribute( meshData[ 'colors' ], 3 ) ); + 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 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 ) { @@ -585,16 +745,42 @@ THREE.ThreeMFLoader.prototype = { } - function getBuild( data, objects, modelData, builder ) { + function getBuild( data, objects, modelData, objectData, builder ) { if ( data.build !== undefined ) return data.build; - data.build = builder( data, objects, modelData ); + 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(); @@ -634,22 +820,23 @@ THREE.ThreeMFLoader.prototype = { function buildObject( objectId, objects, modelData ) { var objectData = modelData[ 'resources' ][ 'object' ][ objectId ]; - var meshData = objectData[ 'mesh' ]; - if ( meshData ) { + 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, buildMesh ); + objects[ objectData.id ] = getBuild( meshData, objects, modelData, objectData, buildMesh ); } else { var compositeData = objectData[ 'components' ]; - objects[ objectData.id ] = getBuild( compositeData, objects, modelData, buildComposite ); + objects[ objectData.id ] = getBuild( compositeData, objects, modelData, objectData, buildComposite ); } diff --git a/examples/models/3mf/gear.3mf b/examples/models/3mf/gear.3mf deleted file mode 100644 index a627c9e3f87fc8bd2b4924a25738ec270cba07bc..0000000000000000000000000000000000000000 Binary files a/examples/models/3mf/gear.3mf and /dev/null differ diff --git a/examples/models/3mf/truck.3mf b/examples/models/3mf/truck.3mf new file mode 100755 index 0000000000000000000000000000000000000000..6fc1daffabf2f9acd16957e907f46615c73a767a Binary files /dev/null and b/examples/models/3mf/truck.3mf differ diff --git a/examples/webgl_loader_3mf_materials.html b/examples/webgl_loader_3mf_materials.html new file mode 100644 index 0000000000000000000000000000000000000000..eeaf5ffd1f19370d5f4e223952e5d252063f58c7 --- /dev/null +++ b/examples/webgl_loader_3mf_materials.html @@ -0,0 +1,158 @@ + + + + three.js webgl - 3MF + + + + + +
+ three.js + 3MF file with materials +
+ + + + + + + + + + + +