-
Notifications
You must be signed in to change notification settings - Fork 311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added alpha texture support #124
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be better if this line was in in the Alternately, you can have a shortcut exit from the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reading the alpha channel before the red channel is probably safer. There could be a case where someone sets the diffuse slot and alpha slot to the same texture, in which case getting the transparency from the red channel would be a mistake. |
||
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 { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about adding a function that writes RGB at once? That way you can read the channels and then just use a single
writeChannels
function.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the purpose of doing it this way was so we could use the same
scratchChannel
and only need to read a single channel of the texture at a time.