Skip to content

Commit 28af38c

Browse files
committed
update to latest obj2gltf
1 parent f0e5fbf commit 28af38c

4 files changed

Lines changed: 147 additions & 15 deletions

File tree

bin/obj23dtiles.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ var argv = yargs
157157
describe : 'Path to the emissive texture that should override textures in the .mtl file.',
158158
type : 'string',
159159
normalize : true
160+
},
161+
alphaTexture : {
162+
describe : 'Path to the alpha texture that should override textures in the .mtl file.'
160163
}
161164
})
162165
.command('combine', 'Combine tilesets in to one tileset.json.')
@@ -193,6 +196,7 @@ if (defined(argv.metallicRoughnessOcclusionTexture) && defined(argv.specularGlos
193196

194197
var objPath = argv.input;
195198
var outputPath = argv.output;
199+
196200
var name = path.basename(objPath, path.extname(objPath));
197201

198202
if (!defined(outputPath)) {
@@ -226,7 +230,8 @@ var overridingTextures = {
226230
occlusionTexture : argv.occlusionTexture,
227231
normalTexture : argv.normalTexture,
228232
baseColorTexture : argv.baseColorTexture,
229-
emissiveTexture : argv.emissiveTexture
233+
emissiveTexture : argv.emissiveTexture,
234+
alphaTexture : argv.alphaTexture
230235
};
231236

232237
var options = {

lib/loadMtl.js

Lines changed: 110 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ function loadMtl(mtlPath, options) {
4747
var overridingNormalTexture = overridingTextures.normalTexture;
4848
var overridingDiffuseTexture = overridingTextures.baseColorTexture;
4949
var overridingEmissiveTexture = overridingTextures.emissiveTexture;
50+
var overridingAlphaTexture = overridingTextures.alphaTexture;
5051

5152
// Textures that are packed into PBR textures need to be decoded first
5253
var decodeOptions = options.materialsCommon ? undefined : {
@@ -62,6 +63,9 @@ function loadMtl(mtlPath, options) {
6263
var specularShinessTextureOptions = defined(overridingSpecularShininessTexture) ? undefined : decodeOptions;
6364
var emissiveTextureOptions;
6465
var normalTextureOptions;
66+
var alphaTextureOptions = {
67+
decode : true
68+
};
6569

6670
function createMaterial(name) {
6771
material = new Material();
@@ -73,6 +77,7 @@ function loadMtl(mtlPath, options) {
7377
material.ambientTexture = overridingAmbientTexture;
7478
material.normalTexture = overridingNormalTexture;
7579
material.emissiveTexture = overridingEmissiveTexture;
80+
material.alphaTexture = overridingAlphaTexture;
7681
materials.push(material);
7782
}
7883

@@ -161,16 +166,30 @@ function loadMtl(mtlPath, options) {
161166
if (!defined(overridingNormalTexture)) {
162167
material.normalTexture = path.resolve(mtlDirectory, cleanTextureName(line.substring(9).trim()));
163168
}
169+
} else if (/^map_d /i.test(line)) {
170+
if (!defined(overridingAlphaTexture)) {
171+
material.alphaTexture = path.resolve(mtlDirectory, cleanTextureName(line.substring(6).trim()));
172+
}
164173
}
165174
}
166175

167176
function loadMaterialTextures(material) {
168-
loadMaterialTexture(material, 'diffuseTexture', diffuseTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options);
177+
// If an alpha texture is present the diffuse texture needs to be decoded so they can be packed together
178+
var diffuseAlphaTextureOptions = defined(material.alphaTexture) ? alphaTextureOptions : diffuseTextureOptions;
179+
180+
if (material.diffuseTexture === material.ambientTexture) {
181+
// OBJ models are often exported with the same texture in the diffuse and ambient slots but this is typically not desirable, particularly
182+
// when saving with PBR materials where the ambient texture is treated as the occlusion texture.
183+
material.ambientTexture = undefined;
184+
}
185+
186+
loadMaterialTexture(material, 'diffuseTexture', diffuseAlphaTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options);
169187
loadMaterialTexture(material, 'ambientTexture', ambientTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options);
170188
loadMaterialTexture(material, 'emissiveTexture', emissiveTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options);
171189
loadMaterialTexture(material, 'specularTexture', specularTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options);
172190
loadMaterialTexture(material, 'specularShininessTexture', specularShinessTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options);
173191
loadMaterialTexture(material, 'normalTexture', normalTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options);
192+
loadMaterialTexture(material, 'alphaTexture', alphaTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options);
174193
}
175194

176195
return readLines(mtlPath, parseLine)
@@ -205,6 +224,7 @@ function Material() {
205224
this.specularTexture = undefined; // map_Ks
206225
this.specularShininessTexture = undefined; // map_Ns
207226
this.normalTexture = undefined; // map_Bump
227+
this.alphaTexture = undefined; // map_d
208228
}
209229

210230
loadMtl.getDefaultMaterial = function(options) {
@@ -354,6 +374,68 @@ function getMinimumDimensions(textures, options) {
354374
return [width, height];
355375
}
356376

377+
function isChannelSingleColor(buffer) {
378+
var first = buffer.readUInt8(0);
379+
var length = buffer.length;
380+
for (var i = 1; i < length; ++i) {
381+
if (buffer[i] !== first) {
382+
return false;
383+
}
384+
}
385+
return true;
386+
}
387+
388+
function createDiffuseAlphaTexture(diffuseTexture, alphaTexture, options) {
389+
var packDiffuse = defined(diffuseTexture);
390+
var packAlpha = defined(alphaTexture);
391+
392+
if (!packDiffuse) {
393+
return undefined;
394+
}
395+
396+
if (!packAlpha) {
397+
return diffuseTexture;
398+
}
399+
400+
if (!defined(diffuseTexture.pixels) || !defined(alphaTexture.pixels)) {
401+
options.logger('Could not get decoded texture data for ' + diffuseTexture.path + ' or ' + alphaTexture.path + '. The material will be created without an alpha texture.');
402+
return diffuseTexture;
403+
}
404+
405+
var packedTextures = [diffuseTexture, alphaTexture];
406+
var dimensions = getMinimumDimensions(packedTextures, options);
407+
var width = dimensions[0];
408+
var height = dimensions[1];
409+
var pixelsLength = width * height;
410+
var pixels = Buffer.alloc(pixelsLength * 4, 0xFF); // Initialize with 4 channels
411+
var scratchChannel = Buffer.alloc(pixelsLength);
412+
413+
// Write into the R, G, B channels
414+
var redChannel = getTextureChannel(diffuseTexture, 0, width, height, scratchChannel);
415+
writeChannel(pixels, redChannel, 0);
416+
var greenChannel = getTextureChannel(diffuseTexture, 1, width, height, scratchChannel);
417+
writeChannel(pixels, greenChannel, 1);
418+
var blueChannel = getTextureChannel(diffuseTexture, 2, width, height, scratchChannel);
419+
writeChannel(pixels, blueChannel, 2);
420+
421+
// First try reading the alpha component from the alpha channel, but if it is a single color read from the red channel instead.
422+
var alphaChannel = getTextureChannel(alphaTexture, 3, width, height, scratchChannel);
423+
if (isChannelSingleColor(alphaChannel)) {
424+
alphaChannel = getTextureChannel(alphaTexture, 0, width, height, scratchChannel);
425+
}
426+
writeChannel(pixels, alphaChannel, 3);
427+
428+
var texture = new Texture();
429+
texture.name = diffuseTexture.name;
430+
texture.extension = '.png';
431+
texture.pixels = pixels;
432+
texture.width = width;
433+
texture.height = height;
434+
texture.transparent = true;
435+
436+
return texture;
437+
}
438+
357439
function createMetallicRoughnessTexture(metallicTexture, roughnessTexture, occlusionTexture, options) {
358440
if (defined(options.overridingTextures.metallicRoughnessOcclusionTexture)) {
359441
return metallicTexture;
@@ -499,9 +581,11 @@ function createSpecularGlossinessMaterial(material, options) {
499581
var normalTexture = material.normalTexture;
500582
var occlusionTexture = material.ambientTexture;
501583
var diffuseTexture = material.diffuseTexture;
584+
var alphaTexture = material.alphaTexture;
502585
var specularTexture = material.specularTexture;
503586
var glossinessTexture = material.specularShininessTexture;
504587
var specularGlossinessTexture = createSpecularGlossinessTexture(specularTexture, glossinessTexture, options);
588+
var diffuseAlphaTexture = createDiffuseAlphaTexture(diffuseTexture, alphaTexture, options);
505589

506590
var emissiveFactor = material.emissiveColor.slice(0, 3);
507591
var diffuseFactor = material.diffuseColor;
@@ -524,10 +608,15 @@ function createSpecularGlossinessMaterial(material, options) {
524608
glossinessFactor = 1.0;
525609
}
526610

527-
var alpha = material.alpha;
528-
diffuseFactor[3] = alpha;
611+
var transparent = false;
612+
if (defined(alphaTexture)) {
613+
transparent = true;
614+
} else {
615+
var alpha = material.alpha;
616+
diffuseFactor[3] = alpha;
617+
transparent = alpha < 1.0;
618+
}
529619

530-
var transparent = alpha < 1.0;
531620
if (defined(diffuseTexture)) {
532621
transparent = transparent || diffuseTexture.transparent;
533622
}
@@ -539,7 +628,7 @@ function createSpecularGlossinessMaterial(material, options) {
539628
name : material.name,
540629
extensions : {
541630
KHR_materials_pbrSpecularGlossiness: {
542-
diffuseTexture : diffuseTexture,
631+
diffuseTexture : diffuseAlphaTexture,
543632
specularGlossinessTexture : specularGlossinessTexture,
544633
diffuseFactor : diffuseFactor,
545634
specularFactor : specularFactor,
@@ -560,9 +649,11 @@ function createMetallicRoughnessMaterial(material, options) {
560649
var normalTexture = material.normalTexture;
561650
var occlusionTexture = material.ambientTexture;
562651
var baseColorTexture = material.diffuseTexture;
652+
var alphaTexture = material.alphaTexture;
563653
var metallicTexture = material.specularTexture;
564654
var roughnessTexture = material.specularShininessTexture;
565655
var metallicRoughnessTexture = createMetallicRoughnessTexture(metallicTexture, roughnessTexture, occlusionTexture, options);
656+
var diffuseAlphaTexture = createDiffuseAlphaTexture(baseColorTexture, alphaTexture, options);
566657

567658
if (options.packOcclusion) {
568659
occlusionTexture = metallicRoughnessTexture;
@@ -589,10 +680,15 @@ function createMetallicRoughnessMaterial(material, options) {
589680
roughnessFactor = 1.0;
590681
}
591682

592-
var alpha = material.alpha;
593-
baseColorFactor[3] = alpha;
683+
var transparent = false;
684+
if (defined(alphaTexture)) {
685+
transparent = true;
686+
} else {
687+
var alpha = material.alpha;
688+
baseColorFactor[3] = alpha;
689+
transparent = alpha < 1.0;
690+
}
594691

595-
var transparent = alpha < 1.0;
596692
if (defined(baseColorTexture)) {
597693
transparent = transparent || baseColorTexture.transparent;
598694
}
@@ -603,7 +699,7 @@ function createMetallicRoughnessMaterial(material, options) {
603699
return {
604700
name : material.name,
605701
pbrMetallicRoughness : {
606-
baseColorTexture : baseColorTexture,
702+
baseColorTexture : diffuseAlphaTexture,
607703
metallicRoughnessTexture : metallicRoughnessTexture,
608704
baseColorFactor : baseColorFactor,
609705
metallicFactor : metallicFactor,
@@ -647,8 +743,9 @@ function convertTraditionalToMetallicRoughness(material) {
647743
}
648744

649745
function createMaterialsCommonMaterial(material, options) {
746+
var diffuseAlphaTexture = createDiffuseAlphaTexture(material.diffuseTexture, material.alphaTexture, options);
650747
var ambient = defaultValue(material.ambientTexture, material.ambientColor);
651-
var diffuse = defaultValue(material.diffuseTexture, material.diffuseColor);
748+
var diffuse = defaultValue(diffuseAlphaTexture, material.diffuseColor);
652749
var emission = defaultValue(material.emissiveTexture, material.emissiveColor);
653750
var specular = defaultValue(material.specularTexture, material.specularColor);
654751

@@ -658,7 +755,9 @@ function createMaterialsCommonMaterial(material, options) {
658755

659756
var transparent;
660757
var transparency = 1.0;
661-
if (defined(material.diffuseTexture)) {
758+
if (defined(material.alphaTexture)) {
759+
transparent = true;
760+
} else if (defined(material.diffuseTexture)) {
662761
transparency = alpha;
663762
transparent = material.diffuseTexture.transparent || (transparency < 1.0);
664763
} else {

lib/loadObj.js

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -447,9 +447,15 @@ function loadObj(objPath, options) {
447447
positions.push(parseFloat(result[2]));
448448
positions.push(parseFloat(result[3]));
449449
} else if ((result = normalPattern.exec(line) ) !== null) {
450-
normals.push(parseFloat(result[1]));
451-
normals.push(parseFloat(result[2]));
452-
normals.push(parseFloat(result[3]));
450+
var normal = Cartesian3.fromElements(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]), scratchNormal);
451+
if (Cartesian3.equals(normal, Cartesian3.ZERO)) {
452+
Cartesian3.clone(Cartesian3.UNIT_Z, normal);
453+
} else {
454+
Cartesian3.normalize(normal, normal);
455+
}
456+
normals.push(normal.x);
457+
normals.push(normal.y);
458+
normals.push(normal.z);
453459
} else if ((result = uvPattern.exec(line)) !== null) {
454460
uvs.push(parseFloat(result[1]));
455461
uvs.push(1.0 - parseFloat(result[2])); // Flip y so 0.0 is the bottom of the image
@@ -525,6 +531,7 @@ function finishLoading(nodes, mtlPaths, objPath, options) {
525531
var name = path.basename(objPath, path.extname(objPath));
526532
return loadMtls(mtlPaths, objPath, options)
527533
.then(function(materials) {
534+
assignDefaultMaterial(nodes, materials);
528535
return {
529536
nodes : nodes,
530537
materials : materials,
@@ -578,6 +585,26 @@ function loadMtls(mtlPaths, objPath, options) {
578585
});
579586
}
580587

588+
function assignDefaultMaterial(nodes, materials) {
589+
if (materials.length === 0) {
590+
return;
591+
}
592+
var defaultMaterial = materials[0].name;
593+
var nodesLength = nodes.length;
594+
for (var i = 0; i < nodesLength; ++i) {
595+
var meshes = nodes[i].meshes;
596+
var meshesLength = meshes.length;
597+
for (var j = 0; j < meshesLength; ++j) {
598+
var primitives = meshes[j].primitives;
599+
var primitivesLength = primitives.length;
600+
for (var k = 0; k < primitivesLength; ++k) {
601+
var primitive = primitives[k];
602+
primitive.material = defaultValue(primitive.material, defaultMaterial);
603+
}
604+
}
605+
}
606+
}
607+
581608
function removeEmptyMeshes(meshes) {
582609
return meshes.filter(function(mesh) {
583610
// Remove empty primitives

lib/obj2gltf.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ module.exports = obj2gltf;
3333
* @param {String} [options.overridingTextures.normalTexture] Path to the normal texture.
3434
* @param {String} [options.overridingTextures.baseColorTexture] Path to the baseColor/diffuse texture.
3535
* @param {String} [options.overridingTextures.emissiveTexture] Path to the emissive texture.
36+
* @param {String} [options.overridingTextures.alphaTexture] Path to the alpha texture.
3637
* @param {Logger} [options.logger] A callback function for handling logged messages. Defaults to console.log.
3738
* @param {Writer} [options.writer] A callback function that writes files that are saved as separate resources.
3839
* @param {String} [options.outputDirectory] Output directory for writing separate resources when options.writer is not defined.

0 commit comments

Comments
 (0)