Skip to content
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

Occlusion/Roughness/Metalness texture packing rethink #258

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ npm/fbx2gltf/node_modules/
npm/tests/node_modules/
npm/tests/test/*.js
npm/tests/test/*.js.map
build
sdk
.vscode
Pipfile
Pipfile.lock
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,19 @@ that route should be digested propertly by FBX2glTF.
(A happy note: Allegorithmic's Substance Painter also exports Stingray PBS,
when hooked up to Maya.)

When processing PBR materials, this converter will always pack Occlusion,
Roughness, and Metallness into a single combined ORM texture, with each parameter
being in the R, G and, B channels respectively. If you specify a texture in any
of the Stingray material slots, a full ORM will be generated; if you leave all 3
blank, then no ORM texture will be assigned.

* Should you specify different textures for each, then they will
be merged into their respective channels. Any missing textures will be
defaulted to: Occlusion 1, Roughness 0, and Metallness 0. Note, all textures
must have the same dimensions.
* Should you specify the same texture in all 3 slots, then the texture will
be assumed to already be a packed ORM, and will be used as-is.

## Draco Compression

The tool will optionally apply [Draco](https://github.com/google/draco)
Expand Down
83 changes: 69 additions & 14 deletions src/gltf/Raw2Gltf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,41 +261,96 @@ ModelData* Raw2Gltf(
*/
RawMetRoughMatProps* props = (RawMetRoughMatProps*)material.info.get();

// determine if we need to generate a combined map
// determine if we need to generate a combined map, or if we only have 1 map to pass through
bool hasMetallicMap = material.textures[RAW_TEXTURE_USAGE_METALLIC] >= 0;
bool hasRoughnessMap = material.textures[RAW_TEXTURE_USAGE_ROUGHNESS] >= 0;
bool hasOcclusionMap = material.textures[RAW_TEXTURE_USAGE_OCCLUSION] >= 0;
bool atLeastTwoMaps = hasMetallicMap ? (hasRoughnessMap || hasOcclusionMap)
: (hasRoughnessMap && hasMetallicMap);
if (!atLeastTwoMaps) {
// this handles the case of 0 or 1 maps supplied

auto texturesAreSame = [&](RawTextureUsage a, RawTextureUsage b) -> bool {
// note: at this point the usages will be different, so we can't just compare indexes
return StringUtils::CompareNoCase(
raw.GetTexture(material.textures[a]).fileLocation,
raw.GetTexture(material.textures[b]).fileLocation ) == 0;
};

bool isPassThroughTexture = hasOcclusionMap && hasRoughnessMap && hasMetallicMap;
if (isPassThroughTexture) {
isPassThroughTexture =
texturesAreSame(RAW_TEXTURE_USAGE_METALLIC, RAW_TEXTURE_USAGE_ROUGHNESS) &&
texturesAreSame(RAW_TEXTURE_USAGE_METALLIC, RAW_TEXTURE_USAGE_OCCLUSION);
}

auto textureName = [&](RawTextureUsage usage){
int index = material.textures[usage];
if (index >= 0) {
return raw.GetTexture(index).name.c_str();
} else {
return "<empty>";
}
};

if (!(hasMetallicMap || hasRoughnessMap || hasOcclusionMap)) {
// no data, assume it's a material that just relies on the uniform properties
aoMetRoughTex = nullptr;
if (verboseOutput) {
fmt::printf("Material %s: no ORM textures detected\n", material.name.c_str() );
}
}
else if (isPassThroughTexture) {
// this handles the case where the same map is assigned to all the channels
aoMetRoughTex = hasMetallicMap
? simpleTex(RAW_TEXTURE_USAGE_METALLIC)
: (hasRoughnessMap
? simpleTex(RAW_TEXTURE_USAGE_ROUGHNESS)
: (hasOcclusionMap ? simpleTex(RAW_TEXTURE_USAGE_OCCLUSION) : nullptr));
? simpleTex(RAW_TEXTURE_USAGE_ROUGHNESS)
: (hasOcclusionMap
? simpleTex(RAW_TEXTURE_USAGE_OCCLUSION)
: nullptr));
if (verboseOutput) {
if (aoMetRoughTex) {
fmt::printf("Material %s: detected single ORM texture: %s\n", material.name.c_str(), aoMetRoughTex->name.c_str());
} else {
fmt::printf("Material %s: no ORM textures detected\n", material.name.c_str() );
}
}
} else {
// otherwise merge occlusion into the red channel, metallic into blue channel, and
// roughness into the green, of a new combinatory texture
/* otherwise we always have to create a new texture that merges
* occlusion into the red channel
* roughness into the green
* metallic into blue channel
* with defaults for any unspecified channels
*/
aoMetRoughTex = textureBuilder.combine(
{
material.textures[RAW_TEXTURE_USAGE_OCCLUSION],
material.textures[RAW_TEXTURE_USAGE_METALLIC],
material.textures[RAW_TEXTURE_USAGE_ROUGHNESS],
material.textures[RAW_TEXTURE_USAGE_METALLIC],
},
"ao_met_rough",
[&](const std::vector<const TextureBuilder::pixel*> pixels)
-> TextureBuilder::pixel {
const float occlusion = (*pixels[0])[0];
const float metallic = (*pixels[1])[0] * (hasMetallicMap ? 1 : props->metallic);
const float roughness =
(*pixels[2])[0] * (hasRoughnessMap ? 1 : props->roughness);
/**
* note: we're picking the channels from the sources aligned with where they're going
* just in case they were authored that way. This makes an existing ORM texture
* "pass through", and has no effect on a grey single type texture.
*/
const float occlusion = hasOcclusionMap ? (*pixels[0])[0] : 1;
const float roughness = (*pixels[1])[1] * (hasRoughnessMap ? 1 : props->roughness);
csubagio marked this conversation as resolved.
Show resolved Hide resolved
const float metallic = (*pixels[2])[2] * (hasMetallicMap ? 1 : props->metallic);
return {{occlusion,
props->invertRoughnessMap ? 1.0f - roughness : roughness,
metallic,
1}};
},
false);
if ( aoMetRoughTex && verboseOutput ) {
fmt::printf("Material %s: detected multiple ORM textures, combined: [%s, %s, %s] into [%s]\n",
material.name.c_str(),
textureName(RAW_TEXTURE_USAGE_OCCLUSION),
textureName(RAW_TEXTURE_USAGE_ROUGHNESS),
textureName(RAW_TEXTURE_USAGE_METALLIC),
aoMetRoughTex->name.c_str()
);
}
}
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
diffuseFactor = props->diffuseFactor;
Expand Down