diff --git a/CMakeLists.txt b/CMakeLists.txt index 2642c31..968569e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,20 +154,20 @@ endif() set(LIB_SOURCE_FILES src/FBX2glTF.h + src/fbx/materials/3dsMaxPhysicalMaterial.cpp + src/fbx/materials/FbxMaterials.cpp + src/fbx/materials/FbxMaterials.hpp + src/fbx/materials/RoughnessMetallicMaterials.hpp + src/fbx/materials/StingrayPBSMaterial.cpp + src/fbx/materials/TraditionalMaterials.cpp + src/fbx/materials/TraditionalMaterials.hpp src/fbx/Fbx2Raw.cpp src/fbx/Fbx2Raw.hpp src/fbx/FbxBlendShapesAccess.cpp src/fbx/FbxBlendShapesAccess.hpp src/fbx/FbxLayerElementAccess.hpp - src/fbx/FbxMaterialInfo.hpp - src/fbx/FbxMaterialsAccess.cpp - src/fbx/FbxMaterialsAccess.hpp - src/fbx/FbxRoughMetMaterialInfo.cpp - src/fbx/FbxRoughMetMaterialInfo.hpp src/fbx/FbxSkinningAccess.cpp src/fbx/FbxSkinningAccess.hpp - src/fbx/FbxTraditionalMaterialInfo.cpp - src/fbx/FbxTraditionalMaterialInfo.hpp src/gltf/Raw2Gltf.cpp src/gltf/Raw2Gltf.hpp src/gltf/GltfModel.cpp diff --git a/src/FBX2glTF.cpp b/src/FBX2glTF.cpp index 6189ca7..7d44777 100644 --- a/src/FBX2glTF.cpp +++ b/src/FBX2glTF.cpp @@ -103,33 +103,10 @@ int main(int argc, char* argv[]) { "When to compute vertex normals from mesh geometry.") ->type_name("(never|broken|missing|always)"); - std::vector> texturesTransforms; - app.add_flag_function( - "--flip-u", - [&](size_t count) { - if (count > 0) { - texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(1.0f - uv[0], uv[1]); }); - if (verboseOutput) { - fmt::printf("Flipping texture coordinates in the 'U' dimension.\n"); - } - } - }, - "Flip all U texture coordinates."); - - app.add_flag("--no-flip-u", "Don't flip U texture coordinates.")->excludes("--flip-u"); - - app.add_flag_function( - "--no-flip-v", - [&](size_t count) { - if (count > 0) { - texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(uv[0], 1.0f - uv[1]); }); - if (verboseOutput) { - fmt::printf("NOT flipping texture coordinates in the 'V' dimension.\n"); - } - } - }, - "Flip all V texture coordinates."); - app.add_flag("--flip-v", "Don't flip U texture coordinates.")->excludes("--no-flip-v"); + const auto opt_flip_u = app.add_flag("--flip-u", "Flip all U texture coordinates."); + const auto opt_no_flip_u = app.add_flag("--no-flip-u", "Don't flip U texture coordinates."); + const auto opt_flip_v = app.add_flag("--flip-v", "Flip all V texture coordinates."); + const auto opt_no_flip_v = app.add_flag("--no-flip-v", "Don't flip V texture coordinates."); app.add_flag( "--pbr-metallic-rougnness", @@ -250,6 +227,32 @@ int main(int argc, char* argv[]) { CLI11_PARSE(app, argc, argv); + bool do_flip_u = false; + bool do_flip_v = true; + // somewhat tedious way to resolve --flag vs --no-flag in order provided + for (const auto opt : app.parse_order()) { + do_flip_u = (do_flip_u || (opt == opt_flip_u)) && (opt != opt_no_flip_u); + do_flip_v = (do_flip_v || (opt == opt_flip_v)) && (opt != opt_no_flip_v); + } + std::vector> texturesTransforms; + if (do_flip_u || do_flip_v) { + if (do_flip_u && do_flip_v) { + texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(1.0 - uv[0], 1.0 - uv[1]); }); + } else if (do_flip_u) { + texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(1.0 - uv[0], uv[1]); }); + } else { + texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(uv[0], 1.0 - uv[1]); }); + } + } + if (verboseOutput) { + if (do_flip_u) { + fmt::printf("Flipping texture coordinates in the 'U' dimension.\n"); + } + if (!do_flip_v) { + fmt::printf("NOT flipping texture coordinates in the 'V' dimension.\n"); + } + } + if (inputPath.empty()) { fmt::printf("You must supply a FBX file to convert.\n"); exit(1); diff --git a/src/fbx/Fbx2Raw.cpp b/src/fbx/Fbx2Raw.cpp index 511e423..3b31cc7 100644 --- a/src/fbx/Fbx2Raw.cpp +++ b/src/fbx/Fbx2Raw.cpp @@ -29,8 +29,9 @@ #include "FbxBlendShapesAccess.hpp" #include "FbxLayerElementAccess.hpp" -#include "FbxMaterialsAccess.hpp" #include "FbxSkinningAccess.hpp" +#include "materials/RoughnessMetallicMaterials.hpp" +#include "materials/TraditionalMaterials.hpp" float scaleFactor; @@ -236,7 +237,7 @@ static void ReadMesh( FbxRoughMetMaterialInfo* fbxMatInfo = static_cast(fbxMaterial.get()); - maybeAddTexture(fbxMatInfo->texColor, RAW_TEXTURE_USAGE_ALBEDO); + maybeAddTexture(fbxMatInfo->texBaseColor, 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); @@ -244,11 +245,12 @@ static void ReadMesh( maybeAddTexture(fbxMatInfo->texAmbientOcclusion, RAW_TEXTURE_USAGE_OCCLUSION); rawMatProps.reset(new RawMetRoughMatProps( RAW_SHADING_MODEL_PBR_MET_ROUGH, - toVec4f(fbxMatInfo->colBase), - toVec3f(fbxMatInfo->colEmissive), + toVec4f(fbxMatInfo->baseColor), + toVec3f(fbxMatInfo->emissive), fbxMatInfo->emissiveIntensity, fbxMatInfo->metallic, - fbxMatInfo->roughness)); + fbxMatInfo->roughness, + fbxMatInfo->invertRoughnessMap)); } else { FbxTraditionalMaterialInfo* fbxMatInfo = static_cast(fbxMaterial.get()); diff --git a/src/fbx/FbxMaterialInfo.hpp b/src/fbx/FbxMaterialInfo.hpp deleted file mode 100644 index aa23ece..0000000 --- a/src/fbx/FbxMaterialInfo.hpp +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#pragma once - -#include "FBX2glTF.h" - -class FbxMaterialInfo { - public: - FbxMaterialInfo(const FbxString& name, const FbxString& shadingModel) - : name(name), shadingModel(shadingModel) {} - - const FbxString name; - const FbxString shadingModel; -}; diff --git a/src/fbx/FbxMaterialsAccess.cpp b/src/fbx/FbxMaterialsAccess.cpp deleted file mode 100644 index 5063a7b..0000000 --- a/src/fbx/FbxMaterialsAccess.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#include "FbxMaterialsAccess.hpp" -#include "Fbx2Raw.hpp" - -FbxMaterialsAccess::FbxMaterialsAccess( - const FbxMesh* pMesh, - const std::map& textureLocations) - : mappingMode(FbxGeometryElement::eNone), mesh(nullptr), indices(nullptr) { - if (pMesh->GetElementMaterialCount() <= 0) { - return; - } - - const FbxGeometryElement::EMappingMode materialMappingMode = - pMesh->GetElementMaterial()->GetMappingMode(); - if (materialMappingMode != FbxGeometryElement::eByPolygon && - materialMappingMode != FbxGeometryElement::eAllSame) { - return; - } - - const FbxGeometryElement::EReferenceMode materialReferenceMode = - pMesh->GetElementMaterial()->GetReferenceMode(); - if (materialReferenceMode != FbxGeometryElement::eIndexToDirect) { - return; - } - - mappingMode = materialMappingMode; - mesh = pMesh; - indices = &pMesh->GetElementMaterial()->GetIndexArray(); - - for (int ii = 0; ii < indices->GetCount(); ii++) { - int materialNum = indices->GetAt(ii); - if (materialNum < 0) { - continue; - } - - FbxSurfaceMaterial* surfaceMaterial = - mesh->GetNode()->GetSrcObject(materialNum); - - if (materialNum >= summaries.size()) { - summaries.resize(materialNum + 1); - } - auto summary = summaries[materialNum]; - if (summary == nullptr) { - summary = summaries[materialNum] = GetMaterialInfo(surfaceMaterial, textureLocations); - } - - if (materialNum >= userProperties.size()) { - userProperties.resize(materialNum + 1); - } - if (userProperties[materialNum].empty()) { - FbxProperty objectProperty = surfaceMaterial->GetFirstProperty(); - while (objectProperty.IsValid()) { - if (objectProperty.GetFlag(FbxPropertyFlags::eUserDefined)) { - userProperties[materialNum].push_back(TranscribeProperty(objectProperty).dump()); - } - objectProperty = surfaceMaterial->GetNextProperty(objectProperty); - } - } - } -} - -const std::shared_ptr FbxMaterialsAccess::GetMaterial( - const int polygonIndex) const { - if (mappingMode != FbxGeometryElement::eNone) { - const int materialNum = - indices->GetAt((mappingMode == FbxGeometryElement::eByPolygon) ? polygonIndex : 0); - if (materialNum < 0) { - return nullptr; - } - return summaries.at((unsigned long)materialNum); - } - return nullptr; -} - -const std::vector FbxMaterialsAccess::GetUserProperties(const int polygonIndex) const { - if (mappingMode != FbxGeometryElement::eNone) { - const int materialNum = - indices->GetAt((mappingMode == FbxGeometryElement::eByPolygon) ? polygonIndex : 0); - if (materialNum < 0) { - return std::vector(); - } - return userProperties.at((unsigned long)materialNum); - } - return std::vector(); -} - -std::unique_ptr FbxMaterialsAccess::GetMaterialInfo( - FbxSurfaceMaterial* material, - const std::map& textureLocations) { - std::unique_ptr res; - res = FbxRoughMetMaterialInfo::From(material, textureLocations); - if (!res) { - res = FbxTraditionalMaterialInfo::From(material, textureLocations); - } - return res; -} diff --git a/src/fbx/FbxMaterialsAccess.hpp b/src/fbx/FbxMaterialsAccess.hpp deleted file mode 100644 index 9e7c8a3..0000000 --- a/src/fbx/FbxMaterialsAccess.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#pragma once - -#include "Fbx2Raw.hpp" -#include "FbxMaterialInfo.hpp" -#include "FbxRoughMetMaterialInfo.hpp" -#include "FbxTraditionalMaterialInfo.hpp" - -class FbxMaterialsAccess { - public: - FbxMaterialsAccess( - const FbxMesh* pMesh, - const std::map& textureLocations); - - const std::shared_ptr GetMaterial(const int polygonIndex) const; - - const std::vector GetUserProperties(const int polygonIndex) const; - - std::unique_ptr GetMaterialInfo( - FbxSurfaceMaterial* material, - const std::map& textureLocations); - - private: - FbxGeometryElement::EMappingMode mappingMode; - std::vector> summaries{}; - std::vector> userProperties; - const FbxMesh* mesh; - const FbxLayerElementArrayTemplate* indices; -}; diff --git a/src/fbx/FbxRoughMetMaterialInfo.cpp b/src/fbx/FbxRoughMetMaterialInfo.cpp deleted file mode 100644 index 10271a6..0000000 --- a/src/fbx/FbxRoughMetMaterialInfo.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#include "FbxRoughMetMaterialInfo.hpp" - -std::unique_ptr FbxRoughMetMaterialInfo::From( - FbxSurfaceMaterial* fbxMaterial, - const std::map& textureLocations) { - std::unique_ptr res( - new FbxRoughMetMaterialInfo(fbxMaterial->GetName(), FBX_SHADER_METROUGH)); - - const FbxProperty mayaProp = fbxMaterial->FindProperty("Maya"); - if (mayaProp.GetPropertyDataType() != FbxCompoundDT) { - return nullptr; - } - if (!fbxMaterial->ShadingModel.Get().IsEmpty()) { - ::fmt::printf( - "Warning: Material %s has surprising shading model: %s\n", - fbxMaterial->GetName(), - fbxMaterial->ShadingModel.Get()); - } - - auto getTex = [&](std::string propName) { - const FbxFileTexture* ptr = nullptr; - - const FbxProperty useProp = mayaProp.FindHierarchical(("use_" + propName + "_map").c_str()); - if (useProp.IsValid() && useProp.Get()) { - const FbxProperty texProp = mayaProp.FindHierarchical(("TEX_" + propName + "_map").c_str()); - if (texProp.IsValid()) { - ptr = texProp.GetSrcObject(); - if (ptr != nullptr && textureLocations.find(ptr) == textureLocations.end()) { - ptr = nullptr; - } - } - } else if (verboseOutput && useProp.IsValid()) { - fmt::printf( - "Note: Property '%s' of material '%s' exists, but is flagged as 'do not use'.\n", - propName, - fbxMaterial->GetName()); - } - return ptr; - }; - - auto getVec = [&](std::string propName) -> FbxDouble3 { - const FbxProperty vecProp = mayaProp.FindHierarchical(propName.c_str()); - return vecProp.IsValid() ? vecProp.Get() : FbxDouble3(1, 1, 1); - }; - - auto getVal = [&](std::string propName) -> FbxDouble { - const FbxProperty vecProp = mayaProp.FindHierarchical(propName.c_str()); - return vecProp.IsValid() ? vecProp.Get() : 0; - }; - - res->texNormal = getTex("normal"); - res->texColor = getTex("color"); - res->colBase = getVec("base_color"); - res->texAmbientOcclusion = getTex("ao"); - res->texEmissive = getTex("emissive"); - res->colEmissive = getVec("emissive"); - res->emissiveIntensity = getVal("emissive_intensity"); - res->texMetallic = getTex("metallic"); - res->metallic = getVal("metallic"); - res->texRoughness = getTex("roughness"); - res->roughness = getVal("roughness"); - - return res; -} diff --git a/src/fbx/FbxRoughMetMaterialInfo.hpp b/src/fbx/FbxRoughMetMaterialInfo.hpp deleted file mode 100644 index f954c1d..0000000 --- a/src/fbx/FbxRoughMetMaterialInfo.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "FbxMaterialInfo.hpp" - -struct FbxRoughMetMaterialInfo : FbxMaterialInfo { - static constexpr const char* FBX_SHADER_METROUGH = "MetallicRoughness"; - - static std::unique_ptr From( - FbxSurfaceMaterial* fbxMaterial, - const std::map& textureLocations); - - FbxRoughMetMaterialInfo(const FbxString& name, const FbxString& shadingModel) - : FbxMaterialInfo(name, shadingModel) {} - - const FbxFileTexture* texColor{}; - FbxVector4 colBase{}; - const FbxFileTexture* texNormal{}; - const FbxFileTexture* texMetallic{}; - FbxDouble metallic{}; - const FbxFileTexture* texRoughness{}; - FbxDouble roughness{}; - const FbxFileTexture* texEmissive{}; - FbxVector4 colEmissive{}; - FbxDouble emissiveIntensity{}; - const FbxFileTexture* texAmbientOcclusion{}; -}; diff --git a/src/fbx/FbxTraditionalMaterialInfo.cpp b/src/fbx/FbxTraditionalMaterialInfo.cpp deleted file mode 100644 index 370973d..0000000 --- a/src/fbx/FbxTraditionalMaterialInfo.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#include "FbxTraditionalMaterialInfo.hpp" - -std::unique_ptr FbxTraditionalMaterialInfo::From( - FbxSurfaceMaterial* fbxMaterial, - const std::map& textureLocations) { - auto getSurfaceScalar = [&](const char* propName) -> std::tuple { - const FbxProperty prop = fbxMaterial->FindProperty(propName); - - FbxDouble val(0); - FbxFileTexture* tex = prop.GetSrcObject(); - if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) { - tex = nullptr; - } - if (tex == nullptr && prop.IsValid()) { - val = prop.Get(); - } - return std::make_tuple(val, tex); - }; - - auto getSurfaceVector = [&](const char* propName) -> std::tuple { - const FbxProperty prop = fbxMaterial->FindProperty(propName); - - FbxDouble3 val(1, 1, 1); - FbxFileTexture* tex = prop.GetSrcObject(); - if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) { - tex = nullptr; - } - if (tex == nullptr && prop.IsValid()) { - val = prop.Get(); - } - return std::make_tuple(val, tex); - }; - - auto getSurfaceValues = - [&](const char* colName, - const char* facName) -> std::tuple { - const FbxProperty colProp = fbxMaterial->FindProperty(colName); - const FbxProperty facProp = fbxMaterial->FindProperty(facName); - - FbxDouble3 colorVal(1, 1, 1); - FbxDouble factorVal(1); - - FbxFileTexture* colTex = colProp.GetSrcObject(); - if (colTex != nullptr && textureLocations.find(colTex) == textureLocations.end()) { - colTex = nullptr; - } - if (colTex == nullptr && colProp.IsValid()) { - colorVal = colProp.Get(); - } - FbxFileTexture* facTex = facProp.GetSrcObject(); - if (facTex != nullptr && textureLocations.find(facTex) == textureLocations.end()) { - facTex = nullptr; - } - if (facTex == nullptr && facProp.IsValid()) { - factorVal = facProp.Get(); - } - - auto val = FbxVector4( - colorVal[0] * factorVal, colorVal[1] * factorVal, colorVal[2] * factorVal, factorVal); - return std::make_tuple(val, colTex, facTex); - }; - - std::string name = fbxMaterial->GetName(); - std::unique_ptr 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 { - FbxFileTexture *colTex, *facTex; - FbxVector4 vec; - - std::tie(vec, colTex, facTex) = getSurfaceValues(colName, facName); - if (colTex) { - if (facTex) { - fmt::printf( - "Warning: Mat [%s]: Can't handle both %s and %s textures; discarding %s.\n", - name, - colName, - facName, - facName); - } - return std::make_tuple(vec, colTex); - } - return std::make_tuple(vec, facTex); - }; - - std::tie(res->colAmbient, res->texAmbient) = - handleBasicProperty(FbxSurfaceMaterial::sAmbient, FbxSurfaceMaterial::sAmbientFactor); - std::tie(res->colSpecular, res->texSpecular) = - handleBasicProperty(FbxSurfaceMaterial::sSpecular, FbxSurfaceMaterial::sSpecularFactor); - std::tie(res->colDiffuse, res->texDiffuse) = - handleBasicProperty(FbxSurfaceMaterial::sDiffuse, FbxSurfaceMaterial::sDiffuseFactor); - std::tie(res->colEmissive, res->texEmissive) = - handleBasicProperty(FbxSurfaceMaterial::sEmissive, FbxSurfaceMaterial::sEmissiveFactor); - - // the normal map can only ever be a map, ignore everything else - tie(std::ignore, res->texNormal) = getSurfaceVector(FbxSurfaceMaterial::sNormalMap); - - // shininess can be a map or a factor; afaict the map is always 'ShininessExponent' and the - // value is always found in 'Shininess' but only sometimes in 'ShininessExponent'. - tie(std::ignore, res->texShininess) = getSurfaceScalar("ShininessExponent"); - tie(res->shininess, std::ignore) = getSurfaceScalar("Shininess"); - - // for transparency we just want a constant vector value; - FbxVector4 transparency; - // extract any existing textures only so we can warn that we're throwing them away - FbxFileTexture *colTex, *facTex; - std::tie(transparency, colTex, facTex) = getSurfaceValues( - FbxSurfaceMaterial::sTransparentColor, FbxSurfaceMaterial::sTransparencyFactor); - if (colTex) { - fmt::printf( - "Warning: Mat [%s]: Can't handle texture for %s; discarding.\n", - name, - FbxSurfaceMaterial::sTransparentColor); - } - if (facTex) { - fmt::printf( - "Warning: Mat [%s]: Can't handle texture for %s; discarding.\n", - name, - FbxSurfaceMaterial::sTransparencyFactor); - } - // FBX color is RGB, so we calculate the A channel as the average of the FBX transparency color - // vector - res->colDiffuse[3] = 1.0 - (transparency[0] + transparency[1] + transparency[2]) / 3.0; - - return res; -} diff --git a/src/fbx/FbxTraditionalMaterialInfo.hpp b/src/fbx/FbxTraditionalMaterialInfo.hpp deleted file mode 100644 index edb23b6..0000000 --- a/src/fbx/FbxTraditionalMaterialInfo.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "FbxMaterialInfo.hpp" - -struct FbxTraditionalMaterialInfo : FbxMaterialInfo { - static constexpr const char* FBX_SHADER_LAMBERT = "Lambert"; - static constexpr const char* FBX_SHADER_BLINN = "Blinn"; - static constexpr const char* FBX_SHADER_PHONG = "Phong"; - - FbxTraditionalMaterialInfo(const FbxString& name, const FbxString& shadingModel) - : FbxMaterialInfo(name, shadingModel) {} - - FbxFileTexture* texAmbient{}; - FbxVector4 colAmbient{}; - FbxFileTexture* texSpecular{}; - FbxVector4 colSpecular{}; - FbxFileTexture* texDiffuse{}; - FbxVector4 colDiffuse{}; - FbxFileTexture* texEmissive{}; - FbxVector4 colEmissive{}; - FbxFileTexture* texNormal{}; - FbxFileTexture* texShininess{}; - FbxDouble shininess{}; - - static std::unique_ptr From( - FbxSurfaceMaterial* fbxMaterial, - const std::map& textureLocations); -}; diff --git a/src/fbx/materials/3dsMaxPhysicalMaterial.cpp b/src/fbx/materials/3dsMaxPhysicalMaterial.cpp index deb0cc1..aa619cd 100644 --- a/src/fbx/materials/3dsMaxPhysicalMaterial.cpp +++ b/src/fbx/materials/3dsMaxPhysicalMaterial.cpp @@ -10,11 +10,14 @@ #include "RoughnessMetallicMaterials.hpp" std::unique_ptr Fbx3dsMaxPhysicalMaterialResolver::resolve() const { - const FbxProperty topProp = fbxMaterial->FindProperty("3dsMax"); + const FbxProperty topProp = fbxMaterial->FindProperty("3dsMax", false); if (topProp.GetPropertyDataType() != FbxCompoundDT) { return nullptr; } - const FbxProperty props = fbxMaterial->FindProperty("Parameters"); + const FbxProperty props = topProp.Find("Parameters", false); + if (!props.IsValid()) { + return nullptr; + } FbxString shadingModel = fbxMaterial->ShadingModel.Get(); if (!shadingModel.IsEmpty() && shadingModel != "unknown") { @@ -26,29 +29,21 @@ std::unique_ptr Fbx3dsMaxPhysicalMaterialResolver::reso auto getTex = [&](std::string propName) -> const FbxFileTexture* { const FbxFileTexture* ptr = nullptr; - - const FbxProperty useProp = props.FindHierarchical((propName + "_map_on").c_str()); - if (useProp.IsValid() && useProp.Get()) { - const FbxProperty texProp = useProp.FindHierarchical((propName + "_map").c_str()); - if (texProp.IsValid()) { - ptr = texProp.GetSrcObject(); - if (ptr != nullptr && textureLocations.find(ptr) == textureLocations.end()) { - ptr = nullptr; - } + const FbxProperty texProp = props.Find((propName + "_map").c_str(), false); + if (texProp.IsValid()) { + const FbxProperty useProp = props.Find((propName + "_map_on").c_str(), false); + if (useProp.IsValid() && !useProp.Get()) { + // skip this texture if the _on property exists *and* is explicitly false + return nullptr; + } + ptr = texProp.GetSrcObject(); + if (ptr != nullptr && textureLocations.find(ptr) == textureLocations.end()) { + ptr = nullptr; } - } else if (verboseOutput && useProp.IsValid()) { - fmt::printf( - "Note: property '%s' of 3dsMax Physical material '%s' exists, but is flagged as 'off'.\n", - propName, - fbxMaterial->GetName()); } return ptr; }; - int materialMode = getValue(props, "material_mode", 0); - fmt::printf("Note: 3dsMax Physical material has material_mode = %d.\n", materialMode); - - // baseWeight && baseColor FbxDouble baseWeight = getValue(props, "base_weight", 1.0); const auto* baseWeightMap = getTex("base_weight"); FbxDouble4 baseCol = getValue(props, "base_color", FbxDouble4(0.5, 0.5, 0.5, 1.0)); @@ -58,49 +53,100 @@ std::unique_ptr Fbx3dsMaxPhysicalMaterialResolver::reso const auto* emissiveWeightMap = getTex("emission"); FbxDouble4 emissiveColor = getValue(props, "emit_color", FbxDouble4(1, 1, 1, 1)); const auto* emissiveColorMap = getTex("emit_color"); - // TODO: emit_luminance, emit_kelvin? - // roughness & metalness: supported double roughness = getValue(props, "roughness", 0.0); const auto* roughnessMap = getTex("roughness"); double metalness = getValue(props, "metalness", 0.0); const auto* metalnessMap = getTex("metalness"); - // TODO: does invertRoughness affect roughness_map too? + // TODO: we need this to affect roughness map, too. bool invertRoughness = getValue(props, "inv_roughness", false); if (invertRoughness) { roughness = 1.0f - roughness; } - // TODO: attempt to bake transparency > 0.0f into the alpha of baseColour? + std::string unsupported; + const auto addUnsupported = [&](const std::string bit) { + if (!unsupported.empty()) { + unsupported += ", "; + } + unsupported += bit; + }; + + // TODO: turn this into a normal map through simple numerial differentiation + const auto* bumpMap = getTex("bump"); + if (bumpMap != nullptr) { + addUnsupported("bump map"); + } + + // TODO: bake transparency > 0.0f into the alpha of baseColor? double transparency = getValue(props, "transparency", 0.0); const auto* transparencyMap = getTex("transparency"); + if (transparency != 0.0 || transparencyMap != nullptr) { + addUnsupported("transparency"); + } - // SSS: not supported - double scattering = getValue(props, "scattering", 0.0); - const auto* scatteringMap = getTex("scattering"); + // TODO: if/when we bake transparency, we'll need this + // double transparencyDepth = getValue(props, "trans_depth", 0.0); + // if (transparencyDepth != 0.0) { + // addUnsupported("transparency depth"); + // } + // double transparencyColor = getValue(props, "trans_color", 0.0); + // const auto* transparencyColorMap = getTex("trans_color"); + // if (transparencyColor != 0.0 || transparencyColorMap != nullptr) { + // addUnsupported("transparency color"); + // } + // double thinWalledTransparency = getValue(props, "thin_walled", false); + // if (thinWalledTransparency) { + // addUnsupported("thin-walled transparency"); + // } - // reflectivity: not supported - double reflectivityWeight = getValue(props, "reflectivity", 1.); + const auto* displacementMap = getTex("displacement"); + if (displacementMap != nullptr) { + addUnsupported("displacement"); + } + + double reflectivityWeight = getValue(props, "reflectivity", 1.0); const auto* reflectivityWeightMap = getTex("reflectivity"); FbxDouble4 reflectivityColor = getValue(props, "refl_color", FbxDouble4(1, 1, 1, 1)); const auto* reflectivityColorMap = getTex("refl_color"); + if (reflectivityWeight != 1.0 || reflectivityWeightMap != nullptr || + reflectivityColor != FbxDouble4(1, 1, 1, 1) || reflectivityColorMap != nullptr) { + addUnsupported("reflectivity"); + } + + double scattering = getValue(props, "scattering", 0.0); + const auto* scatteringMap = getTex("scattering"); + if (scattering != 0.0 || scatteringMap != nullptr) { + addUnsupported("sub-surface scattering"); + } - // coatings: not supported double coating = getValue(props, "coating", 0.0); + if (coating != 0.0) { + addUnsupported("coating"); + } - // diffuse roughness: not supported - double diffuseRoughness = getValue(props, "diff_roughness", 0.); + double diffuseRoughness = getValue(props, "diff_roughness", 0.0); + if (diffuseRoughness != 0.0) { + addUnsupported("diffuse roughness"); + } - // explicit brdf curve control: not supported bool isBrdfMode = getValue(props, "brdf_mode", false); + if (isBrdfMode) { + addUnsupported("advanced reflectance custom curve"); + } - // anisotrophy: not supported double anisotropy = getValue(props, "anisotropy", 1.0); + if (anisotropy != 1.0) { + addUnsupported("anisotropy"); + } - // TODO: how the heck do we combine these to generate a normal map? - const auto* bumpMap = getTex("bump"); - const auto* displacementMap = getTex("displacement"); + if (verboseOutput && !unsupported.empty()) { + fmt::printf( + "Warning: 3dsMax Physical Material %s uses features glTF cannot express:\n %s\n", + fbxMaterial->GetName(), + unsupported); + } std::unique_ptr res(new FbxRoughMetMaterialInfo( fbxMaterial->GetName(), @@ -114,8 +160,7 @@ std::unique_ptr Fbx3dsMaxPhysicalMaterialResolver::reso res->texMetallic = metalnessMap; res->texRoughness = roughnessMap; - - res->texNormal = bumpMap; // TODO LOL NO NONO + res->invertRoughnessMap = invertRoughness; res->emissive = emissiveColor; res->emissiveIntensity = emissiveWeight; diff --git a/src/fbx/materials/FbxMaterials.cpp b/src/fbx/materials/FbxMaterials.cpp index 05237db..f7f5c07 100644 --- a/src/fbx/materials/FbxMaterials.cpp +++ b/src/fbx/materials/FbxMaterials.cpp @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#include "fbx/Fbx2Raw.hpp" + #include "FbxMaterials.hpp" #include "RoughnessMetallicMaterials.hpp" #include "TraditionalMaterials.hpp" @@ -41,13 +43,29 @@ FbxMaterialsAccess::FbxMaterialsAccess( if (materialNum < 0) { continue; } + + FbxSurfaceMaterial* surfaceMaterial = + mesh->GetNode()->GetSrcObject(materialNum); + if (materialNum >= summaries.size()) { summaries.resize(materialNum + 1); } auto summary = summaries[materialNum]; if (summary == nullptr) { - summary = summaries[materialNum] = GetMaterialInfo( - mesh->GetNode()->GetSrcObject(materialNum), textureLocations); + summary = summaries[materialNum] = GetMaterialInfo(surfaceMaterial, textureLocations); + } + + if (materialNum >= userProperties.size()) { + userProperties.resize(materialNum + 1); + } + if (userProperties[materialNum].empty()) { + FbxProperty objectProperty = surfaceMaterial->GetFirstProperty(); + while (objectProperty.IsValid()) { + if (objectProperty.GetFlag(FbxPropertyFlags::eUserDefined)) { + userProperties[materialNum].push_back(TranscribeProperty(objectProperty).dump()); + } + objectProperty = surfaceMaterial->GetNextProperty(objectProperty); + } } } } @@ -65,6 +83,18 @@ const std::shared_ptr FbxMaterialsAccess::GetMaterial( return nullptr; } +const std::vector FbxMaterialsAccess::GetUserProperties(const int polygonIndex) const { + if (mappingMode != FbxGeometryElement::eNone) { + const int materialNum = + indices->GetAt((mappingMode == FbxGeometryElement::eByPolygon) ? polygonIndex : 0); + if (materialNum < 0) { + return std::vector(); + } + return userProperties.at((unsigned long)materialNum); + } + return std::vector(); +} + std::unique_ptr FbxMaterialsAccess::GetMaterialInfo( FbxSurfaceMaterial* material, const std::map& textureLocations) { diff --git a/src/fbx/materials/FbxMaterials.hpp b/src/fbx/materials/FbxMaterials.hpp index 808d6a1..9141b78 100644 --- a/src/fbx/materials/FbxMaterials.hpp +++ b/src/fbx/materials/FbxMaterials.hpp @@ -45,6 +45,8 @@ class FbxMaterialsAccess { const std::shared_ptr GetMaterial(const int polygonIndex) const; + const std::vector GetUserProperties(const int polygonIndex) const; + std::unique_ptr GetMaterialInfo( FbxSurfaceMaterial* material, const std::map& textureLocations); @@ -52,6 +54,7 @@ class FbxMaterialsAccess { private: FbxGeometryElement::EMappingMode mappingMode; std::vector> summaries{}; + std::vector> userProperties; const FbxMesh* mesh; const FbxLayerElementArrayTemplate* indices; }; diff --git a/src/fbx/materials/RoughnessMetallicMaterials.hpp b/src/fbx/materials/RoughnessMetallicMaterials.hpp index b20a306..9123e28 100644 --- a/src/fbx/materials/RoughnessMetallicMaterials.hpp +++ b/src/fbx/materials/RoughnessMetallicMaterials.hpp @@ -35,6 +35,7 @@ struct FbxRoughMetMaterialInfo : FbxMaterialInfo { const FbxDouble metallic; const FbxDouble roughness; + FbxBool invertRoughnessMap = false; FbxDouble baseWeight = 1; FbxVector4 emissive = FbxVector4(0, 0, 0, 1); FbxDouble emissiveIntensity = 1; @@ -70,8 +71,8 @@ class Fbx3dsMaxPhysicalMaterialResolver : FbxMaterialResolver - T getValue(const FbxProperty& props, std::string propName, const T& default) const { + T getValue(const FbxProperty& props, std::string propName, const T& def) const { const FbxProperty prop = props.FindHierarchical(propName.c_str()); - return prop.IsValid() ? prop.Get() : default; + return prop.IsValid() ? prop.Get() : def; } }; diff --git a/src/gltf/Raw2Gltf.cpp b/src/gltf/Raw2Gltf.cpp index d14c725..6542641 100644 --- a/src/gltf/Raw2Gltf.cpp +++ b/src/gltf/Raw2Gltf.cpp @@ -276,7 +276,13 @@ ModelData* Raw2Gltf( }, "ao_met_rough", [&](const std::vector pixels) -> TextureBuilder::pixel { - return {{(*pixels[0])[0], (*pixels[2])[0], (*pixels[1])[0], 1}}; + const float occlusion = (*pixels[0])[0]; + const float metallic = (*pixels[1])[0]; + const float roughness = (*pixels[2])[0]; + return {{occlusion, + props->invertRoughnessMap ? 1.0f - roughness : roughness, + metallic, + 1}}; }, false); diff --git a/src/raw/RawModel.cpp b/src/raw/RawModel.cpp index 2a7fe12..38b5fc7 100644 --- a/src/raw/RawModel.cpp +++ b/src/raw/RawModel.cpp @@ -100,7 +100,10 @@ int RawModel::AddTexture( return -1; } for (size_t i = 0; i < textures.size(); i++) { - if (StringUtils::CompareNoCase(textures[i].name, name) == 0 && textures[i].usage == usage) { + // we allocate the struct even if the implementing image file is missing + if (textures[i].usage == usage && + StringUtils::CompareNoCase(textures[i].fileLocation, fileLocation) == 0 && + StringUtils::CompareNoCase(textures[i].name, name) == 0) { return (int)i; } } diff --git a/src/raw/RawModel.hpp b/src/raw/RawModel.hpp index df134fa..0b81903 100644 --- a/src/raw/RawModel.hpp +++ b/src/raw/RawModel.hpp @@ -234,18 +234,21 @@ struct RawMetRoughMatProps : RawMatProps { const Vec3f&& emissiveFactor, float emissiveIntensity, float metallic, - float roughness) + float roughness, + bool invertRoughnessMap) : RawMatProps(shadingModel), diffuseFactor(diffuseFactor), emissiveFactor(emissiveFactor), emissiveIntensity(emissiveIntensity), metallic(metallic), - roughness(roughness) {} + roughness(roughness), + invertRoughnessMap(invertRoughnessMap) {} const Vec4f diffuseFactor; const Vec3f emissiveFactor; const float emissiveIntensity; const float metallic; const float roughness; + const bool invertRoughnessMap; bool operator==(const RawMatProps& other) const override { if (RawMatProps::operator==(other)) {