From 378479b8f1989d93b01af6051ad667259ed54ada Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 3 Jan 2018 20:41:25 -0500 Subject: [PATCH] Added alpha texture support --- CHANGES.md | 3 +- README.md | 1 + bin/obj2gltf.js | 6 +- lib/loadMtl.js | 115 ++++++++++++++++-- lib/obj2gltf.js | 1 + .../alpha.png | Bin .../box-complex-material-alpha/ambient.gif | Bin 0 -> 4040 bytes .../box-complex-material-alpha.mtl | 20 +++ .../box-complex-material-alpha.obj | 46 +++++++ .../data/box-complex-material-alpha/bump.png | Bin 0 -> 4998 bytes .../box-complex-material-alpha/diffuse.png | Bin 0 -> 8889 bytes .../box-complex-material-alpha/emission.jpg | Bin 0 -> 7092 bytes .../box-complex-material-alpha/shininess.png | Bin 0 -> 4094 bytes .../box-complex-material-alpha/specular.jpeg | Bin 0 -> 5618 bytes .../box-complex-material.mtl | 1 - specs/data/box-texture-options/alpha.png | Bin 2255 -> 0 bytes .../box-texture-options.mtl | 1 - specs/lib/loadMtlSpec.js | 84 ++++++++++++- specs/lib/loadTextureSpec.js | 2 +- specs/lib/obj2gltfSpec.js | 6 +- 20 files changed, 266 insertions(+), 20 deletions(-) rename specs/data/{box-complex-material => box-complex-material-alpha}/alpha.png (100%) create mode 100644 specs/data/box-complex-material-alpha/ambient.gif create mode 100644 specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl create mode 100644 specs/data/box-complex-material-alpha/box-complex-material-alpha.obj create mode 100644 specs/data/box-complex-material-alpha/bump.png create mode 100644 specs/data/box-complex-material-alpha/diffuse.png create mode 100644 specs/data/box-complex-material-alpha/emission.jpg create mode 100644 specs/data/box-complex-material-alpha/shininess.png create mode 100644 specs/data/box-complex-material-alpha/specular.jpeg delete mode 100644 specs/data/box-texture-options/alpha.png diff --git a/CHANGES.md b/CHANGES.md index b62fd97..c46b038 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,7 +3,8 @@ Change Log ### 2.2.0 ??? -* Fixed handling of `usemtl` when appearing before an `o` or `g` token. [#121](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/121) +* Added ability to load alpha textures. [#124](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/124) +* Fixed handling of `usemtl` when appearing before an `o` or `g` token. [#123](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/123) ### 2.1.0 2017-12-28 diff --git a/README.md b/README.md index 260621e..bfdd168 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ map_Bump | normal texture | normal texture | normal texture |`--normalTexture`|Path to the normal texture that should override textures in the .mtl file.|No| |`--baseColorTexture`|Path to the baseColor/diffuse texture that should override textures in the .mtl file.|No| |`--emissiveTexture`|Path to the emissive texture that should override textures in the .mtl file.|No| +|`--alphaTexture`|Path to the alpha texture that should override textures in the .mtl file.|No| ## Build Instructions diff --git a/bin/obj2gltf.js b/bin/obj2gltf.js index 58abdd9..ed83f9d 100644 --- a/bin/obj2gltf.js +++ b/bin/obj2gltf.js @@ -108,6 +108,9 @@ var argv = yargs describe : 'Path to the emissive texture that should override textures in the .mtl file.', type : 'string', normalize : true + }, + alphaTexture : { + describe : 'Path to the alpha texture that should override textures in the .mtl file.' } }).parse(args); @@ -143,7 +146,8 @@ var overridingTextures = { occlusionTexture : argv.occlusionTexture, normalTexture : argv.normalTexture, baseColorTexture : argv.baseColorTexture, - emissiveTexture : argv.emissiveTexture + emissiveTexture : argv.emissiveTexture, + alphaTexture : argv.alphaTexture }; var options = { diff --git a/lib/loadMtl.js b/lib/loadMtl.js index c2cfb62..fc7189a 100644 --- a/lib/loadMtl.js +++ b/lib/loadMtl.js @@ -47,6 +47,7 @@ function loadMtl(mtlPath, options) { var overridingNormalTexture = overridingTextures.normalTexture; var overridingDiffuseTexture = overridingTextures.baseColorTexture; var overridingEmissiveTexture = overridingTextures.emissiveTexture; + var overridingAlphaTexture = overridingTextures.alphaTexture; // Textures that are packed into PBR textures need to be decoded first var decodeOptions = options.materialsCommon ? undefined : { @@ -62,6 +63,9 @@ function loadMtl(mtlPath, options) { var specularShinessTextureOptions = defined(overridingSpecularShininessTexture) ? undefined : decodeOptions; var emissiveTextureOptions; var normalTextureOptions; + var alphaTextureOptions = { + decode : true + }; function createMaterial(name) { material = new Material(); @@ -73,6 +77,7 @@ function loadMtl(mtlPath, options) { material.ambientTexture = overridingAmbientTexture; material.normalTexture = overridingNormalTexture; material.emissiveTexture = overridingEmissiveTexture; + material.alphaTexture = overridingAlphaTexture; materials.push(material); } @@ -161,16 +166,24 @@ function loadMtl(mtlPath, options) { if (!defined(overridingNormalTexture)) { material.normalTexture = path.resolve(mtlDirectory, cleanTextureName(line.substring(9).trim())); } + } else if (/^map_d /i.test(line)) { + if (!defined(overridingAlphaTexture)) { + material.alphaTexture = path.resolve(mtlDirectory, cleanTextureName(line.substring(6).trim())); + } } } function loadMaterialTextures(material) { - loadMaterialTexture(material, 'diffuseTexture', diffuseTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options); + // If an alpha texture is present the diffuse texture needs to be decoded so they can be packed together + var diffuseAlphaTextureOptions = defined(material.alphaTexture) ? alphaTextureOptions : diffuseTextureOptions; + + loadMaterialTexture(material, 'diffuseTexture', diffuseAlphaTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options); loadMaterialTexture(material, 'ambientTexture', ambientTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options); loadMaterialTexture(material, 'emissiveTexture', emissiveTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options); loadMaterialTexture(material, 'specularTexture', specularTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options); loadMaterialTexture(material, 'specularShininessTexture', specularShinessTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options); loadMaterialTexture(material, 'normalTexture', normalTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options); + loadMaterialTexture(material, 'alphaTexture', alphaTextureOptions, mtlDirectory, texturePromiseMap, texturePromises, options); } return readLines(mtlPath, parseLine) @@ -205,6 +218,7 @@ function Material() { this.specularTexture = undefined; // map_Ks this.specularShininessTexture = undefined; // map_Ns this.normalTexture = undefined; // map_Bump + this.alphaTexture = undefined; // map_d } loadMtl.getDefaultMaterial = function(options) { @@ -354,6 +368,68 @@ function getMinimumDimensions(textures, options) { return [width, height]; } +function isChannelSingleColor(buffer) { + var first = buffer.readUInt8(0); + var length = buffer.length; + for (var i = 1; i < length; ++i) { + if (buffer[i] !== first) { + return false; + } + } + return true; +} + +function createDiffuseAlphaTexture(diffuseTexture, alphaTexture, options) { + var packDiffuse = defined(diffuseTexture); + var packAlpha = defined(alphaTexture); + + if (!packDiffuse) { + return undefined; + } + + if (!packAlpha) { + return diffuseTexture; + } + + if (!defined(diffuseTexture.pixels) || !defined(alphaTexture.pixels)) { + options.logger('Could not get decoded texture data for ' + diffuseTexture.path + ' or ' + alphaTexture.path + '. The material will be created without an alpha texture.'); + return diffuseTexture; + } + + var packedTextures = [diffuseTexture, alphaTexture]; + var dimensions = getMinimumDimensions(packedTextures, options); + var width = dimensions[0]; + var height = dimensions[1]; + var pixelsLength = width * height; + var pixels = Buffer.alloc(pixelsLength * 4, 0xFF); // Initialize with 4 channels + var scratchChannel = Buffer.alloc(pixelsLength); + + // Write into the R, G, B channels + var redChannel = getTextureChannel(diffuseTexture, 0, width, height, scratchChannel); + writeChannel(pixels, redChannel, 0); + var greenChannel = getTextureChannel(diffuseTexture, 1, width, height, scratchChannel); + writeChannel(pixels, greenChannel, 1); + var blueChannel = getTextureChannel(diffuseTexture, 2, width, height, scratchChannel); + writeChannel(pixels, blueChannel, 2); + + // First try reading the alpha component from the alpha channel, but if it is a single color read from the red channel instead. + var alphaChannel = getTextureChannel(alphaTexture, 3, width, height, scratchChannel); + if (isChannelSingleColor(alphaChannel)) { + alphaChannel = getTextureChannel(alphaTexture, 0, width, height, scratchChannel); + } + writeChannel(pixels, alphaChannel, 3); + + var texture = new Texture(); + texture.name = diffuseTexture.name; + texture.extension = '.png'; + texture.pixels = pixels; + texture.width = width; + texture.height = height; + texture.transparent = true; + + return texture; +} + function createMetallicRoughnessTexture(metallicTexture, roughnessTexture, occlusionTexture, options) { if (defined(options.overridingTextures.metallicRoughnessOcclusionTexture)) { return metallicTexture; @@ -499,9 +575,11 @@ function createSpecularGlossinessMaterial(material, options) { var normalTexture = material.normalTexture; var occlusionTexture = material.ambientTexture; var diffuseTexture = material.diffuseTexture; + var alphaTexture = material.alphaTexture; var specularTexture = material.specularTexture; var glossinessTexture = material.specularShininessTexture; var specularGlossinessTexture = createSpecularGlossinessTexture(specularTexture, glossinessTexture, options); + var diffuseAlphaTexture = createDiffuseAlphaTexture(diffuseTexture, alphaTexture, options); var emissiveFactor = material.emissiveColor.slice(0, 3); var diffuseFactor = material.diffuseColor; @@ -524,10 +602,15 @@ function createSpecularGlossinessMaterial(material, options) { glossinessFactor = 1.0; } - var alpha = material.alpha; - diffuseFactor[3] = alpha; + var transparent = false; + if (defined(alphaTexture)) { + transparent = true; + } else { + var alpha = material.alpha; + diffuseFactor[3] = alpha; + transparent = alpha < 1.0; + } - var transparent = alpha < 1.0; if (defined(diffuseTexture)) { transparent = transparent || diffuseTexture.transparent; } @@ -539,7 +622,7 @@ function createSpecularGlossinessMaterial(material, options) { name : material.name, extensions : { KHR_materials_pbrSpecularGlossiness: { - diffuseTexture : diffuseTexture, + diffuseTexture : diffuseAlphaTexture, specularGlossinessTexture : specularGlossinessTexture, diffuseFactor : diffuseFactor, specularFactor : specularFactor, @@ -560,9 +643,11 @@ function createMetallicRoughnessMaterial(material, options) { var normalTexture = material.normalTexture; var occlusionTexture = material.ambientTexture; var baseColorTexture = material.diffuseTexture; + var alphaTexture = material.alphaTexture; var metallicTexture = material.specularTexture; var roughnessTexture = material.specularShininessTexture; var metallicRoughnessTexture = createMetallicRoughnessTexture(metallicTexture, roughnessTexture, occlusionTexture, options); + var diffuseAlphaTexture = createDiffuseAlphaTexture(baseColorTexture, alphaTexture, options); if (options.packOcclusion) { occlusionTexture = metallicRoughnessTexture; @@ -589,10 +674,15 @@ function createMetallicRoughnessMaterial(material, options) { roughnessFactor = 1.0; } - var alpha = material.alpha; - baseColorFactor[3] = alpha; + var transparent = false; + if (defined(alphaTexture)) { + transparent = true; + } else { + var alpha = material.alpha; + baseColorFactor[3] = alpha; + transparent = alpha < 1.0; + } - var transparent = alpha < 1.0; if (defined(baseColorTexture)) { transparent = transparent || baseColorTexture.transparent; } @@ -603,7 +693,7 @@ function createMetallicRoughnessMaterial(material, options) { return { name : material.name, pbrMetallicRoughness : { - baseColorTexture : baseColorTexture, + baseColorTexture : diffuseAlphaTexture, metallicRoughnessTexture : metallicRoughnessTexture, baseColorFactor : baseColorFactor, metallicFactor : metallicFactor, @@ -647,8 +737,9 @@ function convertTraditionalToMetallicRoughness(material) { } function createMaterialsCommonMaterial(material, options) { + var diffuseAlphaTexture = createDiffuseAlphaTexture(material.diffuseTexture, material.alphaTexture, options); var ambient = defaultValue(material.ambientTexture, material.ambientColor); - var diffuse = defaultValue(material.diffuseTexture, material.diffuseColor); + var diffuse = defaultValue(diffuseAlphaTexture, material.diffuseColor); var emission = defaultValue(material.emissiveTexture, material.emissiveColor); var specular = defaultValue(material.specularTexture, material.specularColor); @@ -658,7 +749,9 @@ function createMaterialsCommonMaterial(material, options) { var transparent; var transparency = 1.0; - if (defined(material.diffuseTexture)) { + if (defined(material.alphaTexture)) { + transparent = true; + } else if (defined(material.diffuseTexture)) { transparency = alpha; transparent = material.diffuseTexture.transparent || (transparency < 1.0); } else { diff --git a/lib/obj2gltf.js b/lib/obj2gltf.js index 3dcd092..d7858ab 100644 --- a/lib/obj2gltf.js +++ b/lib/obj2gltf.js @@ -33,6 +33,7 @@ module.exports = obj2gltf; * @param {String} [options.overridingTextures.normalTexture] Path to the normal texture. * @param {String} [options.overridingTextures.baseColorTexture] Path to the baseColor/diffuse texture. * @param {String} [options.overridingTextures.emissiveTexture] Path to the emissive texture. + * @param {String} [options.overridingTextures.alphaTexture] Path to the alpha texture. * @param {Logger} [options.logger] A callback function for handling logged messages. Defaults to console.log. * @param {Writer} [options.writer] A callback function that writes files that are saved as separate resources. * @param {String} [options.outputDirectory] Output directory for writing separate resources when options.writer is not defined. diff --git a/specs/data/box-complex-material/alpha.png b/specs/data/box-complex-material-alpha/alpha.png similarity index 100% rename from specs/data/box-complex-material/alpha.png rename to specs/data/box-complex-material-alpha/alpha.png diff --git a/specs/data/box-complex-material-alpha/ambient.gif b/specs/data/box-complex-material-alpha/ambient.gif new file mode 100644 index 0000000000000000000000000000000000000000..1276823d350c3a7939ce8021a937381ee08564ae GIT binary patch literal 4040 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl b/specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl new file mode 100644 index 0000000..5c598ff --- /dev/null +++ b/specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl @@ -0,0 +1,20 @@ +# Blender MTL File: 'None' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.200000 0.200000 0.200000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.100000 0.100000 0.100000 +Ni 1.000000 +d 0.900000 +Tr 0.100000 +map_Ka ambient.gif +map_Ke emission.jpg +map_Kd diffuse.png +map_Ks specular.jpeg +map_Ns shininess.png +map_Bump bump.png +map_d alpha.png +illum 2 diff --git a/specs/data/box-complex-material-alpha/box-complex-material-alpha.obj b/specs/data/box-complex-material-alpha/box-complex-material-alpha.obj new file mode 100644 index 0000000..15d7f4a --- /dev/null +++ b/specs/data/box-complex-material-alpha/box-complex-material-alpha.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: '' +# www.blender.org +mtllib box-complex-material-alpha.mtl +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +usemtl Material +s off +f 1/1/1 2/2/1 4/3/1 3/4/1 +f 3/5/2 4/6/2 8/7/2 7/8/2 +f 7/9/3 8/10/3 6/11/3 5/12/3 +f 5/13/4 6/14/4 2/15/4 1/16/4 +f 3/5/5 7/17/5 5/18/5 1/16/5 +f 8/19/6 4/6/6 2/15/6 6/20/6 diff --git a/specs/data/box-complex-material-alpha/bump.png b/specs/data/box-complex-material-alpha/bump.png new file mode 100644 index 0000000000000000000000000000000000000000..16ec3a936601922682f2efadd6f19a39d80c7150 GIT binary patch literal 4998 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/diffuse.png b/specs/data/box-complex-material-alpha/diffuse.png new file mode 100644 index 0000000000000000000000000000000000000000..8704966c032736dd3b504891393c5a69e90f0a7b GIT binary patch literal 8889 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 Hu0mjfJUwN5 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/emission.jpg b/specs/data/box-complex-material-alpha/emission.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e5c85a943f6c5c95ca46b1bfd5fc9e8d7f1c4b77 GIT binary patch literal 7092 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/shininess.png b/specs/data/box-complex-material-alpha/shininess.png new file mode 100644 index 0000000000000000000000000000000000000000..bfcf1a2c625c38a8da19b7c7390102dde2b3f90a GIT binary patch literal 4094 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 literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material-alpha/specular.jpeg b/specs/data/box-complex-material-alpha/specular.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..661bf98060289f196f149b5c0b6b05ff737f9ff8 GIT binary patch literal 5618 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 IJS9*353{Fhng9R* literal 0 HcmV?d00001 diff --git a/specs/data/box-complex-material/box-complex-material.mtl b/specs/data/box-complex-material/box-complex-material.mtl index 5c598ff..1ab3601 100644 --- a/specs/data/box-complex-material/box-complex-material.mtl +++ b/specs/data/box-complex-material/box-complex-material.mtl @@ -16,5 +16,4 @@ map_Kd diffuse.png map_Ks specular.jpeg map_Ns shininess.png map_Bump bump.png -map_d alpha.png illum 2 diff --git a/specs/data/box-texture-options/alpha.png b/specs/data/box-texture-options/alpha.png deleted file mode 100644 index d04eb82f52c40bc1af9a3a8f516c0de93da592d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2255 zcmV;=2r&1FP)FfK>5mnqYvqeW=j14KyJn zTA*voO{Lw0+M=4J2`y=E&Bb<#ofyaW*sbk%ACRVTF7~l~F7Jc<|JKM({mXMM?|XdS z=KvH^NFjw3qCyz?esyuF*=#l$OY}O1B;v7XI1~y+;;0~1jV;ZFk}^wWWo3Dp>HD9B zgMOdS7YN3dXQo{~`XR>EeXXWyTkAE#FY#P*dZQC(Ct~zMx&}6s+B!{QUK$#8hV3UW zQV7v+y0gaGX3pjR$iIho_-xn*-D1Ly* zk-Li&W^4-+d9t)wui!4;{wa|!M^-6S73&{}ym`Dvk$rr?L*&lfrjl$wR@Mja)la-> zlKqcmxl1#*B$5GFGJa;aBPv+7WAaBqo>T`sLRjAqpmWs9Pq-t)oQQ zoZBV`L;Hw>q!e|e$fZ5fJ4#+zQrJOAig=&bN$e>9eQR{IM(~5g4Ca{P;(mUS+^(+q+!$ys>Zu^K?;a<(gZ5U+u_Y0QhljR{Yy2T zc7MJQ^Gp>D7o8j((ey9SWSR^PQ{e#HX3nkyP!v+#@$aQvAZ0K7j3x&#&}cY$H3!I^y+uG- z*36FNu!GDK`ZCv-bwzA1brXiRijZ>a@kn+jME$P^fI0^-u`H7r$xgw%rW0X4im5{HE191r(>wBeo)jhiG=dpiX2(m~|smL!fO+ zTt$liOt%AG){3jhmVZ$Wfxfe1Ao`JZ+94y?irKVoq22*K+eB1EGuc8t1TJ=FuE?zj z9MG{nb49*hhX6wCeVHn<8G{3u^&2w{8vowJppewRlYY}`9@hhk1iD;lD{`Pqg9D?hm47uHe z2x9rUAf(9xm{L_Q1le7O3evPo2yz>;itG@8Sd4%xW-~wJMk_K%Q!hVcrwtjTZGazw z%>mHJ3#qlBgH%@YLDtu!gRET32kBpl4$}S;K1hQG9YnK&2ck0qzLaXY5Nj1eNNo)l z(y{^}#Af3{I+_te+E#HP4Jh}KIxfTj7-QfDi&ehLl-QLdr|o5DQL7 zB^OeT5>jFQkyL~WF`IGjBPKSav=rx2%gBb5AU%&5*pOnt89f_PgcPDauq2g!G^?P}AtpA6cnMDT~`wt&v*o6)<`wbst5#MOb=i`IG z96{zF4&a3x!*)uX_?#bd+KugWXy=E7BG^V+5q=1Kh-X6NbR6crKiJ5B%VttTk2|H=Z?CKU~4esz~Y> zmPyH#k0D(}pkW%xx1xbHo^%xf*9ja$;N%HU`ij8DGbkWe`_4hyiolmcC}tvdmV@W( z^=B|Zm}faZ;l5^UHT{tDoG#PcijYAB72$qHp2h3BA?nxWe|0@%t_UO^puQmYgBS>$ zJ4(6Z5Kl`W-o6MOqjW=>c`o+*g2Cq z^rFCZUBMQhA;;DV>dL`PJU$IsLckHdiMpGb{rf(HtRXPg+Cv-UFR$fvZKU-qRV{K; zcRqon1!y*E!t@eCDBMy5J49C$M>8;Q4jDR4M?GMzv&uYU=4s4sDKM>NS_P_ey%#zWHEW zmGj{8J0d%IihV-mmBYGDqta7G!gyYOLh>&)HTO*^{yfCd-^ua^8W>V|yE=WNOr`F6 zH>F@X5^u@(jZ@_#i;78H_sjWcHeLTW3OVe(GOr%4e@=-RO6+O*$K1ue@;|(1YJ(yZ zQ_EhN9SGEB0x(}X}s3D7_lh-x< z-Imt%qQ)2g;pxEeX$ddRNIeTTv|QU%TG!H^rr-JHv1w0uW@JX%PAsxqPAIoF)*FoF z71h<%mF3?Th=lw;uh+ZeUtV@O-7>Ugkny^RzQklQ84blnT81RA#$wTEB(i)33Mr(J dLJ9$Z{{r)zV|143F1i2!002ovPDHLkV1iPOEQ|mE diff --git a/specs/data/box-texture-options/box-texture-options.mtl b/specs/data/box-texture-options/box-texture-options.mtl index 76f464a..0d45c3b 100644 --- a/specs/data/box-texture-options/box-texture-options.mtl +++ b/specs/data/box-texture-options/box-texture-options.mtl @@ -16,5 +16,4 @@ map_Kd -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 diffuse.png map_Ks -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 specular.jpeg map_Ns -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 shininess.png map_Bump -bm 0.2 bump.png -map_d alpha.png illum 2 diff --git a/specs/lib/loadMtlSpec.js b/specs/lib/loadMtlSpec.js index f491e90..5e9dcaa 100644 --- a/specs/lib/loadMtlSpec.js +++ b/specs/lib/loadMtlSpec.js @@ -20,7 +20,7 @@ var transparentMaterialPath = 'specs/data/box-transparent/box-transparent.mtl'; var diffuseTexturePath = 'specs/data/box-textured/cesium.png'; var transparentDiffuseTexturePath = 'specs/data/box-complex-material/diffuse.png'; -var alphaTexturePath = 'specs/data/box-complex-material/alpha.png'; +var alphaTexturePath = 'specs/data/box-complex-material-alpha/alpha.png'; var ambientTexturePath = 'specs/data/box-complex-material/ambient.gif'; var normalTexturePath = 'specs/data/box-complex-material/bump.png'; var emissiveTexturePath = 'specs/data/box-complex-material/emission.jpg'; @@ -48,7 +48,7 @@ var options; describe('loadMtl', function() { beforeAll(function(done) { return Promise.all([ - loadTexture(diffuseTexturePath) + loadTexture(diffuseTexturePath, decodeOptions) .then(function(texture) { diffuseTexture = texture; }), @@ -333,6 +333,33 @@ describe('loadMtl', function() { expect(material.alphaMode).toBe('BLEND'); expect(material.doubleSided).toBe(true); }); + + it('packs alpha texture in base color texture', function() { + options.metallicRoughness = true; + + var material = loadMtl._createMaterial({ + diffuseTexture : diffuseTexture, + alphaTexture : alphaTexture + }, options); + + var pbr = material.pbrMetallicRoughness; + expect(pbr.baseColorTexture).toBeDefined(); + + var hasBlack = false; + var hasWhite = false; + var pixels = pbr.baseColorTexture.pixels; + var pixelsLength = pixels.length / 4; + for (var i = 0; i < pixelsLength; ++i) { + var alpha = pixels[i * 4 + 3]; + hasBlack = hasBlack || (alpha === 0); + hasWhite = hasWhite || (alpha === 255); + } + expect(hasBlack).toBe(true); + expect(hasWhite).toBe(true); + expect(pbr.baseColorFactor[3]).toEqual(1); + expect(material.alphaMode).toBe('BLEND'); + expect(material.doubleSided).toBe(true); + }); }); describe('specularGlossiness', function() { @@ -401,6 +428,33 @@ describe('loadMtl', function() { expect(material.alphaMode).toBe('BLEND'); expect(material.doubleSided).toBe(true); }); + + it('packs alpha texture in diffuse texture', function() { + options.specularGlossiness = true; + + var material = loadMtl._createMaterial({ + diffuseTexture : diffuseTexture, + alphaTexture : alphaTexture + }, options); + + var pbr = material.extensions.KHR_materials_pbrSpecularGlossiness; + expect(pbr.diffuseTexture).toBeDefined(); + + var hasBlack = false; + var hasWhite = false; + var pixels = pbr.diffuseTexture.pixels; + var pixelsLength = pixels.length / 4; + for (var i = 0; i < pixelsLength; ++i) { + var alpha = pixels[i * 4 + 3]; + hasBlack = hasBlack || (alpha === 0); + hasWhite = hasWhite || (alpha === 255); + } + expect(hasBlack).toBe(true); + expect(hasWhite).toBe(true); + expect(pbr.diffuseFactor[3]).toEqual(1); + expect(material.alphaMode).toBe('BLEND'); + expect(material.doubleSided).toBe(true); + }); }); describe('materialsCommon', function() { @@ -535,5 +589,31 @@ describe('loadMtl', function() { var values = material.extensions.KHR_materials_common.values; expect(values.ambient).toEqual([0.0, 0.0, 0.0, 1.0]); }); + + it('packs alpha texture in diffuse texture', function() { + options.materialsCommon = true; + + var material = loadMtl._createMaterial({ + diffuseTexture : diffuseTexture, + alphaTexture : alphaTexture + }, options); + + var values = material.extensions.KHR_materials_common.values; + + var hasBlack = false; + var hasWhite = false; + var pixels = values.diffuse.pixels; + var pixelsLength = pixels.length / 4; + for (var i = 0; i < pixelsLength; ++i) { + var alpha = pixels[i * 4 + 3]; + hasBlack = hasBlack || (alpha === 0); + hasWhite = hasWhite || (alpha === 255); + } + expect(hasBlack).toBe(true); + expect(hasWhite).toBe(true); + expect(values.transparency).toBe(1.0); + expect(values.transparent).toBe(true); + expect(values.doubleSided).toBe(true); + }); }); }); diff --git a/specs/lib/loadTextureSpec.js b/specs/lib/loadTextureSpec.js index b8e33b9..9ae4285 100644 --- a/specs/lib/loadTextureSpec.js +++ b/specs/lib/loadTextureSpec.js @@ -5,7 +5,7 @@ var pngTexturePath = 'specs/data/box-complex-material/shininess.png'; var jpgTexturePath = 'specs/data/box-complex-material/emission.jpg'; var jpegTexturePath = 'specs/data/box-complex-material/specular.jpeg'; var gifTexturePath = 'specs/data/box-complex-material/ambient.gif'; -var grayscaleTexturePath = 'specs/data/box-complex-material/alpha.png'; +var grayscaleTexturePath = 'specs/data/box-complex-material-alpha/alpha.png'; var transparentTexturePath = 'specs/data/box-complex-material/diffuse.png'; describe('loadTexture', function() { diff --git a/specs/lib/obj2gltfSpec.js b/specs/lib/obj2gltfSpec.js index d54b5bc..eec6c6c 100644 --- a/specs/lib/obj2gltfSpec.js +++ b/specs/lib/obj2gltfSpec.js @@ -78,7 +78,8 @@ describe('obj2gltf', function() { metallicRoughnessOcclusionTexture : textureUrl, normalTexture : textureUrl, baseColorTexture : textureUrl, - emissiveTexture : textureUrl + emissiveTexture : textureUrl, + alphaTexture : textureUrl }, separateTextures : true, outputDirectory : outputDirectory @@ -100,7 +101,8 @@ describe('obj2gltf', function() { occlusionTexture : textureUrl, normalTexture : textureUrl, baseColorTexture : textureUrl, - emissiveTexture : textureUrl + emissiveTexture : textureUrl, + alphaTexture : textureUrl }, separateTextures : true, outputDirectory : outputDirectory