Skip to content

Commit 0e426de

Browse files
committed
3MFLoader: Added basic material support.
1 parent cfef70f commit 0e426de

File tree

5 files changed

+362
-18
lines changed

5 files changed

+362
-18
lines changed

examples/files.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ var files = {
7575
"webgl_lines_sphere",
7676
"webgl_loader_3ds",
7777
"webgl_loader_3mf",
78+
"webgl_loader_3mf_materials",
7879
"webgl_loader_amf",
7980
"webgl_loader_assimp",
8081
"webgl_loader_assimp2json",

examples/js/loaders/3MFLoader.js

Lines changed: 205 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,35 @@ THREE.ThreeMFLoader.prototype = {
205205

206206
}
207207

208-
function parseBasematerialsNode( /* basematerialsNode */ ) {
208+
function parseBasematerialsNode( basematerialsNode ) {
209+
210+
var basematerialsData = {
211+
id: basematerialsNode.getAttribute( 'id' ), // required
212+
basematerials: []
213+
};
214+
215+
var basematerialNodes = basematerialsNode.querySelectorAll( 'base' );
216+
217+
for ( var i = 0; i < basematerialNodes.length; i ++ ) {
218+
219+
var basematerialNode = basematerialNodes[ i ];
220+
var basematerialData = parseBasematerialNode( basematerialNode );
221+
basematerialsData.basematerials.push( basematerialData );
222+
223+
}
224+
225+
return basematerialsData;
226+
227+
}
228+
229+
function parseBasematerialNode( basematerialNode ) {
230+
231+
var basematerialData = {};
232+
233+
basematerialData[ 'name' ] = basematerialNode.getAttribute( 'name' ); // required
234+
basematerialData[ 'displaycolor' ] = basematerialNode.getAttribute( 'displaycolor' ); // required
235+
236+
return basematerialData;
209237

210238
}
211239

@@ -437,7 +465,7 @@ THREE.ThreeMFLoader.prototype = {
437465

438466
if ( basematerialsNode ) {
439467

440-
resourcesData[ 'basematerial' ] = parseBasematerialsNode( basematerialsNode );
468+
resourcesData[ 'basematerials' ] = parseBasematerialsNode( basematerialsNode );
441469

442470
}
443471

@@ -514,39 +542,171 @@ THREE.ThreeMFLoader.prototype = {
514542

515543
}
516544

517-
function buildMesh( meshData ) {
545+
function buildMesh( meshData, objects, modelData, objectData ) {
546+
547+
// geometry
518548

519549
var geometry = new THREE.BufferGeometry();
520550
geometry.setIndex( new THREE.BufferAttribute( meshData[ 'triangles' ], 1 ) );
521551
geometry.addAttribute( 'position', new THREE.BufferAttribute( meshData[ 'vertices' ], 3 ) );
522552

523-
if ( meshData[ 'colors' ] ) {
553+
// groups
554+
555+
var basematerialsData = modelData[ 'resources' ][ 'basematerials' ];
556+
var triangleProperties = meshData[ 'triangleProperties' ];
557+
558+
var start = 0;
559+
var count = 0;
560+
var currentMaterialIndex = - 1;
561+
562+
for ( var i = 0, l = triangleProperties.length; i < l; i ++ ) {
563+
564+
var triangleProperty = triangleProperties[ i ];
565+
var pid = triangleProperty.pid;
566+
567+
// only proceed if the triangle refers to a basematerials definition
568+
569+
if ( basematerialsData && ( basematerialsData.id === pid ) ) {
570+
571+
if ( currentMaterialIndex === - 1 ) currentMaterialIndex = triangleProperty.p1;
572+
573+
if ( currentMaterialIndex === triangleProperty.p1 ) {
574+
575+
count += 3; // primitves per triangle
576+
577+
} else {
524578

525-
geometry.addAttribute( 'color', new THREE.BufferAttribute( meshData[ 'colors' ], 3 ) );
579+
geometry.addGroup( start, count, currentMaterialIndex );
580+
581+
start += count;
582+
count = 3;
583+
currentMaterialIndex = triangleProperty.p1;
584+
585+
}
586+
587+
}
526588

527589
}
528590

591+
if ( geometry.groups.length > 0 ) mergeGroups( geometry );
592+
529593
geometry.computeBoundingSphere();
530594

531-
var materialOpts = {
532-
flatShading: true
533-
};
595+
// material
596+
597+
var material;
598+
599+
if ( basematerialsData && ( basematerialsData.id === objectData.pid ) ) {
600+
601+
var materialIndex = objectData.pindex;
602+
var basematerialData = basematerialsData.basematerials[ materialIndex ];
603+
604+
material = getBuild( basematerialData, objects, modelData, objectData, buildBasematerial );
605+
606+
} else if ( geometry.groups.length > 0 ) {
607+
608+
var groups = geometry.groups;
609+
material = [];
534610

535-
if ( meshData[ 'colors' ] && 0 < meshData[ 'colors' ].length ) {
611+
for ( var i = 0, l = groups.length; i < l; i ++ ) {
536612

537-
materialOpts[ 'vertexColors' ] = THREE.VertexColors;
613+
var group = groups[ i ];
614+
var basematerialData = basematerialsData.basematerials[ group.materialIndex ];
615+
616+
material.push( getBuild( basematerialData, objects, modelData, objectData, buildBasematerial ) );
617+
618+
}
538619

539620
} else {
540621

541-
materialOpts[ 'color' ] = 0xaaaaff;
622+
// default material
623+
624+
material = new THREE.MeshPhongMaterial( { color: 0xaaaaff, flatShading: true } );
542625

543626
}
544627

545-
var material = new THREE.MeshPhongMaterial( materialOpts );
546628
return new THREE.Mesh( geometry, material );
547629

548630
}
549631

632+
function mergeGroups( geometry ) {
633+
634+
// sort by material index
635+
636+
var groups = geometry.groups.sort( function ( a, b ) {
637+
638+
if ( a.materialIndex !== b.materialIndex ) return a.materialIndex - b.materialIndex;
639+
640+
return a.start - b.start;
641+
642+
} );
643+
644+
// reorganize index buffer
645+
646+
var index = geometry.index;
647+
648+
var itemSize = index.itemSize;
649+
var srcArray = index.array;
650+
651+
var targetOffset = 0;
652+
653+
var targetArray = new srcArray.constructor( srcArray.length );
654+
655+
for ( var i = 0; i < groups.length; i ++ ) {
656+
657+
var group = groups[ i ];
658+
659+
var groupLength = group.count * itemSize;
660+
var groupStart = group.start * itemSize;
661+
662+
var sub = srcArray.subarray( groupStart, groupStart + groupLength );
663+
664+
targetArray.set( sub, targetOffset );
665+
666+
targetOffset += groupLength;
667+
668+
}
669+
670+
srcArray.set( targetArray );
671+
672+
// update groups
673+
674+
var start = 0;
675+
676+
for ( i = 0; i < groups.length; i ++ ) {
677+
678+
group = groups[ i ];
679+
680+
group.start = start;
681+
start += group.count;
682+
683+
}
684+
685+
// merge groups
686+
687+
var lastGroup = groups[ 0 ];
688+
689+
geometry.groups = [ lastGroup ];
690+
691+
for ( i = 1; i < groups.length; i ++ ) {
692+
693+
group = groups[ i ];
694+
695+
if ( lastGroup.materialIndex === group.materialIndex ) {
696+
697+
lastGroup.count += group.count;
698+
699+
} else {
700+
701+
lastGroup = group;
702+
geometry.groups.push( lastGroup );
703+
704+
}
705+
706+
}
707+
708+
}
709+
550710
function applyExtensions( extensions, meshData, modelXml ) {
551711

552712
if ( ! extensions ) {
@@ -585,16 +745,42 @@ THREE.ThreeMFLoader.prototype = {
585745

586746
}
587747

588-
function getBuild( data, objects, modelData, builder ) {
748+
function getBuild( data, objects, modelData, objectData, builder ) {
589749

590750
if ( data.build !== undefined ) return data.build;
591751

592-
data.build = builder( data, objects, modelData );
752+
data.build = builder( data, objects, modelData, objectData );
593753

594754
return data.build;
595755

596756
}
597757

758+
function buildBasematerial( materialData ) {
759+
760+
var material = new THREE.MeshPhongMaterial( { flatShading: true } );
761+
762+
material.name = materialData.name;
763+
764+
// displaycolor MUST be specified with a value of a 6 or 8 digit hexadecimal number, e.g. "#RRGGBB" or "#RRGGBBAA"
765+
766+
var displaycolor = materialData.displaycolor;
767+
768+
var color = displaycolor.substring( 0, 7 );
769+
material.color.setStyle( color );
770+
material.color.convertSRGBToLinear(); // displaycolor is in sRGB
771+
772+
// process alpha if set
773+
774+
if ( displaycolor.length === 9 ) {
775+
776+
material.opacity = parseInt( displaycolor.charAt( 7 ) + displaycolor.charAt( 8 ), 16 ) / 255;
777+
778+
}
779+
780+
return material;
781+
782+
}
783+
598784
function buildComposite( compositeData, objects, modelData ) {
599785

600786
var composite = new THREE.Group();
@@ -634,22 +820,23 @@ THREE.ThreeMFLoader.prototype = {
634820
function buildObject( objectId, objects, modelData ) {
635821

636822
var objectData = modelData[ 'resources' ][ 'object' ][ objectId ];
637-
var meshData = objectData[ 'mesh' ];
638823

639-
if ( meshData ) {
824+
if ( objectData[ 'mesh' ] ) {
825+
826+
var meshData = objectData[ 'mesh' ];
640827

641828
var extensions = modelData[ 'extensions' ];
642829
var modelXml = modelData[ 'xml' ];
643830

644831
applyExtensions( extensions, meshData, modelXml );
645832

646-
objects[ objectData.id ] = getBuild( meshData, objects, modelData, buildMesh );
833+
objects[ objectData.id ] = getBuild( meshData, objects, modelData, objectData, buildMesh );
647834

648835
} else {
649836

650837
var compositeData = objectData[ 'components' ];
651838

652-
objects[ objectData.id ] = getBuild( compositeData, objects, modelData, buildComposite );
839+
objects[ objectData.id ] = getBuild( compositeData, objects, modelData, objectData, buildComposite );
653840

654841
}
655842

examples/models/3mf/gear.3mf

-127 KB
Binary file not shown.

examples/models/3mf/truck.3mf

2.53 MB
Binary file not shown.

0 commit comments

Comments
 (0)