Dramatic sweeping material changes.

This commit is contained in:
Par Winzell 2017-11-27 21:58:44 -08:00
parent d67722a5d7
commit de1de9ef8f
4 changed files with 537 additions and 196 deletions

View File

@ -257,7 +257,7 @@ struct FbxTraditionalMaterialInfo : FbxMaterialInfo {
};
std::string name = fbxMaterial->GetName();
std::unique_ptr<FbxTraditionalMaterialInfo> res(new FbxTraditionalMaterialInfo(name.c_str(), fbxMaterial->sShadingModel));
std::unique_ptr<FbxTraditionalMaterialInfo> res(new FbxTraditionalMaterialInfo(name.c_str(), fbxMaterial->ShadingModel.Get()));
// four properties are on the same structure and follow the same rules
auto handleBasicProperty = [&](const char *colName, const char *facName) -> std::tuple<FbxVector4, FbxFileTexture *>{
@ -768,22 +768,20 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
for (int polygonIndex = 0; polygonIndex < pMesh->GetPolygonCount(); polygonIndex++) {
FBX_ASSERT(pMesh->GetPolygonSize(polygonIndex) == 3);
int textures[RAW_TEXTURE_USAGE_MAX] { -1 };
std::fill_n(textures, RAW_TEXTURE_USAGE_MAX, -1);
FbxString shadingModel, materialName;
FbxVector4 ambient, specular, diffuse, emissive;
FbxDouble shininess, emissiveIntensity, metallic, roughness;
const std::shared_ptr<FbxMaterialInfo> fbxMaterial = materials.GetMaterial(polygonIndex);
int textures[RAW_TEXTURE_USAGE_MAX];
std::fill_n(textures, RAW_TEXTURE_USAGE_MAX, -1);
FbxString materialName;
std::shared_ptr<RawMatProps> rawMatProps;
if (fbxMaterial == nullptr) {
materialName = "DefaultMaterial";
shadingModel = "Lambert";
rawMatProps.reset(new RawTraditionalMatProps(RAW_SHADING_MODEL_LAMBERT,
Vec3f(0, 0, 0), Vec4f(.5, .5, .5, 1), Vec3f(0, 0, 0), Vec3f(0, 0, 0), 0.5));
} else {
materialName = fbxMaterial->name;
shadingModel = fbxMaterial->shadingModel;
const auto maybeAddTexture = [&](const FbxFileTexture *tex, RawTextureUsage usage) {
if (tex != nullptr) {
@ -793,37 +791,44 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
}
};
if (shadingModel == FbxRoughMetMaterialInfo::FBX_SHADER_METROUGH) {
FbxRoughMetMaterialInfo *matProps = static_cast<FbxRoughMetMaterialInfo *>(fbxMaterial.get());
std::shared_ptr<RawMatProps> matInfo;
if (fbxMaterial->shadingModel == FbxRoughMetMaterialInfo::FBX_SHADER_METROUGH) {
FbxRoughMetMaterialInfo *fbxMatInfo = static_cast<FbxRoughMetMaterialInfo *>(fbxMaterial.get());
maybeAddTexture(matProps->texColor, RAW_TEXTURE_USAGE_ALBEDO);
diffuse = matProps->colBase;
maybeAddTexture(matProps->texNormal, RAW_TEXTURE_USAGE_NORMAL);
maybeAddTexture(matProps->texEmissive, RAW_TEXTURE_USAGE_EMISSIVE);
emissive = matProps->colEmissive;
emissiveIntensity = matProps->emissiveIntensity;
maybeAddTexture(matProps->texRoughness, RAW_TEXTURE_USAGE_ROUGHNESS);
maybeAddTexture(matProps->texMetallic, RAW_TEXTURE_USAGE_METALLIC);
metallic = matProps->metallic;
maybeAddTexture(matProps->texAmbientOcclusion, RAW_TEXTURE_USAGE_OCCLUSION);
roughness = matProps->roughness;
maybeAddTexture(fbxMatInfo->texColor, RAW_TEXTURE_USAGE_ALBEDO);
maybeAddTexture(fbxMatInfo->texNormal, RAW_TEXTURE_USAGE_NORMAL);
maybeAddTexture(fbxMatInfo->texEmissive, RAW_TEXTURE_USAGE_EMISSIVE);
maybeAddTexture(fbxMatInfo->texRoughness, RAW_TEXTURE_USAGE_ROUGHNESS);
maybeAddTexture(fbxMatInfo->texMetallic, RAW_TEXTURE_USAGE_METALLIC);
maybeAddTexture(fbxMatInfo->texAmbientOcclusion, RAW_TEXTURE_USAGE_OCCLUSION);
rawMatProps.reset(new RawMetRoughMatProps(
RAW_SHADING_MODEL_PBR_MET_ROUGH, toVec4f(fbxMatInfo->colBase), toVec3f(fbxMatInfo->colEmissive),
fbxMatInfo->emissiveIntensity, fbxMatInfo->metallic, fbxMatInfo->roughness));
} else {
FbxTraditionalMaterialInfo *matProps = static_cast<FbxTraditionalMaterialInfo *>(fbxMaterial.get());
maybeAddTexture(matProps->texDiffuse, RAW_TEXTURE_USAGE_DIFFUSE);
diffuse = matProps->colDiffuse;
maybeAddTexture(matProps->texNormal, RAW_TEXTURE_USAGE_NORMAL);
maybeAddTexture(matProps->texEmissive, RAW_TEXTURE_USAGE_EMISSIVE);
emissive = matProps->colEmissive;
maybeAddTexture(matProps->texShininess, RAW_TEXTURE_USAGE_SHININESS);
shininess = matProps->shininess;
maybeAddTexture(matProps->texAmbient, RAW_TEXTURE_USAGE_AMBIENT);
ambient = matProps->colAmbient;
maybeAddTexture(matProps->texSpecular, RAW_TEXTURE_USAGE_SPECULAR);
specular = matProps->colSpecular;
FbxTraditionalMaterialInfo *fbxMatInfo = static_cast<FbxTraditionalMaterialInfo *>(fbxMaterial.get());
RawShadingModel shadingModel;
if (fbxMaterial->shadingModel == "Lambert") {
shadingModel = RAW_SHADING_MODEL_LAMBERT;
} else if (fbxMaterial->shadingModel == "Blinn") {
shadingModel = RAW_SHADING_MODEL_BLINN;
} else if (fbxMaterial->shadingModel == "Phong") {
shadingModel = RAW_SHADING_MODEL_PHONG;
} else if (fbxMaterial->shadingModel == "Constant") {
shadingModel = RAW_SHADING_MODEL_PHONG;
} else {
shadingModel = RAW_SHADING_MODEL_UNKNOWN;
}
maybeAddTexture(fbxMatInfo->texDiffuse, RAW_TEXTURE_USAGE_DIFFUSE);
maybeAddTexture(fbxMatInfo->texNormal, RAW_TEXTURE_USAGE_NORMAL);
maybeAddTexture(fbxMatInfo->texEmissive, RAW_TEXTURE_USAGE_EMISSIVE);
maybeAddTexture(fbxMatInfo->texShininess, RAW_TEXTURE_USAGE_SHININESS);
maybeAddTexture(fbxMatInfo->texAmbient, RAW_TEXTURE_USAGE_AMBIENT);
maybeAddTexture(fbxMatInfo->texSpecular, RAW_TEXTURE_USAGE_SPECULAR);
rawMatProps.reset(new RawTraditionalMatProps(shadingModel,
toVec3f(fbxMatInfo->colAmbient), toVec4f(fbxMatInfo->colDiffuse), toVec3f(fbxMatInfo->colEmissive),
toVec3f(fbxMatInfo->colSpecular), fbxMatInfo->shininess));
}
}
RawVertex rawVertices[3];
@ -951,10 +956,7 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
}
const RawMaterialType materialType = GetMaterialType(raw, textures, vertexTransparency, skinning.IsSkinned());
const int rawMaterialIndex = raw.AddMaterial(
materialName, shadingModel, materialType, textures,
toVec3f(ambient), toVec4f(diffuse), toVec3f(specular), toVec3f(emissive),
emissiveIntensity, shininess, metallic, roughness);
const int rawMaterialIndex = raw.AddMaterial(materialName, materialType, textures, rawMatProps);
raw.AddTriangle(rawVertexIndices[0], rawVertexIndices[1], rawVertexIndices[2], rawMaterialIndex, rawSurfaceIndex);
}

View File

@ -273,7 +273,7 @@ ModelData *Raw2Gltf(
for (int i = 0; i < raw.GetMaterialCount(); i++) {
fmt::printf(
"Material %d: %s [shading: %s]\n", i, raw.GetMaterial(i).name.c_str(),
raw.GetMaterial(i).shadingModel.c_str());
Describe(raw.GetMaterial(i).info->shadingModel));
}
if (raw.GetVertexCount() > 2 * raw.GetTriangleCount()) {
fmt::printf(
@ -298,7 +298,7 @@ ModelData *Raw2Gltf(
std::map<std::string, std::shared_ptr<NodeData>> nodesByName;
std::map<std::string, std::shared_ptr<MaterialData>> materialsByName;
std::map<std::string, std::shared_ptr<MeshData>> meshByNodeName;
std::map<int, std::shared_ptr<TextureData>> textureByRawIndex;
std::map<std::string, std::shared_ptr<TextureData>> textureByIndicesKey;
// for now, we only have one buffer; data->binary points to the same vector as that BufferData does.
BufferData &buffer = *gltf->buffers.hold(
@ -383,25 +383,36 @@ ModelData *Raw2Gltf(
//
// textures
//
typedef std::array<uint8_t, 4> pixel;
typedef std::function<pixel(const pixel &, const pixel &)> pixel_merger;
typedef std::array<float, 4> pixel; // pixel components are floats in [0, 1]
typedef std::function<pixel(const std::vector<const pixel *>)> pixel_merger;
auto texIndicesKey = [&](std::vector<int> ixVec, std::string tag) -> std::string {
std::string result = tag;
for (int ix : ixVec) {
result += "_";
result += ix;
}
return result;
};
/**
* Create a new derived TextureData for the two given RawTexture indexes, or return a previously created one.
* Each pixel in the derived texture will be determined from its equivalent in each source pixels, as decided
* by the provided `combine` function.
*/
auto getDerivedTexture = [&](int rawTexIx1, int rawTexIx2, const pixel_merger &combine) -> std::shared_ptr<TextureData> {
// TODO: index on first texture? what if it's -1?
auto iter = textureByRawIndex.find(rawTexIx1);
if (iter != textureByRawIndex.end()) {
auto getDerivedTexture = [&](
std::vector<int> rawTexIndices,
const pixel_merger &combine,
const std::string &tag
) -> std::shared_ptr<TextureData>
{
const std::string key = texIndicesKey(rawTexIndices, tag);
auto iter = textureByIndicesKey.find(key);
if (iter != textureByIndicesKey.end()) {
return iter->second;
}
const RawTexture &rawTex1 = raw.GetTexture(rawTexIx1);
const RawTexture &rawTex2 = raw.GetTexture(rawTexIx2);
auto channelStr = [&](int channels) -> std::string {
auto describeChannel = [&](int channels) -> std::string {
switch(channels) {
case 1: return "G";
case 2: return "GA";
@ -412,60 +423,99 @@ ModelData *Raw2Gltf(
}
};
int w1, h1, c1;
uint8_t *s1 = stbi_load(rawTex1.fileLocation.c_str(), &w1, &h1, &c1, 4);
if (!s1) {
fmt::printf("Warning: merge texture 1 (%s) could not be loaded.\n", rawTex1.fileName);
return nullptr;
// keep track of some texture data as we load them
struct TexInfo {
explicit TexInfo(int rawTexIx) : rawTexIx(rawTexIx) {}
const int rawTexIx;
int width;
int height;
int channels;
uint8_t *pixels;
};
int width = -1, height = -1;
std::string mergedName = tag;
std::string mergedFilename = tag;
std::vector<TexInfo> texes { };
for (const int rawTexIx : rawTexIndices) {
TexInfo info(rawTexIx);
if (rawTexIx >= 0) {
const RawTexture &rawTex = raw.GetTexture(rawTexIx);
const std::string &fileLoc = rawTex.fileLocation;
const std::string &fileLocBase = Gltf::StringUtils::GetFileBaseString(Gltf::StringUtils::GetFileNameString(fileLoc));
if (!fileLoc.empty()) {
info.pixels = stbi_load(fileLoc.c_str(), &info.width, &info.height, &info.channels, 4);
if (!info.pixels) {
fmt::printf("Warning: merge texture [%d](%s) could not be loaded.\n",
rawTexIx,
Gltf::StringUtils::GetFileBaseString(Gltf::StringUtils::GetFileNameString(fileLoc)));
} else {
if (width < 0) {
width = info.width;
height = info.height;
} else if (width != info.width || height != info.height) {
fmt::printf("Warning: texture %s (%d, %d) can't be merged with previous texture(s) of dimension (%d, %d)\n",
Gltf::StringUtils::GetFileBaseString(Gltf::StringUtils::GetFileNameString(fileLoc)),
info.width, info.height, width, height);
// this is bad enough that we abort the whole merge
return nullptr;
}
mergedName += "_" + rawTex.fileName;
mergedFilename += "_" + fileLocBase;
}
}
}
texes.push_back(info);
}
int w2, h2, c2;
uint8_t *s2 = stbi_load(rawTex2.fileLocation.c_str(), &w2, &h2, &c2, 4);
if (!s2) {
fmt::printf("Warning: merge texture 2 (%s) could not be loaded.\n", rawTex2.fileName);
return nullptr;
}
if (w1 != w2 || h1 != h2) {
fmt::printf("Warning: textures %s and %s have different dimensions and can't be combined\n",
rawTex1.fileName, rawTex2.fileName);
if (width < 0) {
// no textures to merge; bail
return nullptr;
}
// TODO: which channel combinations make sense in input files?
std::vector<uint8_t > mergedPixels(4 * w1 * h1);
std::vector<uint8_t > mergedPixels(4 * width * height);
std::vector<pixel> pixels(texes.size());
std::vector<const pixel *> pixelPointers(texes.size());
for (int ii = 0; ii < mergedPixels.size(); ii += 4) {
pixel merged = combine(
pixel { s1[ii+0], s1[ii+1], s1[ii+2], s1[ii+3] },
pixel { s2[ii+0], s2[ii+1], s2[ii+2], s2[ii+3] });
pixels.clear();
for (int jj = 0; jj < texes.size(); jj ++) {
const TexInfo &tex = texes[jj];
if (tex.pixels != nullptr) {
pixels[jj] = { tex.pixels[ii+0]/255.0f, tex.pixels[ii+1]/255.0f, tex.pixels[ii+2]/255.0f, tex.pixels[ii+3]/255.0f };
} else {
// absent textures are to be treated as all ones
pixels[jj] = { 1.0f, 1.0f, 1.0f, 1.0f };
}
pixelPointers[jj] = &pixels[jj];
}
const pixel &merged = combine(pixelPointers);
for (int jj = 0; jj < 4; jj ++) {
mergedPixels[ii + jj] = merged[jj];
mergedPixels[ii + jj] = static_cast<uint8_t>(fmax(0, fmin(255.0f, merged[jj] * 255.0f)));
}
}
bool png = false;
bool png = true;
std::vector<char> imgBuffer;
int res;
if (png) {
res = stbi_write_png_to_func(WriteToVectorContext<char>, &imgBuffer, w1, h1, 4, mergedPixels.data(), w1 * 4);
res = stbi_write_png_to_func(WriteToVectorContext<char>, &imgBuffer, width, height, 4, mergedPixels.data(), width * 4);
} else {
res = stbi_write_jpg_to_func(WriteToVectorContext<char>, &imgBuffer, w1, h1, 4, mergedPixels.data(), 80);
res = stbi_write_jpg_to_func(WriteToVectorContext<char>, &imgBuffer, width, height, 4, mergedPixels.data(), 80);
}
if (!res) {
fmt::printf("Warning: failed to generate merge texture '%s'.\n", mergedFilename);
return nullptr;
}
// TODO
assert(res != 0);
const std::string name = "merge_" + rawTex1.name + "_" + rawTex2.name;
const std::string fileName = "merge_" +
Gltf::StringUtils::GetFileBaseString(Gltf::StringUtils::GetFileNameString(rawTex1.fileLocation)) + "_" +
Gltf::StringUtils::GetFileBaseString(Gltf::StringUtils::GetFileNameString(rawTex2.fileLocation));
ImageData *image;
if (options.outputBinary) {
const auto bufferView = gltf->AddRawBufferView(buffer, imgBuffer.data(), imgBuffer.size());
image = new ImageData(name, *bufferView, png ? "image/png" : "image/jpeg");
image = new ImageData(mergedName, *bufferView, png ? "image/png" : "image/jpeg");
} else {
const std::string imageFilename = fileName + (png ? ".png" : ".jpg");
const std::string imageFilename = mergedFilename + (png ? ".png" : ".jpg");
const std::string imagePath = outputFolder + imageFilename;
FILE *fp = fopen(imagePath.c_str(), "wb");
if (fp == nullptr) {
@ -483,19 +533,20 @@ ModelData *Raw2Gltf(
fmt::printf("Wrote %lu bytes to texture '%s'.\n", imgBuffer.size(), imagePath);
}
image = new ImageData(name, imageFilename);
image = new ImageData(mergedName, imageFilename);
}
std::shared_ptr<TextureData> texDat = gltf->textures.hold(
new TextureData(name, defaultSampler, *gltf->images.hold(image)));
textureByRawIndex.insert(std::make_pair(rawTexIx1, texDat));
new TextureData(mergedName, defaultSampler, *gltf->images.hold(image)));
textureByIndicesKey.insert(std::make_pair(key, texDat));
return texDat;
};
/** Create a new TextureData for the given RawTexture index, or return a previously created one. */
auto getSimpleTexture = [&](int rawTexIndex) {
auto iter = textureByRawIndex.find(rawTexIndex);
if (iter != textureByRawIndex.end()) {
auto getSimpleTexture = [&](int rawTexIndex, const std::string &tag) {
const std::string key = texIndicesKey({ rawTexIndex }, tag);
auto iter = textureByIndicesKey.find(key);
if (iter != textureByIndicesKey.end()) {
return iter->second;
}
@ -531,7 +582,7 @@ ModelData *Raw2Gltf(
std::shared_ptr<TextureData> texDat = gltf->textures.hold(
new TextureData(textureName, defaultSampler, *gltf->images.hold(image)));
textureByRawIndex.insert(std::make_pair(rawTexIndex, texDat));
textureByIndicesKey.insert(std::make_pair(key, texDat));
return texDat;
};
@ -545,59 +596,289 @@ ModelData *Raw2Gltf(
material.type == RAW_MATERIAL_TYPE_TRANSPARENT ||
material.type == RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT;
Vec3f emissiveFactor;
float emissiveIntensity;
const Vec3f dielectric(0.04f, 0.04f, 0.04f);
const float epsilon = 1e-6f;
// acquire the texture of a specific RawTextureUsage as *TextData, or nullptr if none exists
auto simpleTex = [&](RawTextureUsage usage) -> const TextureData * {
return (material.textures[usage] >= 0) ? getSimpleTexture(material.textures[usage]).get() : nullptr;
auto simpleTex = [&](RawTextureUsage usage) -> std::shared_ptr<TextureData> {
return (material.textures[usage] >= 0) ? getSimpleTexture(material.textures[usage], "simple") : nullptr;
};
// acquire derived texture of two RawTextureUsage as *TextData, or nullptr if neither exists
auto derivedTex = [&](RawTextureUsage u1, RawTextureUsage u2, const pixel_merger &combine) -> const TextureData * {
int t1 = material.textures[u1], t2 = material.textures[u2];
return (t1 >= 0 || t2 >= 0) ? getDerivedTexture(t1, t2, combine).get() : nullptr;
auto merge2Tex = [&](
const std::string tag,
RawTextureUsage u1,
RawTextureUsage u2,
const pixel_merger &combine
) -> std::shared_ptr<TextureData> {
return getDerivedTexture({ material.textures[u1], material.textures[u2] }, combine, tag);
};
// acquire derived texture of two RawTextureUsage as *TextData, or nullptr if neither exists
auto merge3Tex = [&](
const std::string tag,
RawTextureUsage u1,
RawTextureUsage u2,
RawTextureUsage u3,
const pixel_merger &combine
) -> std::shared_ptr<TextureData> {
return getDerivedTexture({ material.textures[u1], material.textures[u2], material.textures[u3] }, combine, tag);
};
auto getMaxComponent = [&](const Vec3f &color) {
return fmax(color.x, fmax(color.y, color.z));
};
auto getPerceivedBrightness = [&](const Vec3f &color) {
return sqrt(0.299 * color.x * color.x + 0.587 * color.y * color.y + 0.114 * color.z * color.z);
};
auto toVec3f = [&](const pixel &pix) -> const Vec3f {
return Vec3f(pix[0], pix[1], pix[2]);
};
auto toVec4f = [&](const pixel &pix) -> const Vec4f {
return Vec4f(pix[0], pix[1], pix[2], pix[3]);
};
std::shared_ptr<PBRMetallicRoughness> pbrMetRough;
if (options.usePBRMetRough) {
// merge metallic into the blue channel and roughness into the green, of a new combinatory texture
const TextureData *metRoughTex = derivedTex(
RAW_TEXTURE_USAGE_METALLIC, RAW_TEXTURE_USAGE_ROUGHNESS,
[&](const pixel &met, const pixel &rough) -> pixel { return { 0, rough[0], met[0], 0 }; });
// albedo is basic
const TextureData *albedoTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
// albedo is a basic texture, no merging needed
std::shared_ptr<TextureData> baseColorTex, metRoughTex;
pbrMetRough.reset(new PBRMetallicRoughness(albedoTex, metRoughTex, material.diffuseFactor, material.metallic, material.roughness));
Vec4f diffuseFactor;
float metallic, roughness;
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) {
/**
* PBR FBX Material -> PBR Met/Rough glTF.
*
* METALLIC and ROUGHNESS textures are packed in G and B channels of a rough/met texture.
* Other values translate directly.
*/
RawMetRoughMatProps *props = (RawMetRoughMatProps *) material.info.get();
// merge metallic into the blue channel and roughness into the green, of a new combinatory texture
metRoughTex = merge2Tex("met_rough",
RAW_TEXTURE_USAGE_METALLIC, RAW_TEXTURE_USAGE_ROUGHNESS,
[&](const std::vector<const pixel *> pixels) -> pixel { return { 0, (*pixels[1])[0], (*pixels[0])[0], 0 }; });
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
diffuseFactor = props->diffuseFactor;
metallic = props->metallic;
roughness = props->roughness;
emissiveFactor = props->emissiveFactor;
emissiveIntensity = props->emissiveIntensity;
} else {
/**
* Traditional FBX Material -> PBR Met/Rough glTF.
*
* Diffuse channel is used as base colour. No metallic/roughness texture is attempted. Constant
* metallic/roughness values are hard-coded.
*
* TODO: If we have specular & optional shininess map, we can generate a reasonable rough/met map.
*/
const RawTraditionalMatProps *props = ((RawTraditionalMatProps *) material.info.get());
diffuseFactor = props->diffuseFactor;
// TODO: make configurable on the command line, or do a better job guessing from other, supplied params
if (material.info->shadingModel == RAW_SHADING_MODEL_LAMBERT) {
metallic = 0.6f;
roughness = 1.0f;
} else {
metallic = 0.2f;
roughness = 0.6f;
}
auto solveMetallic = [&](float pDiff, float pSpec, float oneMinusSpecularStrength)
{
if (pSpec < dielectric.x) {
return 0.0;
}
float a = dielectric.x;
float b = pDiff * oneMinusSpecularStrength / (1 - dielectric.x) + pSpec - 2 * dielectric.x;
float c = dielectric.x - pSpec;
float D = fmax(b * b - 4 * a * c, 0);
return fmax(0.0, fmin(1.0, (-b + sqrt(D)) / (2 * a)));
};
metRoughTex = merge3Tex("rough_met",
RAW_TEXTURE_USAGE_SPECULAR, RAW_TEXTURE_USAGE_SHININESS, RAW_TEXTURE_USAGE_DIFFUSE,
[&](const std::vector<const pixel *> pixels) -> pixel {
const Vec3f specular = pixels[0] ? toVec3f(*pixels[0]) : props->specularFactor;
float shininess = pixels[1] ? (*pixels[1])[0] : props->shininess;
const Vec4f diffuse = pixels[2] ? toVec4f(*pixels[2]) : props->diffuseFactor;
float pixelMet = solveMetallic(
getPerceivedBrightness(diffuse.xyz()),
getPerceivedBrightness(specular),
1 - getMaxComponent(specular));
float pixelRough = 1 - shininess;
return { 0, pixelRough, pixelMet, 0 };
});
if (material.textures[RAW_TEXTURE_USAGE_DIFFUSE] >= 0) {
baseColorTex = merge2Tex("base_col",
RAW_TEXTURE_USAGE_DIFFUSE, RAW_TEXTURE_USAGE_SPECULAR,
[&](const std::vector<const pixel *> pixels) -> pixel {
const Vec4f diffuse = pixels[0] ? toVec4f(*pixels[0]) : props->diffuseFactor;
const Vec3f specular = pixels[1] ? toVec3f(*pixels[1]) : props->specularFactor;
float oneMinus = 1 - getMaxComponent(specular);
float pixelMet = solveMetallic(
getPerceivedBrightness(diffuse.xyz()),
getPerceivedBrightness(specular),
oneMinus);
Vec3f fromDiffuse = diffuse.xyz() * (oneMinus / (1.0 - dielectric.x) / fmax(1.0 - pixelMet, epsilon));
Vec3f fromSpecular = specular - dielectric * (1.0 - pixelMet) * (1.0 / fmax(pixelMet, epsilon));
Vec3f baseColor = Vec3f::Lerp(fromDiffuse, fromSpecular, pixelMet * pixelMet);
return { baseColor[0], baseColor[1], baseColor[2], diffuse[3] };
});
}
emissiveFactor = props->emissiveFactor;
emissiveIntensity = 1.0f;
}
pbrMetRough.reset(new PBRMetallicRoughness(baseColorTex.get(), metRoughTex.get(), diffuseFactor, metallic, roughness));
}
std::shared_ptr<PBRSpecularGlossiness> pbrSpecGloss;
if (options.usePBRSpecGloss) {
Vec4f diffuseFactor;
Vec3f specularFactor;
float glossiness;
std::shared_ptr<TextureData> specGlossTex;
std::shared_ptr<TextureData> diffuseTex;
const Vec3f dielectric(0.04f, 0.04f, 0.04f);
const float epsilon = 1e-6f;
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) {
/**
* PBR FBX Material -> PBR Spec/Gloss glTF.
*
* TODO: A complete mess. Low priority.
*/
RawMetRoughMatProps *props = (RawMetRoughMatProps *) material.info.get();
// we can estimate spec/gloss from met/rough by between Vec4f(0.04, 0.04, 0.04) and baseColor
// according to metalness, and then taking gloss to be the inverse of roughness
specGlossTex = merge3Tex("specgloss",
RAW_TEXTURE_USAGE_ALBEDO, RAW_TEXTURE_USAGE_METALLIC, RAW_TEXTURE_USAGE_ROUGHNESS,
[&](const std::vector<const pixel *> pixels) -> pixel {
Vec3f baseColor(1.0f);
if (pixels[0]) {
baseColor.x = (*pixels[0])[0];
baseColor.y = (*pixels[0])[1];
baseColor.z = (*pixels[0])[2];
}
float metallic = pixels[1] ? (*pixels[1])[0] : 1.0f;
float roughness = pixels[2] ? (*pixels[2])[0] : 1.0f;
Vec3f spec = Vec3f::Lerp(dielectric, baseColor, metallic);
return { spec[0], spec[1], spec[2], 1.0f - roughness };
});
diffuseTex = merge2Tex("albedo",
RAW_TEXTURE_USAGE_ALBEDO, RAW_TEXTURE_USAGE_METALLIC,
[&](const std::vector<const pixel *> pixels) -> pixel {
Vec3f baseColor(1.0f);
float alpha = 1.0f;
if (pixels[0]) {
baseColor[0] = (*pixels[0])[0];
baseColor[1] = (*pixels[0])[1];
baseColor[2] = (*pixels[0])[2];
alpha = (*pixels[0])[3];
}
float metallic = pixels[1] ? (*pixels[1])[0] : 1.0f;
Vec3f spec = Vec3f::Lerp(dielectric, baseColor, metallic);
float maxSpecComp = fmax(fmax(spec.x, spec.y), spec.z);
// attenuate baseColor to get specgloss-compliant diffuse; for details consult
// https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_pbrSpecularGlossiness
Vec3f diffuse = baseColor * (1 - dielectric[0]) * (1 - metallic) * fmax(1.0f - maxSpecComp, epsilon);
return { diffuse[0], diffuse[1], diffuse[2], alpha };
});
diffuseFactor = props->diffuseFactor;
specularFactor = Vec3f::Lerp(dielectric, props->diffuseFactor.xyz(), props->metallic);
glossiness = 1.0f - props->roughness;
} else {
/**
* Traditional FBX Material -> PBR Spec/Gloss glTF.
*
* TODO: A complete mess. Low priority.
*/
// TODO: this section a ludicrous over-simplifictation; we can surely do better.
const RawTraditionalMatProps *props = ((RawTraditionalMatProps *) material.info.get());
specGlossTex = merge2Tex("specgloss",
RAW_TEXTURE_USAGE_SPECULAR, RAW_TEXTURE_USAGE_SHININESS,
[&](const std::vector<const pixel *> pixels) -> pixel {
const auto &spec = *(pixels[0]);
const auto &shine = *(pixels[1]);
return { spec[0], spec[1], spec[2], shine[0] };
});
diffuseTex = simpleTex(RAW_TEXTURE_USAGE_DIFFUSE);
diffuseFactor = props->diffuseFactor;
specularFactor = props->specularFactor;
glossiness = props->shininess;
}
pbrSpecGloss.reset(
new PBRSpecularGlossiness(
simpleTex(RAW_TEXTURE_USAGE_DIFFUSE), material.diffuseFactor,
simpleTex(RAW_TEXTURE_USAGE_SPECULAR), material.specularFactor, material.shininess));
diffuseTex.get(), diffuseFactor, specGlossTex.get(), specularFactor, glossiness));
}
std::shared_ptr<KHRCommonMats> khrComMat;
if (options.useKHRMatCom) {
auto type = KHRCommonMats::MaterialType::Constant;
if (material.shadingModel == "Lambert") {
type = KHRCommonMats::MaterialType::Lambert;
} else if (material.shadingModel == "Blinn") {
type = KHRCommonMats::MaterialType::Blinn;
} else if (material.shadingModel == "Phong") {
type = KHRCommonMats::MaterialType::Phong;
float shininess;
Vec3f ambientFactor, specularFactor;
Vec4f diffuseFactor;
std::shared_ptr<TextureData> diffuseTex;
auto type = KHRCommonMats::MaterialType::Constant;
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) {
/**
* PBR FBX Material -> KHR Common Materials glTF.
*
* TODO: We can use the specularFactor calculation below to generate a reasonable specular map, too.
*/
const RawMetRoughMatProps *props = (RawMetRoughMatProps *) material.info.get();
shininess = 1.0f - props->roughness;
ambientFactor = Vec3f(0.0f, 0.0f, 0.0f);
diffuseTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
diffuseFactor = props->diffuseFactor;
specularFactor = Vec3f::Lerp(Vec3f(0.04f, 0.04f, 0.04f), props->diffuseFactor.xyz(), props->metallic);
// render as Phong if there's any specularity, otherwise Lambert
type = (specularFactor.LengthSquared() > 1e-4) ?
KHRCommonMats::MaterialType::Phong : KHRCommonMats::MaterialType::Lambert;
// TODO: convert textures
} else {
/**
* Traditional FBX Material -> KHR Common Materials glTF.
*
* Should be in good shape. Essentially pass-through.
*/
const RawTraditionalMatProps *props = (RawTraditionalMatProps *) material.info.get();
shininess = props->shininess;
ambientFactor = props->ambientFactor;
diffuseTex = simpleTex(RAW_TEXTURE_USAGE_DIFFUSE);
diffuseFactor = props->diffuseFactor;
specularFactor = props->specularFactor;
if (material.info->shadingModel == RAW_SHADING_MODEL_LAMBERT) {
type = KHRCommonMats::MaterialType::Lambert;
} else if (material.info->shadingModel == RAW_SHADING_MODEL_BLINN) {
type = KHRCommonMats::MaterialType::Blinn;
} else if (material.info->shadingModel == RAW_SHADING_MODEL_PHONG) {
type = KHRCommonMats::MaterialType::Phong;
}
}
khrComMat.reset(
new KHRCommonMats(
type,
simpleTex(RAW_TEXTURE_USAGE_SHININESS), material.shininess,
simpleTex(RAW_TEXTURE_USAGE_AMBIENT), material.ambientFactor,
simpleTex(RAW_TEXTURE_USAGE_DIFFUSE), material.diffuseFactor,
simpleTex(RAW_TEXTURE_USAGE_SPECULAR), material.specularFactor));
new KHRCommonMats(type,
simpleTex(RAW_TEXTURE_USAGE_SHININESS).get(), shininess,
simpleTex(RAW_TEXTURE_USAGE_AMBIENT).get(), ambientFactor,
diffuseTex.get(), diffuseFactor,
simpleTex(RAW_TEXTURE_USAGE_SPECULAR).get(), specularFactor));
}
std::shared_ptr<MaterialData> mData = gltf->materials.hold(
new MaterialData(
material.name, isTransparent, simpleTex(RAW_TEXTURE_USAGE_NORMAL),
simpleTex(RAW_TEXTURE_USAGE_EMISSIVE), material.emissiveFactor * material.emissiveIntensity, // TODO: 1.0 default value for emissiveIntensity?
material.name, isTransparent,
simpleTex(RAW_TEXTURE_USAGE_NORMAL).get(), simpleTex(RAW_TEXTURE_USAGE_EMISSIVE).get(),
emissiveFactor * emissiveIntensity,
khrComMat, pbrMetRough, pbrSpecGloss));
materialsByName[materialHash(material)] = mData;
}

View File

@ -111,38 +111,25 @@ int RawModel::AddTexture(const std::string &name, const std::string &fileName, c
int RawModel::AddMaterial(const RawMaterial &material)
{
return AddMaterial(
material.name.c_str(), material.shadingModel.c_str(), material.type, material.textures, material.ambientFactor,
material.diffuseFactor, material.specularFactor, material.emissiveFactor, material.emissiveIntensity,
material.shininess, material.metallic, material.roughness);
return AddMaterial(material.name.c_str(), material.type, material.textures, material.info);
}
int RawModel::AddMaterial(
const char *name, const char *shadingModel, const RawMaterialType materialType, const int textures[RAW_TEXTURE_USAGE_MAX],
const Vec3f ambientFactor, const Vec4f diffuseFactor, const Vec3f specularFactor, const Vec3f emissiveFactor,
float emissiveIntensity, float shinineness, float metallic, float roughness)
const char *name,
const RawMaterialType materialType,
const int textures[RAW_TEXTURE_USAGE_MAX],
std::shared_ptr<RawMatProps> materialInfo)
{
for (size_t i = 0; i < materials.size(); i++) {
if (materials[i].name != name) {
continue;
}
if (materials[i].shadingModel != shadingModel) {
continue;
}
if (materials[i].type != materialType) {
continue;
}
if (materials[i].ambientFactor != ambientFactor ||
materials[i].diffuseFactor != diffuseFactor ||
materials[i].specularFactor != specularFactor ||
materials[i].emissiveFactor != emissiveFactor ||
materials[i].emissiveIntensity != emissiveIntensity ||
materials[i].shininess != shinineness ||
materials[i].metallic != metallic ||
materials[i].roughness != roughness) {
if (*(materials[i].info) != *materialInfo) {
continue;
}
bool match = true;
for (int j = 0; match && j < RAW_TEXTURE_USAGE_MAX; j++) {
match = match && (materials[i].textures[j] == textures[j]);
@ -153,17 +140,9 @@ int RawModel::AddMaterial(
}
RawMaterial material;
material.name = name;
material.shadingModel = shadingModel;
material.type = materialType;
material.ambientFactor = ambientFactor;
material.diffuseFactor = diffuseFactor;
material.specularFactor = specularFactor;
material.emissiveFactor = emissiveFactor;
material.emissiveIntensity = emissiveIntensity;
material.shininess = shinineness;
material.metallic = metallic;
material.roughness = roughness;
material.name = name;
material.type = materialType;
material.info = materialInfo;
for (int i = 0; i < RAW_TEXTURE_USAGE_MAX; i++) {
material.textures[i] = textures[i];

View File

@ -95,8 +95,32 @@ struct RawTriangle
int surfaceIndex;
};
enum RawShadingModel
{
RAW_SHADING_MODEL_UNKNOWN = -1,
RAW_SHADING_MODEL_CONSTANT,
RAW_SHADING_MODEL_LAMBERT,
RAW_SHADING_MODEL_BLINN,
RAW_SHADING_MODEL_PHONG,
RAW_SHADING_MODEL_PBR_MET_ROUGH,
RAW_SHADING_MODEL_MAX
};
static inline std::string Describe(RawShadingModel model) {
switch(model) {
case RAW_SHADING_MODEL_UNKNOWN: return "<unknown>";
case RAW_SHADING_MODEL_CONSTANT: return "Constant";
case RAW_SHADING_MODEL_LAMBERT: return "Lambert";
case RAW_SHADING_MODEL_BLINN: return "Blinn";
case RAW_SHADING_MODEL_PHONG: return "Phong";
case RAW_SHADING_MODEL_PBR_MET_ROUGH: return "Metallic/Roughness";
case RAW_SHADING_MODEL_MAX: default: return "<unknown>";
}
}
enum RawTextureUsage
{
RAW_TEXTURE_USAGE_NONE = -1,
RAW_TEXTURE_USAGE_AMBIENT,
RAW_TEXTURE_USAGE_DIFFUSE,
RAW_TEXTURE_USAGE_NORMAL,
@ -111,35 +135,21 @@ enum RawTextureUsage
RAW_TEXTURE_USAGE_MAX
};
inline std::string DescribeTextureUsage(int usage)
static inline std::string Describe(RawTextureUsage usage)
{
if (usage < 0) {
return "<none>";
}
switch (static_cast<RawTextureUsage>(usage)) {
case RAW_TEXTURE_USAGE_AMBIENT:
return "ambient";
case RAW_TEXTURE_USAGE_DIFFUSE:
return "diffuse";
case RAW_TEXTURE_USAGE_NORMAL:
return "normal";
case RAW_TEXTURE_USAGE_SPECULAR:
return "specuar";
case RAW_TEXTURE_USAGE_SHININESS:
return "shininess";
case RAW_TEXTURE_USAGE_EMISSIVE:
return "emissive";
case RAW_TEXTURE_USAGE_REFLECTION:
return "reflection";
case RAW_TEXTURE_USAGE_OCCLUSION:
return "occlusion";
case RAW_TEXTURE_USAGE_ROUGHNESS:
return "roughness";
case RAW_TEXTURE_USAGE_METALLIC:
return "metallic";
case RAW_TEXTURE_USAGE_MAX:
default:
return "unknown";
switch (usage) {
case RAW_TEXTURE_USAGE_NONE: return "<none>";
case RAW_TEXTURE_USAGE_AMBIENT: return "ambient";
case RAW_TEXTURE_USAGE_DIFFUSE: return "diffuse";
case RAW_TEXTURE_USAGE_NORMAL: return "normal";
case RAW_TEXTURE_USAGE_SPECULAR: return "specuar";
case RAW_TEXTURE_USAGE_SHININESS: return "shininess";
case RAW_TEXTURE_USAGE_EMISSIVE: return "emissive";
case RAW_TEXTURE_USAGE_REFLECTION: return "reflection";
case RAW_TEXTURE_USAGE_OCCLUSION: return "occlusion";
case RAW_TEXTURE_USAGE_ROUGHNESS: return "roughness";
case RAW_TEXTURE_USAGE_METALLIC: return "metallic";
case RAW_TEXTURE_USAGE_MAX:default: return "unknown";
}
};
@ -169,21 +179,91 @@ enum RawMaterialType
RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT,
};
struct RawMatProps {
explicit RawMatProps(RawShadingModel shadingModel)
: shadingModel(shadingModel)
{}
const RawShadingModel shadingModel;
virtual bool operator!=(const RawMatProps &other) const { return !(*this == other); }
virtual bool operator==(const RawMatProps &other) const { return shadingModel == other.shadingModel; };
};
struct RawTraditionalMatProps : RawMatProps {
RawTraditionalMatProps(
RawShadingModel shadingModel,
const Vec3f &&ambientFactor,
const Vec4f &&diffuseFactor,
const Vec3f &&emissiveFactor,
const Vec3f &&specularFactor,
const float shininess
) : RawMatProps(shadingModel),
ambientFactor(ambientFactor),
diffuseFactor(diffuseFactor),
emissiveFactor(emissiveFactor),
specularFactor(specularFactor),
shininess(shininess)
{}
const Vec3f ambientFactor;
const Vec4f diffuseFactor;
const Vec3f emissiveFactor;
const Vec3f specularFactor;
const float shininess;
bool operator==(const RawMatProps &other) const override {
if (RawMatProps::operator==(other)) {
const auto &typed = (RawTraditionalMatProps &) other;
return ambientFactor == typed.ambientFactor &&
diffuseFactor == typed.diffuseFactor &&
specularFactor == typed.specularFactor &&
emissiveFactor == typed.emissiveFactor &&
shininess == typed.shininess;
}
return false;
}
};
struct RawMetRoughMatProps : RawMatProps {
RawMetRoughMatProps(
RawShadingModel shadingModel,
const Vec4f &&diffuseFactor,
const Vec3f &&emissiveFactor,
float emissiveIntensity,
float metallic,
float roughness
) : RawMatProps(shadingModel),
diffuseFactor(diffuseFactor),
emissiveFactor(emissiveFactor),
emissiveIntensity(emissiveIntensity),
metallic(metallic),
roughness(roughness)
{}
const Vec4f diffuseFactor;
const Vec3f emissiveFactor;
const float emissiveIntensity;
const float metallic;
const float roughness;
bool operator==(const RawMatProps &other) const override {
if (RawMatProps::operator==(other)) {
const auto &typed = (RawMetRoughMatProps &) other;
return diffuseFactor == typed.diffuseFactor &&
emissiveFactor == typed.emissiveFactor &&
emissiveIntensity == typed.emissiveIntensity &&
metallic == typed.metallic &&
roughness == typed.roughness;
}
return false;
}
};
struct RawMaterial
{
std::string name;
std::string shadingModel; // typically "Surface", "Anisotropic", "Blinn", "Lambert", "Phong", "Phone E"
RawMaterialType type;
Vec3f ambientFactor;
Vec4f diffuseFactor;
Vec3f specularFactor;
Vec3f emissiveFactor;
float emissiveIntensity;
float shininess;
float metallic;
float roughness;
int textures[RAW_TEXTURE_USAGE_MAX];
std::string name;
RawMaterialType type;
std::shared_ptr<RawMatProps> info;
int textures[RAW_TEXTURE_USAGE_MAX];
};
struct RawBlendChannel
@ -275,9 +355,8 @@ public:
int AddTexture(const std::string &name, const std::string &fileName, const std::string &fileLocation, RawTextureUsage usage);
int AddMaterial(const RawMaterial &material);
int AddMaterial(
const char *name, const char *shadingModel, const RawMaterialType materialType, const int textures[RAW_TEXTURE_USAGE_MAX],
const Vec3f ambientFactor, const Vec4f diffuseFactor, const Vec3f specularFactor, const Vec3f emissiveFactor,
float emissiveIntensity, float shinineness, float metallic, float roughness);
const char *name, const RawMaterialType materialType, const int textures[RAW_TEXTURE_USAGE_MAX],
std::shared_ptr<RawMatProps> materialInfo);
int AddSurface(const RawSurface &suface);
int AddSurface(const char *name, const char *nodeName);
int AddAnimation(const RawAnimation &animation);