diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c1cbf8..a6e838b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,24 +14,30 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}") include(ExternalProject) # FBX -find_package(FBX REQUIRED) +foreach (FBXSDK_VERSION "2019.0" "2018.1.1") + find_package(FBX) + if (FBXSDK_FOUND) + break() + endif() +endforeach(FBXSDK_VERSION) if (NOT FBXSDK_FOUND) message(FATAL_ERROR "Can't find FBX SDK in either:\n" " - Mac OS X: ${FBXSDK_APPLE_ROOT}\n" " - Windows: ${FBXSDK_WINDOWS_ROOT}\n" " - Linux: ${FBXSDK_LINUX_ROOT}" -) + ) endif() # DRACO ExternalProject_Add(Draco GIT_REPOSITORY https://github.com/google/draco - GIT_TAG 1.3.1 + GIT_TAG 1.3.4 PREFIX draco INSTALL_DIR CMAKE_ARGS - -DCMAKE_INSTALL_PREFIX= + -DCMAKE_INSTALL_PREFIX= + -DBUILD_FOR_GLTF=1 ) set(DRACO_INCLUDE_DIR "${CMAKE_BINARY_DIR}/draco/include") if (WIN32) @@ -129,54 +135,70 @@ if (APPLE) set(FRAMEWORKS ${CF_FRAMEWORK}) endif() -set(SOURCE_FILES +set(LIB_SOURCE_FILES src/FBX2glTF.h - src/Fbx2Raw.cpp - src/Fbx2Raw.h - src/Raw2Gltf.cpp - src/Raw2Gltf.h - src/RawModel.cpp - src/RawModel.h - src/glTF/AccessorData.cpp - src/glTF/AccessorData.h - src/glTF/AnimationData.cpp - src/glTF/AnimationData.h - src/glTF/BufferData.cpp - src/glTF/BufferData.h - src/glTF/BufferViewData.cpp - src/glTF/BufferViewData.h - src/glTF/CameraData.cpp - src/glTF/CameraData.h - src/glTF/ImageData.cpp - src/glTF/ImageData.h - src/glTF/MaterialData.cpp - src/glTF/MaterialData.h - src/glTF/MeshData.cpp - src/glTF/MeshData.h - src/glTF/NodeData.cpp - src/glTF/NodeData.h - src/glTF/PrimitiveData.cpp - src/glTF/PrimitiveData.h - src/glTF/SamplerData.h - src/glTF/SceneData.cpp - src/glTF/SceneData.h - src/glTF/SkinData.cpp - src/glTF/SkinData.h - src/glTF/TextureData.cpp - src/glTF/TextureData.h - src/main.cpp - src/mathfu.h + src/fbx/Fbx2Raw.cpp + src/fbx/Fbx2Raw.hpp + src/fbx/FbxLayerElementAccess.hpp + src/fbx/FbxBlendShapesAccess.hpp + src/fbx/FbxMaterialInfo.hpp + src/gltf/Raw2Gltf.cpp + src/gltf/Raw2Gltf.hpp + src/raw/RawModel.cpp + src/raw/RawModel.hpp + src/gltf/properties/AccessorData.cpp + src/gltf/properties/AccessorData.hpp + src/gltf/properties/AnimationData.cpp + src/gltf/properties/AnimationData.hpp + src/gltf/properties/BufferData.cpp + src/gltf/properties/BufferData.hpp + src/gltf/properties/BufferViewData.cpp + src/gltf/properties/BufferViewData.hpp + src/gltf/properties/CameraData.cpp + src/gltf/properties/CameraData.hpp + src/gltf/properties/ImageData.cpp + src/gltf/properties/ImageData.hpp + src/gltf/properties/MaterialData.cpp + src/gltf/properties/MaterialData.hpp + src/gltf/properties/MeshData.cpp + src/gltf/properties/MeshData.hpp + src/gltf/properties/NodeData.cpp + src/gltf/properties/NodeData.hpp + src/gltf/properties/PrimitiveData.cpp + src/gltf/properties/PrimitiveData.hpp + src/gltf/properties/SamplerData.hpp + src/gltf/properties/SceneData.cpp + src/gltf/properties/SceneData.hpp + src/gltf/properties/SkinData.cpp + src/gltf/properties/SkinData.hpp + src/gltf/properties/TextureData.cpp + src/gltf/properties/TextureData.hpp + src/mathfu.hpp src/utils/File_Utils.cpp - src/utils/File_Utils.h + src/utils/File_Utils.hpp src/utils/Image_Utils.cpp - src/utils/Image_Utils.h + src/utils/Image_Utils.hpp src/utils/String_Utils.cpp - src/utils/String_Utils.h + src/utils/String_Utils.hpp + src/fbx/FbxBlendShapesAccess.cpp + src/fbx/FbxBlendShapesAccess.hpp + src/fbx/FbxLayerElementAccess.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 ) -add_executable(FBX2glTF ${SOURCE_FILES}) +add_library(libFBX2glTF STATIC ${LIB_SOURCE_FILES}) +set_target_properties(libFBX2glTF PROPERTIES OUTPUT_NAME "FBX2glTF") +add_executable(appFBX2glTF src/FBX2glTF.cpp) +set_target_properties(appFBX2glTF PROPERTIES OUTPUT_NAME "FBX2glTF") -add_dependencies(FBX2glTF +add_dependencies(libFBX2glTF Draco MathFu FiFoMap @@ -189,33 +211,44 @@ add_dependencies(FBX2glTF if (NOT MSVC) # Disable annoying & spammy warning from FBX SDK header file - target_compile_options(FBX2glTF PRIVATE + target_compile_options(libFBX2glTF PRIVATE + "-Wno-null-dereference" + "-Wunused" + ) + target_compile_options(appFBX2glTF PRIVATE "-Wno-null-dereference" "-Wunused" ) endif() -target_link_libraries(FBX2glTF +target_link_libraries(libFBX2glTF ${FRAMEWORKS} - ${CMAKE_DL_LIBS} - ${CMAKE_THREAD_LIBS_INIT} ${DRACO_LIB} ${FMT_LIB} optimized ${FBXSDK_LIBRARY} debug ${FBXSDK_LIBRARY_DEBUG} + ${CMAKE_DL_LIBS} + ${CMAKE_THREAD_LIBS_INIT} ) -target_include_directories(FBX2glTF PUBLIC +target_include_directories(libFBX2glTF PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src ${FBXSDK_INCLUDE_DIR} ${DRACO_INCLUDE_DIR} ${MATHFU_INCLUDE_DIRS} ${FIFO_MAP_INCLUDE_DIR} ${JSON_INCLUDE_DIR} - ${CXXOPTS_INCLUDE_DIR} ${STB_INCLUDE_DIR} ${CPPCODEC_INCLUDE_DIR} ${FMT_INCLUDE_DIR} ) -install (TARGETS FBX2glTF DESTINATION bin) +target_include_directories(appFBX2glTF PUBLIC + ${CXXOPTS_INCLUDE_DIR} +) +target_link_libraries(appFBX2glTF libFBX2glTF) + +install (TARGETS libFBX2glTF appFBX2glTF + RUNTIME DESTINATION bin + ARCHIVE DESTINATION lib +) diff --git a/FindFBX.cmake b/FindFBX.cmake index 4244631..b187427 100644 --- a/FindFBX.cmake +++ b/FindFBX.cmake @@ -21,7 +21,9 @@ else() set(ARCH_32 OFF) endif() -set(FBXSDK_VERSION "2018.1.1" CACHE STRING "Precise version string of FBX SDK to use.") +if (NOT DEFINED FBXSDK_VERSION) + set(FBXSDK_VERSION "2018.1.1") +endif() set(_fbxsdk_vstudio_version "vs2015") diff --git a/models/Pinecone.glb b/models/Pinecone.glb new file mode 100644 index 0000000..a877761 Binary files /dev/null and b/models/Pinecone.glb differ diff --git a/models/SphereWithTangents.glb b/models/SphereWithTangents.glb new file mode 100644 index 0000000..8e329f4 Binary files /dev/null and b/models/SphereWithTangents.glb differ diff --git a/models/apple.glb b/models/apple.glb new file mode 100644 index 0000000..61e8822 Binary files /dev/null and b/models/apple.glb differ diff --git a/models/roughness.glb b/models/roughness.glb new file mode 100644 index 0000000..3cd971f Binary files /dev/null and b/models/roughness.glb differ diff --git a/models/shininess_example.glb b/models/shininess_example.glb new file mode 100644 index 0000000..c7198bf Binary files /dev/null and b/models/shininess_example.glb differ diff --git a/src/main.cpp b/src/FBX2glTF.cpp similarity index 99% rename from src/main.cpp rename to src/FBX2glTF.cpp index d608dd7..a01a48b 100644 --- a/src/main.cpp +++ b/src/FBX2glTF.cpp @@ -23,10 +23,10 @@ #include #include "FBX2glTF.h" -#include "utils/String_Utils.h" -#include "utils/File_Utils.h" -#include "Fbx2Raw.h" -#include "Raw2Gltf.h" +#include "utils/String_Utils.hpp" +#include "utils/File_Utils.hpp" +#include "fbx/Fbx2Raw.hpp" +#include "gltf/Raw2Gltf.hpp" bool verboseOutput = false; diff --git a/src/FBX2glTF.h b/src/FBX2glTF.h index 53b57ce..ed5cb09 100644 --- a/src/FBX2glTF.h +++ b/src/FBX2glTF.h @@ -7,8 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef __FBX2GLTF_H__ -#define __FBX2GLTF_H__ +#pragma once + +#include #if defined ( _WIN32 ) // Tell Windows not to define min() and max() macros @@ -16,7 +17,7 @@ #include #endif -const std::string FBX2GLTF_VERSION = "0.9.5"; +#define FBX2GLTF_VERSION std::string("0.9.5") #include #include @@ -26,6 +27,6 @@ const std::string FBX2GLTF_VERSION = "0.9.5"; #undef isnan #endif -#include "mathfu.h" +#include "mathfu.hpp" -#endif // !__FBX2GLTF_H__ +extern bool verboseOutput; diff --git a/src/Fbx2Raw.cpp b/src/fbx/Fbx2Raw.cpp similarity index 59% rename from src/Fbx2Raw.cpp rename to src/fbx/Fbx2Raw.cpp index c90140e..93e0ea7 100644 --- a/src/Fbx2Raw.cpp +++ b/src/fbx/Fbx2Raw.cpp @@ -20,658 +20,19 @@ #include #include "FBX2glTF.h" -#include "utils/File_Utils.h" -#include "utils/String_Utils.h" -#include "RawModel.h" -#include "Fbx2Raw.h" -extern bool verboseOutput; +#include "utils/File_Utils.hpp" +#include "utils/String_Utils.hpp" +#include "raw/RawModel.hpp" +#include "Fbx2Raw.hpp" + +#include "FbxBlendShapesAccess.hpp" +#include "FbxLayerElementAccess.hpp" +#include "FbxMaterialsAccess.hpp" +#include "FbxSkinningAccess.hpp" float scaleFactor; -template -class FbxLayerElementAccess -{ -public: - - FbxLayerElementAccess(const FbxLayerElementTemplate<_type_> *layer, int count) : - mappingMode(FbxGeometryElement::eNone), - elements(nullptr), - indices(nullptr) - { - if (count <= 0 || layer == nullptr) { - return; - } - const FbxGeometryElement::EMappingMode newMappingMode = layer->GetMappingMode(); - if (newMappingMode == FbxGeometryElement::eByControlPoint || - newMappingMode == FbxGeometryElement::eByPolygonVertex || - newMappingMode == FbxGeometryElement::eByPolygon) { - mappingMode = newMappingMode; - elements = &layer->GetDirectArray(); - indices = ( - layer->GetReferenceMode() == FbxGeometryElement::eIndexToDirect || - layer->GetReferenceMode() == FbxGeometryElement::eIndex) ? &layer->GetIndexArray() : nullptr; - } - } - - bool LayerPresent() const - { - return (mappingMode != FbxGeometryElement::eNone); - } - - _type_ GetElement(const int polygonIndex, const int polygonVertexIndex, const int controlPointIndex, const _type_ defaultValue) const - { - if (mappingMode != FbxGeometryElement::eNone) { - int index = (mappingMode == FbxGeometryElement::eByControlPoint) ? controlPointIndex : - ((mappingMode == FbxGeometryElement::eByPolygonVertex) ? polygonVertexIndex : polygonIndex); - index = (indices != nullptr) ? (*indices)[index] : index; - _type_ element = elements->GetAt(index); - return element; - } - return defaultValue; - } - - _type_ GetElement( - const int polygonIndex, const int polygonVertexIndex, const int controlPointIndex, const _type_ defaultValue, - const FbxMatrix &transform, const bool normalize) const - { - if (mappingMode != FbxGeometryElement::eNone) { - _type_ element = transform.MultNormalize(GetElement(polygonIndex, polygonVertexIndex, controlPointIndex, defaultValue)); - if (normalize) { - element.Normalize(); - } - return element; - } - return defaultValue; - } - -private: - FbxGeometryElement::EMappingMode mappingMode; - const FbxLayerElementArrayTemplate<_type_> *elements; - const FbxLayerElementArrayTemplate *indices; -}; - -struct FbxMaterialInfo { - FbxMaterialInfo(const FbxString &name, const FbxString &shadingModel) - : name(name), - shadingModel(shadingModel) - {} - const FbxString name; - const FbxString shadingModel; -}; - -struct FbxRoughMetMaterialInfo : FbxMaterialInfo { - static constexpr const char *FBX_SHADER_METROUGH = "MetallicRoughness"; - - 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 {}; - - static std::unique_ptr 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; - } -}; - -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) - { - 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 - std::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'. - std::tie(std::ignore, res->texShininess) = getSurfaceScalar("ShininessExponent"); - std::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; - } -}; - - -std::unique_ptr -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; -} - -class FbxMaterialsAccess -{ -public: - - 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; - } - if (materialNum >= summaries.size()) { - summaries.resize(materialNum + 1); - } - auto summary = summaries[materialNum]; - if (summary == nullptr) { - summary = summaries[materialNum] = GetMaterialInfo( - mesh->GetNode()->GetSrcObject(materialNum), - textureLocations); - } - } - } - - const std::shared_ptr 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; - } - -private: - FbxGeometryElement::EMappingMode mappingMode; - std::vector> summaries {}; - const FbxMesh *mesh; - const FbxLayerElementArrayTemplate *indices; -}; - -class FbxSkinningAccess -{ -public: - - static const int MAX_WEIGHTS = 4; - - FbxSkinningAccess(const FbxMesh *pMesh, FbxScene *pScene, FbxNode *pNode) - : rootIndex(-1) - { - for (int deformerIndex = 0; deformerIndex < pMesh->GetDeformerCount(); deformerIndex++) { - FbxSkin *skin = reinterpret_cast< FbxSkin * >( pMesh->GetDeformer(deformerIndex, FbxDeformer::eSkin)); - if (skin != nullptr) { - const int clusterCount = skin->GetClusterCount(); - if (clusterCount == 0) { - continue; - } - int controlPointCount = pMesh->GetControlPointsCount(); - vertexJointIndices.resize(controlPointCount, Vec4i(0, 0, 0, 0)); - vertexJointWeights.resize(controlPointCount, Vec4f(0.0f, 0.0f, 0.0f, 0.0f)); - - for (int clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++) { - FbxCluster *cluster = skin->GetCluster(clusterIndex); - const int indexCount = cluster->GetControlPointIndicesCount(); - const int *clusterIndices = cluster->GetControlPointIndices(); - const double *clusterWeights = cluster->GetControlPointWeights(); - - assert(cluster->GetLinkMode() == FbxCluster::eNormalize); - - // Transform link matrix. - FbxAMatrix transformLinkMatrix; - cluster->GetTransformLinkMatrix(transformLinkMatrix); - - // The transformation of the mesh at binding time - FbxAMatrix transformMatrix; - cluster->GetTransformMatrix(transformMatrix); - - // Inverse bind matrix. - FbxAMatrix globalBindposeInverseMatrix = transformLinkMatrix.Inverse() * transformMatrix; - inverseBindMatrices.emplace_back(globalBindposeInverseMatrix); - - jointNodes.push_back(cluster->GetLink()); - jointIds.push_back(cluster->GetLink()->GetUniqueID()); - - const FbxAMatrix globalNodeTransform = cluster->GetLink()->EvaluateGlobalTransform(); - jointSkinningTransforms.push_back(FbxMatrix(globalNodeTransform * globalBindposeInverseMatrix)); - jointInverseGlobalTransforms.push_back(FbxMatrix(globalNodeTransform.Inverse())); - - for (int i = 0; i < indexCount; i++) { - if (clusterIndices[i] < 0 || clusterIndices[i] >= controlPointCount) { - continue; - } - if (clusterWeights[i] <= vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1]) { - continue; - } - vertexJointIndices[clusterIndices[i]][MAX_WEIGHTS - 1] = clusterIndex; - vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1] = (float) clusterWeights[i]; - for (int j = MAX_WEIGHTS - 1; j > 0; j--) { - if (vertexJointWeights[clusterIndices[i]][j - 1] >= vertexJointWeights[clusterIndices[i]][j]) { - break; - } - std::swap(vertexJointIndices[clusterIndices[i]][j - 1], vertexJointIndices[clusterIndices[i]][j]); - std::swap(vertexJointWeights[clusterIndices[i]][j - 1], vertexJointWeights[clusterIndices[i]][j]); - } - } - - } - for (int i = 0; i < controlPointCount; i++) { - vertexJointWeights[i] = vertexJointWeights[i].Normalized(); - } - } - } - - rootIndex = -1; - for (size_t i = 0; i < jointNodes.size() && rootIndex == -1; i++) { - rootIndex = (int) i; - FbxNode *parent = jointNodes[i]->GetParent(); - if (parent == nullptr) { - break; - } - for (size_t j = 0; j < jointNodes.size(); j++) { - if (jointNodes[j] == parent) { - rootIndex = -1; - break; - } - } - } - } - - bool IsSkinned() const - { - return (vertexJointWeights.size() > 0); - } - - int GetNodeCount() const - { - return (int) jointNodes.size(); - } - - FbxNode *GetJointNode(const int jointIndex) const - { - return jointNodes[jointIndex]; - } - - const long GetJointId(const int jointIndex) const - { - return jointIds[jointIndex]; - } - - const FbxMatrix &GetJointSkinningTransform(const int jointIndex) const - { - return jointSkinningTransforms[jointIndex]; - } - - const FbxMatrix &GetJointInverseGlobalTransforms(const int jointIndex) const - { - return jointInverseGlobalTransforms[jointIndex]; - } - - const long GetRootNode() const - { - assert(rootIndex != -1); - return jointIds[rootIndex]; - } - - const FbxAMatrix &GetInverseBindMatrix(const int jointIndex) const - { - return inverseBindMatrices[jointIndex]; - } - - const Vec4i GetVertexIndices(const int controlPointIndex) const - { - return (!vertexJointIndices.empty()) ? - vertexJointIndices[controlPointIndex] : Vec4i(0, 0, 0, 0); - } - - const Vec4f GetVertexWeights(const int controlPointIndex) const - { - return (!vertexJointWeights.empty()) ? - vertexJointWeights[controlPointIndex] : Vec4f(0, 0, 0, 0); - } - -private: - int rootIndex; - std::vector jointIds; - std::vector jointNodes; - std::vector jointSkinningTransforms; - std::vector jointInverseGlobalTransforms; - std::vector inverseBindMatrices; - std::vector vertexJointIndices; - std::vector vertexJointWeights; -}; - -/** - * At the FBX level, each Mesh can have a set of FbxBlendShape deformers; organisational units that contain no data - * of their own. The actual deformation is determined by one or more FbxBlendShapeChannels, whose influences are all - * additively applied to the mesh. In a simpler world, each such channel would extend each base vertex with alternate - * position, and optionally normal and tangent. - * - * It's not quite so simple, though. We also have progressive morphing, where one logical morph actually consists of - * several concrete ones, each applied in sequence. For us, this means each channel contains a sequence of FbxShapes - * (aka target shape); these are the actual data-holding entities that provide the alternate vertex attributes. As such - * a channel is given more weight, it moves from one target shape to another. - * - * The total number of alternate sets of attributes, then, is the total number of target shapes across all the channels - * of all the blend shapes of the mesh. - * - * Each animation in the scene stack can yield one or zero FbxAnimCurves per channel (not target shape). We evaluate - * these curves to get the weight of the channel: this weight is further introspected on to figure out which target - * shapes we're currently interpolation between. - */ -class FbxBlendShapesAccess -{ -public: - /** - * A target shape is on a 1:1 basis with the eventual glTF morph target, and is the object which contains the - * actual morphed vertex data. - */ - struct TargetShape - { - explicit TargetShape(const FbxShape *shape, FbxDouble fullWeight) : - shape(shape), - fullWeight(fullWeight), - count(shape->GetControlPointsCount()), - positions(shape->GetControlPoints()), - normals(FbxLayerElementAccess(shape->GetElementNormal(), shape->GetElementNormalCount())), - tangents(FbxLayerElementAccess(shape->GetElementTangent(), shape->GetElementTangentCount())) - {} - - const FbxShape *shape; - const FbxDouble fullWeight; - const unsigned int count; - const FbxVector4 *positions; - const FbxLayerElementAccess normals; - const FbxLayerElementAccess tangents; - }; - - /** - * A channel collects a sequence (often of length 1) of target shapes. - */ - struct BlendChannel - { - BlendChannel( - FbxMesh *mesh, - const unsigned int blendShapeIx, - const unsigned int channelIx, - const FbxDouble deformPercent, - const std::vector &targetShapes, - const std::string name - ) : mesh(mesh), - blendShapeIx(blendShapeIx), - channelIx(channelIx), - deformPercent(deformPercent), - targetShapes(targetShapes), - name(name) - {} - - FbxAnimCurve *ExtractAnimation(unsigned int animIx) const { - FbxAnimStack *stack = mesh->GetScene()->GetSrcObject(animIx); - FbxAnimLayer *layer = stack->GetMember(0); - return mesh->GetShapeChannel(blendShapeIx, channelIx, layer, true); - } - - FbxMesh *const mesh; - - const unsigned int blendShapeIx; - const unsigned int channelIx; - const std::vector targetShapes; - const std::string name; - - const FbxDouble deformPercent; - }; - - explicit FbxBlendShapesAccess(FbxMesh *mesh) : - channels(extractChannels(mesh)) - { } - - size_t GetChannelCount() const { return channels.size(); } - const BlendChannel &GetBlendChannel(size_t channelIx) const { - return channels.at(channelIx); - } - - size_t GetTargetShapeCount(size_t channelIx) const { return channels[channelIx].targetShapes.size(); } - const TargetShape &GetTargetShape(size_t channelIx, size_t targetShapeIx) const { - return channels.at(channelIx).targetShapes[targetShapeIx]; - } - - FbxAnimCurve * GetAnimation(size_t channelIx, size_t animIx) const { - return channels.at(channelIx).ExtractAnimation(animIx); - } - -private: - std::vector extractChannels(FbxMesh *mesh) const { - std::vector channels; - for (int shapeIx = 0; shapeIx < mesh->GetDeformerCount(FbxDeformer::eBlendShape); shapeIx++) { - auto *fbxBlendShape = static_cast(mesh->GetDeformer(shapeIx, FbxDeformer::eBlendShape)); - - for (int channelIx = 0; channelIx < fbxBlendShape->GetBlendShapeChannelCount(); ++channelIx) { - FbxBlendShapeChannel *fbxChannel = fbxBlendShape->GetBlendShapeChannel(channelIx); - - if (fbxChannel->GetTargetShapeCount() > 0) { - std::vector targetShapes; - const double *fullWeights = fbxChannel->GetTargetShapeFullWeights(); - std::string name = std::string(fbxChannel->GetName()); - - if (verboseOutput) { - fmt::printf("\rblendshape channel: %s\n", name); - } - - for (int targetIx = 0; targetIx < fbxChannel->GetTargetShapeCount(); targetIx ++) { - FbxShape *fbxShape = fbxChannel->GetTargetShape(targetIx); - targetShapes.push_back(TargetShape(fbxShape, fullWeights[targetIx])); - } - channels.push_back(BlendChannel(mesh, shapeIx, channelIx, fbxChannel->DeformPercent * 0.01, targetShapes, name)); - } - } - } - return channels; - } - - const std::vector channels; -}; - static bool TriangleTexturePolarity(const Vec2f &uv0, const Vec2f &uv1, const Vec2f &uv2) { const Vec2f d0 = uv1 - uv0; @@ -945,7 +306,7 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std: } if (skinning.IsSkinned()) { - const int jointIndices[FbxSkinningAccess::MAX_WEIGHTS] = { + const int jointIndices[FbxSkinningAccess::MAX_WEIGHTS] = { vertex.jointIndices[0], vertex.jointIndices[1], vertex.jointIndices[2], @@ -957,7 +318,7 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std: vertex.jointWeights[2], vertex.jointWeights[3] }; - const FbxMatrix skinningMatrix = + const FbxMatrix skinningMatrix = skinning.GetJointSkinningTransform(jointIndices[0]) * jointWeights[0] + skinning.GetJointSkinningTransform(jointIndices[1]) * jointWeights[1] + skinning.GetJointSkinningTransform(jointIndices[2]) * jointWeights[2] + diff --git a/src/Fbx2Raw.h b/src/fbx/Fbx2Raw.hpp similarity index 81% rename from src/Fbx2Raw.h rename to src/fbx/Fbx2Raw.hpp index 935da9d..7bb125e 100644 --- a/src/Fbx2Raw.h +++ b/src/fbx/Fbx2Raw.hpp @@ -7,11 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef __FBX2RAW_H__ -#define __FBX2RAW_H__ +#pragma once -#include "RawModel.h" +#include "raw/RawModel.hpp" bool LoadFBXFile(RawModel &raw, const char *fbxFileName, const char *textureExtensions); - -#endif // !__FBX2RAW_H__ diff --git a/src/fbx/FbxBlendShapesAccess.cpp b/src/fbx/FbxBlendShapesAccess.cpp new file mode 100644 index 0000000..141f8c4 --- /dev/null +++ b/src/fbx/FbxBlendShapesAccess.cpp @@ -0,0 +1,58 @@ +/** + * 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 "FbxBlendShapesAccess.hpp" + +FbxBlendShapesAccess::TargetShape::TargetShape(const FbxShape *shape, FbxDouble fullWeight) : + shape(shape), + fullWeight(fullWeight), + count(shape->GetControlPointsCount()), + positions(shape->GetControlPoints()), + normals(FbxLayerElementAccess(shape->GetElementNormal(), shape->GetElementNormalCount())), + tangents(FbxLayerElementAccess(shape->GetElementTangent(), shape->GetElementTangentCount())) +{} + +FbxAnimCurve *FbxBlendShapesAccess::BlendChannel::ExtractAnimation(unsigned int animIx) const +{ + FbxAnimStack *stack = mesh->GetScene()->GetSrcObject(animIx); + FbxAnimLayer *layer = stack->GetMember(0); + return mesh->GetShapeChannel(blendShapeIx, channelIx, layer, true); +} + +FbxBlendShapesAccess::BlendChannel::BlendChannel( + FbxMesh *mesh, const unsigned int blendShapeIx, const unsigned int channelIx, const FbxDouble deformPercent, + const std::vector &targetShapes) : mesh(mesh), + blendShapeIx(blendShapeIx), + channelIx(channelIx), + deformPercent(deformPercent), + targetShapes(targetShapes) +{} + +std::vector FbxBlendShapesAccess::extractChannels(FbxMesh *mesh) const +{ + std::vector channels; + for (int shapeIx = 0; shapeIx < mesh->GetDeformerCount(FbxDeformer::eBlendShape); shapeIx++) { + auto *fbxBlendShape = dynamic_cast(mesh->GetDeformer(shapeIx, FbxDeformer::eBlendShape)); + + for (int channelIx = 0; channelIx < fbxBlendShape->GetBlendShapeChannelCount(); ++channelIx) { + FbxBlendShapeChannel *fbxChannel = fbxBlendShape->GetBlendShapeChannel(channelIx); + + if (fbxChannel->GetTargetShapeCount() > 0) { + std::vector targetShapes; + const double *fullWeights = fbxChannel->GetTargetShapeFullWeights(); + for (int targetIx = 0; targetIx < fbxChannel->GetTargetShapeCount(); targetIx ++) { + FbxShape *fbxShape = fbxChannel->GetTargetShape(targetIx); + targetShapes.emplace_back(fbxShape, fullWeights[targetIx]); + } + channels.emplace_back(mesh, shapeIx, channelIx, fbxChannel->DeformPercent * 0.01, targetShapes); + } + } + } + return channels; +} diff --git a/src/fbx/FbxBlendShapesAccess.hpp b/src/fbx/FbxBlendShapesAccess.hpp new file mode 100644 index 0000000..484fd06 --- /dev/null +++ b/src/fbx/FbxBlendShapesAccess.hpp @@ -0,0 +1,103 @@ +/** + * 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 +#include +#include +#include + +#include "FBX2glTF.h" +#include "FbxLayerElementAccess.hpp" + +/** + * At the FBX level, each Mesh can have a set of FbxBlendShape deformers; organisational units that contain no data + * of their own. The actual deformation is determined by one or more FbxBlendShapeChannels, whose influences are all + * additively applied to the mesh. In a simpler world, each such channel would extend each base vertex with alternate + * position, and optionally normal and tangent. + * + * It's not quite so simple, though. We also have progressive morphing, where one logical morph actually consists of + * several concrete ones, each applied in sequence. For us, this means each channel contains a sequence of FbxShapes + * (aka target shape); these are the actual data-holding entities that provide the alternate vertex attributes. As such + * a channel is given more weight, it moves from one target shape to another. + * + * The total number of alternate sets of attributes, then, is the total number of target shapes across all the channels + * of all the blend shapes of the mesh. + * + * Each animation in the scene stack can yield one or zero FbxAnimCurves per channel (not target shape). We evaluate + * these curves to get the weight of the channel: this weight is further introspected on to figure out which target + * shapes we're currently interpolation between. + */ +class FbxBlendShapesAccess +{ +public: + /** + * A target shape is on a 1:1 basis with the eventual glTF morph target, and is the object which contains the + * actual morphed vertex data. + */ + struct TargetShape + { + explicit TargetShape(const FbxShape *shape, FbxDouble fullWeight); + + const FbxShape *shape; + const FbxDouble fullWeight; + const unsigned int count; + const FbxVector4 *positions; + const FbxLayerElementAccess normals; + const FbxLayerElementAccess tangents; + }; + + /** + * A channel collects a sequence (often of length 1) of target shapes. + */ + struct BlendChannel + { + BlendChannel( + FbxMesh *mesh, + const unsigned int blendShapeIx, + const unsigned int channelIx, + const FbxDouble deformPercent, + const std::vector &targetShapes + ); + + FbxAnimCurve *ExtractAnimation(unsigned int animIx) const; + + FbxMesh *const mesh; + + const unsigned int blendShapeIx; + const unsigned int channelIx; + const std::vector targetShapes; + + const FbxDouble deformPercent; + }; + + explicit FbxBlendShapesAccess(FbxMesh *mesh) : + channels(extractChannels(mesh)) + { } + + size_t GetChannelCount() const { return channels.size(); } + const BlendChannel &GetBlendChannel(size_t channelIx) const { + return channels.at(channelIx); + } + + size_t GetTargetShapeCount(size_t channelIx) const { return channels[channelIx].targetShapes.size(); } + const TargetShape &GetTargetShape(size_t channelIx, size_t targetShapeIx) const { + return channels.at(channelIx).targetShapes[targetShapeIx]; + } + + FbxAnimCurve * GetAnimation(size_t channelIx, size_t animIx) const { + return channels.at(channelIx).ExtractAnimation(animIx); + } + +private: + std::vector extractChannels(FbxMesh *mesh) const; + + const std::vector channels; +}; diff --git a/src/fbx/FbxLayerElementAccess.hpp b/src/fbx/FbxLayerElementAccess.hpp new file mode 100644 index 0000000..f35f74f --- /dev/null +++ b/src/fbx/FbxLayerElementAccess.hpp @@ -0,0 +1,83 @@ +/** + * 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" + +template +class FbxLayerElementAccess +{ +public: + + FbxLayerElementAccess(const FbxLayerElementTemplate<_type_> *layer, int count); + + bool LayerPresent() const + { + return (mappingMode != FbxLayerElement::eNone); + } + + _type_ GetElement(const int polygonIndex, const int polygonVertexIndex, const int controlPointIndex, const _type_ defaultValue) const; + _type_ GetElement( + const int polygonIndex, const int polygonVertexIndex, const int controlPointIndex, const _type_ defaultValue, + const FbxMatrix &transform, const bool normalize) const; + +private: + FbxLayerElement::EMappingMode mappingMode; + const FbxLayerElementArrayTemplate<_type_> *elements; + const FbxLayerElementArrayTemplate *indices; +}; + +template +FbxLayerElementAccess<_type_>::FbxLayerElementAccess(const FbxLayerElementTemplate<_type_> *layer, int count) : + mappingMode(FbxLayerElement::eNone), + elements(nullptr), + indices(nullptr) +{ + if (count <= 0 || layer == nullptr) { + return; + } + const FbxLayerElement::EMappingMode newMappingMode = layer->GetMappingMode(); + if (newMappingMode == FbxLayerElement::eByControlPoint || + newMappingMode == FbxLayerElement::eByPolygonVertex || + newMappingMode == FbxLayerElement::eByPolygon) { + mappingMode = newMappingMode; + elements = &layer->GetDirectArray(); + indices = ( + layer->GetReferenceMode() == FbxLayerElement::eIndexToDirect || + layer->GetReferenceMode() == FbxLayerElement::eIndex) ? &layer->GetIndexArray() : nullptr; + } +} + +template +_type_ FbxLayerElementAccess<_type_>::GetElement( + const int polygonIndex, const int polygonVertexIndex, const int controlPointIndex, const _type_ defaultValue) const +{ + if (mappingMode != FbxLayerElement::eNone) { + int index = (mappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : + ((mappingMode == FbxLayerElement::eByPolygonVertex) ? polygonVertexIndex : polygonIndex); + index = (indices != nullptr) ? (*indices)[index] : index; + _type_ element = elements->GetAt(index); + return element; + } + return defaultValue; +} + +template +_type_ FbxLayerElementAccess<_type_>::GetElement( + const int polygonIndex, const int polygonVertexIndex, const int controlPointIndex, const _type_ defaultValue, + const FbxMatrix &transform, const bool normalize) const +{ + if (mappingMode != FbxLayerElement::eNone) { + _type_ element = transform.MultNormalize(GetElement(polygonIndex, polygonVertexIndex, controlPointIndex, defaultValue)); + if (normalize) { + element.Normalize(); + } + return element; + } + return defaultValue; +} diff --git a/src/fbx/FbxMaterialInfo.hpp b/src/fbx/FbxMaterialInfo.hpp new file mode 100644 index 0000000..cb026e5 --- /dev/null +++ b/src/fbx/FbxMaterialInfo.hpp @@ -0,0 +1,22 @@ +/** + * 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 new file mode 100644 index 0000000..f8b27ad --- /dev/null +++ b/src/fbx/FbxMaterialsAccess.cpp @@ -0,0 +1,74 @@ +/** + * 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" + +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; + } + if (materialNum >= summaries.size()) { + summaries.resize(materialNum + 1); + } + auto summary = summaries[materialNum]; + if (summary == nullptr) { + summary = summaries[materialNum] = GetMaterialInfo( + mesh->GetNode()->GetSrcObject(materialNum), + textureLocations); + } + } +} + +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; +} + +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 new file mode 100644 index 0000000..33e6cf6 --- /dev/null +++ b/src/fbx/FbxMaterialsAccess.hpp @@ -0,0 +1,32 @@ +/** + * 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 "FbxMaterialInfo.hpp" +#include "FbxTraditionalMaterialInfo.hpp" +#include "FbxRoughMetMaterialInfo.hpp" + +class FbxMaterialsAccess +{ +public: + + FbxMaterialsAccess(const FbxMesh *pMesh, const std::map &textureLocations); + + const std::shared_ptr GetMaterial(const int polygonIndex) const; + + std::unique_ptr + GetMaterialInfo(FbxSurfaceMaterial *material, const std::map &textureLocations); + +private: + FbxGeometryElement::EMappingMode mappingMode; + std::vector> summaries {}; + const FbxMesh *mesh; + const FbxLayerElementArrayTemplate *indices; +}; diff --git a/src/fbx/FbxRoughMetMaterialInfo.cpp b/src/fbx/FbxRoughMetMaterialInfo.cpp new file mode 100644 index 0000000..6d3eb67 --- /dev/null +++ b/src/fbx/FbxRoughMetMaterialInfo.cpp @@ -0,0 +1,68 @@ +/** + * 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 new file mode 100644 index 0000000..0058744 --- /dev/null +++ b/src/fbx/FbxRoughMetMaterialInfo.hpp @@ -0,0 +1,42 @@ +/** + * 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/FbxSkinningAccess.cpp b/src/fbx/FbxSkinningAccess.cpp new file mode 100644 index 0000000..7279065 --- /dev/null +++ b/src/fbx/FbxSkinningAccess.cpp @@ -0,0 +1,92 @@ +/** + * 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 "FbxSkinningAccess.hpp" + +FbxSkinningAccess::FbxSkinningAccess(const FbxMesh *pMesh, FbxScene *pScene, FbxNode *pNode) + : rootIndex(-1) +{ + for (int deformerIndex = 0; deformerIndex < pMesh->GetDeformerCount(); deformerIndex++) { + FbxSkin *skin = reinterpret_cast< FbxSkin * >( pMesh->GetDeformer(deformerIndex, FbxDeformer::eSkin)); + if (skin != nullptr) { + const int clusterCount = skin->GetClusterCount(); + if (clusterCount == 0) { + continue; + } + int controlPointCount = pMesh->GetControlPointsCount(); + vertexJointIndices.resize(controlPointCount, Vec4i(0, 0, 0, 0)); + vertexJointWeights.resize(controlPointCount, Vec4f(0.0f, 0.0f, 0.0f, 0.0f)); + + for (int clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++) { + FbxCluster *cluster = skin->GetCluster(clusterIndex); + const int indexCount = cluster->GetControlPointIndicesCount(); + const int *clusterIndices = cluster->GetControlPointIndices(); + const double *clusterWeights = cluster->GetControlPointWeights(); + + assert(cluster->GetLinkMode() == FbxCluster::eNormalize); + + // Transform link matrix. + FbxAMatrix transformLinkMatrix; + cluster->GetTransformLinkMatrix(transformLinkMatrix); + + // The transformation of the mesh at binding time + FbxAMatrix transformMatrix; + cluster->GetTransformMatrix(transformMatrix); + + // Inverse bind matrix. + FbxAMatrix globalBindposeInverseMatrix = transformLinkMatrix.Inverse() * transformMatrix; + inverseBindMatrices.emplace_back(globalBindposeInverseMatrix); + + jointNodes.push_back(cluster->GetLink()); + jointIds.push_back(cluster->GetLink()->GetUniqueID()); + + const FbxAMatrix globalNodeTransform = cluster->GetLink()->EvaluateGlobalTransform(); + jointSkinningTransforms.push_back(FbxMatrix(globalNodeTransform * globalBindposeInverseMatrix)); + jointInverseGlobalTransforms.push_back(FbxMatrix(globalNodeTransform.Inverse())); + + for (int i = 0; i < indexCount; i++) { + if (clusterIndices[i] < 0 || clusterIndices[i] >= controlPointCount) { + continue; + } + if (clusterWeights[i] <= vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1]) { + continue; + } + vertexJointIndices[clusterIndices[i]][MAX_WEIGHTS - 1] = clusterIndex; + vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1] = (float) clusterWeights[i]; + for (int j = MAX_WEIGHTS - 1; j > 0; j--) { + if (vertexJointWeights[clusterIndices[i]][j - 1] >= vertexJointWeights[clusterIndices[i]][j]) { + break; + } + std::swap(vertexJointIndices[clusterIndices[i]][j - 1], vertexJointIndices[clusterIndices[i]][j]); + std::swap(vertexJointWeights[clusterIndices[i]][j - 1], vertexJointWeights[clusterIndices[i]][j]); + } + } + + } + for (int i = 0; i < controlPointCount; i++) { + vertexJointWeights[i] = vertexJointWeights[i].Normalized(); + } + } + } + + rootIndex = -1; + for (size_t i = 0; i < jointNodes.size() && rootIndex == -1; i++) { + rootIndex = (int) i; + FbxNode *parent = jointNodes[i]->GetParent(); + if (parent == nullptr) { + break; + } + for (size_t j = 0; j < jointNodes.size(); j++) { + if (jointNodes[j] == parent) { + rootIndex = -1; + break; + } + } + } +} diff --git a/src/fbx/FbxSkinningAccess.hpp b/src/fbx/FbxSkinningAccess.hpp new file mode 100644 index 0000000..cabf48b --- /dev/null +++ b/src/fbx/FbxSkinningAccess.hpp @@ -0,0 +1,92 @@ +/** + * 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 +#include +#include +#include +#include +#include +#include + +#include "FBX2glTF.h" + +class FbxSkinningAccess +{ +public: + + static const int MAX_WEIGHTS = 4; + + FbxSkinningAccess(const FbxMesh *pMesh, FbxScene *pScene, FbxNode *pNode); + + bool IsSkinned() const + { + return (vertexJointWeights.size() > 0); + } + + int GetNodeCount() const + { + return (int) jointNodes.size(); + } + + FbxNode *GetJointNode(const int jointIndex) const + { + return jointNodes[jointIndex]; + } + + const long GetJointId(const int jointIndex) const + { + return jointIds[jointIndex]; + } + + const FbxMatrix &GetJointSkinningTransform(const int jointIndex) const + { + return jointSkinningTransforms[jointIndex]; + } + + const FbxMatrix &GetJointInverseGlobalTransforms(const int jointIndex) const + { + return jointInverseGlobalTransforms[jointIndex]; + } + + const long GetRootNode() const + { + assert(rootIndex != -1); + return jointIds[rootIndex]; + } + + const FbxAMatrix &GetInverseBindMatrix(const int jointIndex) const + { + return inverseBindMatrices[jointIndex]; + } + + const Vec4i GetVertexIndices(const int controlPointIndex) const + { + return (!vertexJointIndices.empty()) ? + vertexJointIndices[controlPointIndex] : Vec4i(0, 0, 0, 0); + } + + const Vec4f GetVertexWeights(const int controlPointIndex) const + { + return (!vertexJointWeights.empty()) ? + vertexJointWeights[controlPointIndex] : Vec4f(0, 0, 0, 0); + } + +private: + int rootIndex; + std::vector jointIds; + std::vector jointNodes; + std::vector jointSkinningTransforms; + std::vector jointInverseGlobalTransforms; + std::vector inverseBindMatrices; + std::vector vertexJointIndices; + std::vector vertexJointWeights; +}; diff --git a/src/fbx/FbxTraditionalMaterialInfo.cpp b/src/fbx/FbxTraditionalMaterialInfo.cpp new file mode 100644 index 0000000..0b42713 --- /dev/null +++ b/src/fbx/FbxTraditionalMaterialInfo.cpp @@ -0,0 +1,124 @@ +/** + * 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 new file mode 100644 index 0000000..ed86387 --- /dev/null +++ b/src/fbx/FbxTraditionalMaterialInfo.hpp @@ -0,0 +1,44 @@ +/** + * 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 new file mode 100644 index 0000000..3a6d64e --- /dev/null +++ b/src/fbx/materials/3dsMaxPhysicalMaterial.cpp @@ -0,0 +1,125 @@ +/** + * 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 "RoughnessMetallicMaterials.hpp" + +std::unique_ptr Fbx3dsMaxPhysicalMaterialResolver::resolve() const { + const FbxProperty topProp = fbxMaterial->FindProperty("3dsMax"); + if (topProp.GetPropertyDataType() != FbxCompoundDT) { + return nullptr; + } + const FbxProperty props = fbxMaterial->FindProperty("Parameters"); + + FbxString shadingModel = fbxMaterial->ShadingModel.Get(); + if (!shadingModel.IsEmpty() && shadingModel != "unknown") { + ::fmt::printf("Warning: Material %s has surprising shading model: %s\n", + fbxMaterial->GetName(), shadingModel); + } + + 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; + } + } + } 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)); + const auto *baseTex = getTex("base_color"); + + double emissiveWeight = getValue(props, "emission", 0.0); + 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? + 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? + double transparency = getValue(props, "transparency", 0.0); + const auto *transparencyMap = getTex("transparency"); + + // SSS: not supported + double scattering = getValue(props, "scattering", 0.0); + const auto *scatteringMap = getTex("scattering"); + + // reflectivity: not supported + double reflectivityWeight = getValue(props, "reflectivity", 1.); + const auto *reflectivityWeightMap = getTex("reflectivity"); + FbxDouble4 reflectivityColor = getValue(props, "refl_color", FbxDouble4(1, 1, 1, 1)); + const auto *reflectivityColorMap = getTex("refl_color"); + + // coatings: not supported + double coating = getValue(props, "coating", 0.0); + + // diffuse roughness: not supported + double diffuseRoughness = getValue(props, "diff_roughness", 0.); + + // explicit brdf curve control: not supported + bool isBrdfMode = getValue(props, "brdf_mode", false); + + // anisotrophy: not supported + double anisotropy = getValue(props, "anisotropy", 1.0); + + // TODO: how the heck do we combine these to generate a normal map? + const auto *bumpMap = getTex("bump"); + const auto *displacementMap = getTex("displacement"); + + std::unique_ptr res( + new FbxRoughMetMaterialInfo( + fbxMaterial->GetName(), + FbxRoughMetMaterialInfo::FBX_SHADER_METROUGH, + baseCol, + metalness, + roughness + ) + ); + res->texBaseColor = baseTex; + res->baseWeight = baseWeight; + res->texBaseWeight = baseWeightMap; + + res->texMetallic = metalnessMap; + res->texRoughness = roughnessMap; + + res->texNormal = bumpMap; // TODO LOL NO NONO + + res->emissive = emissiveColor; + res->emissiveIntensity = emissiveWeight; + res->texEmissive = emissiveColorMap; + res->texEmissiveWeight = emissiveWeightMap; + + return res; +} diff --git a/src/fbx/materials/FbxMaterials.cpp b/src/fbx/materials/FbxMaterials.cpp new file mode 100644 index 0000000..542d837 --- /dev/null +++ b/src/fbx/materials/FbxMaterials.cpp @@ -0,0 +1,78 @@ +/** + * 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 "FbxMaterials.hpp" +#include "RoughnessMetallicMaterials.hpp" +#include "TraditionalMaterials.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; + } + if (materialNum >= summaries.size()) { + summaries.resize(materialNum + 1); + } + auto summary = summaries[materialNum]; + if (summary == nullptr) { + summary = summaries[materialNum] = GetMaterialInfo( + mesh->GetNode()->GetSrcObject(materialNum), + textureLocations); + } + } +} + +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; +} + +std::unique_ptr +FbxMaterialsAccess::GetMaterialInfo(FbxSurfaceMaterial *material, const std::map &textureLocations) +{ + std::unique_ptr res = FbxStingrayPBSMaterialResolver(material, textureLocations).resolve(); + if (res == nullptr) { + res = Fbx3dsMaxPhysicalMaterialResolver(material, textureLocations).resolve(); + if (res == nullptr) { + res = FbxTraditionalMaterialResolver(material, textureLocations).resolve(); + } + } + return res; +} + diff --git a/src/fbx/materials/FbxMaterials.hpp b/src/fbx/materials/FbxMaterials.hpp new file mode 100644 index 0000000..9bd3403 --- /dev/null +++ b/src/fbx/materials/FbxMaterials.hpp @@ -0,0 +1,60 @@ +/** + * 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 +#include + +#include "FBX2glTF.h" + +class FbxMaterialInfo { +public: + FbxMaterialInfo(const FbxString &name, const FbxString &shadingModel) + : name(name) + , shadingModel(shadingModel) + {} + + const FbxString name; + const FbxString shadingModel; +}; + +template +class FbxMaterialResolver +{ +public: + FbxMaterialResolver( + FbxSurfaceMaterial *fbxMaterial, + const std::map &textureLocations) + : fbxMaterial(fbxMaterial) + , textureLocations(textureLocations) + {} + virtual std::unique_ptr resolve() const = 0; + +protected: + const FbxSurfaceMaterial *fbxMaterial; + const std::map textureLocations; +}; + +class FbxMaterialsAccess +{ +public: + FbxMaterialsAccess(const FbxMesh *pMesh, const std::map &textureLocations); + + const std::shared_ptr GetMaterial(const int polygonIndex) const; + + std::unique_ptr + GetMaterialInfo(FbxSurfaceMaterial *material, const std::map &textureLocations); + +private: + FbxGeometryElement::EMappingMode mappingMode; + std::vector> summaries {}; + const FbxMesh *mesh; + const FbxLayerElementArrayTemplate *indices; +}; diff --git a/src/fbx/materials/RoughnessMetallicMaterials.hpp b/src/fbx/materials/RoughnessMetallicMaterials.hpp new file mode 100644 index 0000000..2fe95d9 --- /dev/null +++ b/src/fbx/materials/RoughnessMetallicMaterials.hpp @@ -0,0 +1,81 @@ +/** + * 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 + +#include "FbxMaterials.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, + FbxDouble4 baseColor, + FbxDouble metallic, + FbxDouble roughness + ) + : FbxMaterialInfo(name, shadingModel) + , baseColor(baseColor) + , metallic(metallic) + , roughness(roughness) + {} + + const FbxVector4 baseColor; + const FbxDouble metallic; + const FbxDouble roughness; + + FbxDouble baseWeight = 1; + FbxVector4 emissive = FbxVector4(0, 0, 0, 1); + FbxDouble emissiveIntensity = 1; + + const FbxFileTexture *texNormal = nullptr; + const FbxFileTexture *texBaseColor = nullptr; + const FbxFileTexture *texBaseWeight = nullptr; + const FbxFileTexture *texMetallic = nullptr; + const FbxFileTexture *texRoughness = nullptr; + const FbxFileTexture *texEmissive = nullptr; + const FbxFileTexture *texEmissiveWeight = nullptr; + const FbxFileTexture *texAmbientOcclusion = nullptr; +}; + +class FbxStingrayPBSMaterialResolver : FbxMaterialResolver { +public: + FbxStingrayPBSMaterialResolver( + FbxSurfaceMaterial *fbxMaterial, + const std::map &textureLocations) + : FbxMaterialResolver(fbxMaterial, textureLocations) + {} + + virtual std::unique_ptr resolve() const; +}; + +class Fbx3dsMaxPhysicalMaterialResolver : FbxMaterialResolver { +public: + Fbx3dsMaxPhysicalMaterialResolver( + FbxSurfaceMaterial *fbxMaterial, + const std::map &textureLocations) + : FbxMaterialResolver(fbxMaterial, textureLocations) + {} + + virtual std::unique_ptr resolve() const; + +private: + template + T getValue(const FbxProperty &props, std::string propName, const T& default) const { + const FbxProperty prop = props.FindHierarchical(propName.c_str()); + return prop.IsValid() ? prop.Get() : default; + } +}; diff --git a/src/fbx/materials/StingrayPBSMaterial.cpp b/src/fbx/materials/StingrayPBSMaterial.cpp new file mode 100644 index 0000000..5d757ad --- /dev/null +++ b/src/fbx/materials/StingrayPBSMaterial.cpp @@ -0,0 +1,71 @@ +/** + * 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 "RoughnessMetallicMaterials.hpp" + +std::unique_ptr FbxStingrayPBSMaterialResolver::resolve() const { + 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 Stingray PBS 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; + }; + + FbxDouble3 baseColor = getVec("base_color"); + std::unique_ptr res( + new FbxRoughMetMaterialInfo( + fbxMaterial->GetName(), + FbxRoughMetMaterialInfo::FBX_SHADER_METROUGH, + FbxDouble4(baseColor[0], baseColor[1], baseColor[2], 1), + getVal("metallic"), + getVal("roughness") + ) + ); + res->texNormal = getTex("normal"); + res->texBaseColor = getTex("color"); + res->texAmbientOcclusion = getTex("ao"); + res->texEmissive = getTex("emissive"); + res->emissive = getVec("emissive"); + res->emissiveIntensity = getVal("emissive_intensity"); + res->texMetallic = getTex("metallic"); + res->texRoughness = getTex("roughness"); + + return res; +}; diff --git a/src/fbx/materials/TraditionalMaterials.cpp b/src/fbx/materials/TraditionalMaterials.cpp new file mode 100644 index 0000000..23fe89f --- /dev/null +++ b/src/fbx/materials/TraditionalMaterials.cpp @@ -0,0 +1,123 @@ +/** + * 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 "TraditionalMaterials.hpp" + +std::unique_ptr FbxTraditionalMaterialResolver::resolve() const +{ + 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/materials/TraditionalMaterials.hpp b/src/fbx/materials/TraditionalMaterials.hpp new file mode 100644 index 0000000..12b08f9 --- /dev/null +++ b/src/fbx/materials/TraditionalMaterials.hpp @@ -0,0 +1,43 @@ +/** + * 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 "FbxMaterials.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 {}; +}; + +class FbxTraditionalMaterialResolver : FbxMaterialResolver { +public: + FbxTraditionalMaterialResolver( + FbxSurfaceMaterial *fbxMaterial, + const std::map &textureLocations) + : FbxMaterialResolver(fbxMaterial, textureLocations) + {} + + virtual std::unique_ptr resolve() const; +}; diff --git a/src/gltf/GltfModel.cpp b/src/gltf/GltfModel.cpp new file mode 100644 index 0000000..f01f994 --- /dev/null +++ b/src/gltf/GltfModel.cpp @@ -0,0 +1,10 @@ +/** +* 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 "GltfModel.hpp" diff --git a/src/gltf/GltfModel.hpp b/src/gltf/GltfModel.hpp new file mode 100644 index 0000000..ec4c05c --- /dev/null +++ b/src/gltf/GltfModel.hpp @@ -0,0 +1,189 @@ +/** +* 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" + +/** +* glTF 2.0 is based on the idea that data structs within a file are referenced by index; an accessor will +* point to the n:th buffer view, and so on. The Holder class takes a freshly instantiated class, and then +* creates, stored, and returns a shared_ptr for it. +* +* The idea is that every glTF resource in the file will live as long as the Holder does, and the Holders +* are all kept in the GLTFData struct. Clients may certainly cnhoose to perpetuate the full shared_ptr +* reference counting type, but generally speaking we pass around simple T& and T* types because the GLTFData +* struct will, by design, outlive all other activity that takes place during in a single conversion run. +*/ +template +struct Holder +{ + std::vector> ptrs; + std::shared_ptr hold(T *ptr) + { + ptr->ix = ptrs.size(); + ptrs.emplace_back(ptr); + return ptrs.back(); + } +}; + +struct GltfModel +{ + explicit GltfModel(bool _isGlb) + : binary(new std::vector), + isGlb(_isGlb) + { + } + + std::shared_ptr GetAlignedBufferView(BufferData &buffer, const BufferViewData::GL_ArrayType target) + { + unsigned long bufferSize = this->binary->size(); + if ((bufferSize % 4) > 0) { + bufferSize += (4 - (bufferSize % 4)); + this->binary->resize(bufferSize); + } + return this->bufferViews.hold(new BufferViewData(buffer, bufferSize, target)); + } + + // add a bufferview on the fly and copy data into it + std::shared_ptr AddRawBufferView(BufferData &buffer, const char *source, uint32_t bytes) + { + auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE); + bufferView->byteLength = bytes; + + // make space for the new bytes (possibly moving the underlying data) + unsigned long bufferSize = this->binary->size(); + this->binary->resize(bufferSize + bytes); + + // and copy them into place + memcpy(&(*this->binary)[bufferSize], source, bytes); + return bufferView; + } + + std::shared_ptr AddBufferViewForFile(BufferData &buffer, const std::string &filename) + { + // see if we've already created a BufferViewData for this precise file + auto iter = filenameToBufferView.find(filename); + if (iter != filenameToBufferView.end()) { + return iter->second; + } + + std::shared_ptr result; + std::ifstream file(filename, std::ios::binary | std::ios::ate); + if (file) { + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); + + std::vector fileBuffer(size); + if (file.read(fileBuffer.data(), size)) { + result = AddRawBufferView(buffer, fileBuffer.data(), size); + } else { + fmt::printf("Warning: Couldn't read %lu bytes from %s, skipping file.\n", size, filename); + } + } else { + fmt::printf("Warning: Couldn't open file %s, skipping file.\n", filename); + } + // note that we persist here not only success, but also failure, as nullptr + filenameToBufferView[filename] = result; + return result; + } + + + template + std::shared_ptr AddAccessorWithView( + BufferViewData &bufferView, const GLType &type, const std::vector &source) + { + auto accessor = accessors.hold(new AccessorData(bufferView, type)); + accessor->appendAsBinaryArray(source, *binary); + bufferView.byteLength = accessor->byteLength(); + return accessor; + } + + template + std::shared_ptr AddAccessorAndView( + BufferData &buffer, const GLType &type, const std::vector &source) + { + auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE); + return AddAccessorWithView(*bufferView, type, source); + } + + template + std::shared_ptr AddAttributeToPrimitive( + BufferData &buffer, const RawModel &surfaceModel, PrimitiveData &primitive, + const AttributeDefinition &attrDef) + { + // copy attribute data into vector + std::vector attribArr; + surfaceModel.GetAttributeArray(attribArr, attrDef.rawAttributeIx); + + std::shared_ptr accessor; + if (attrDef.dracoComponentType != draco::DT_INVALID && primitive.dracoMesh != nullptr) { + primitive.AddDracoAttrib(attrDef, attribArr); + + accessor = accessors.hold(new AccessorData(attrDef.glType)); + accessor->count = attribArr.size(); + } else { + auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER); + accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr); + } + primitive.AddAttrib(attrDef.gltfName, *accessor); + return accessor; + }; + + template + void serializeHolder(json &glTFJson, std::string key, const Holder holder) + { + if (!holder.ptrs.empty()) { + std::vector bits; + for (const auto &ptr : holder.ptrs) { + bits.push_back(ptr->serialize()); + } + glTFJson[key] = bits; + } + } + + void serializeHolders(json &glTFJson) + { + serializeHolder(glTFJson, "buffers", buffers); + serializeHolder(glTFJson, "bufferViews", bufferViews); + serializeHolder(glTFJson, "scenes", scenes); + serializeHolder(glTFJson, "accessors", accessors); + serializeHolder(glTFJson, "images", images); + serializeHolder(glTFJson, "samplers", samplers); + serializeHolder(glTFJson, "textures", textures); + serializeHolder(glTFJson, "materials", materials); + serializeHolder(glTFJson, "meshes", meshes); + serializeHolder(glTFJson, "skins", skins); + serializeHolder(glTFJson, "animations", animations); + serializeHolder(glTFJson, "cameras", cameras); + serializeHolder(glTFJson, "nodes", nodes); + } + + const bool isGlb; + + // cache BufferViewData instances that've already been created from a given filename + std::map> filenameToBufferView; + + std::shared_ptr > binary; + + + Holder buffers; + Holder bufferViews; + Holder accessors; + Holder images; + Holder samplers; + Holder textures; + Holder materials; + Holder meshes; + Holder skins; + Holder animations; + Holder cameras; + Holder nodes; + Holder scenes; +}; diff --git a/src/Raw2Gltf.cpp b/src/gltf/Raw2Gltf.cpp similarity index 97% rename from src/Raw2Gltf.cpp rename to src/gltf/Raw2Gltf.cpp index 00a8171..17f4309 100644 --- a/src/Raw2Gltf.cpp +++ b/src/gltf/Raw2Gltf.cpp @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#include "Raw2Gltf.hpp" + #include #include #include @@ -15,33 +17,29 @@ #include #include -#include "FBX2glTF.h" -#include "utils/String_Utils.h" -#include "utils/Image_Utils.h" -#include -#include "RawModel.h" -#include "Raw2Gltf.h" +#include "utils/String_Utils.hpp" +#include "utils/Image_Utils.hpp" +#include +#include "raw/RawModel.hpp" -#include "glTF/AccessorData.h" -#include "glTF/AnimationData.h" -#include "glTF/BufferData.h" -#include "glTF/BufferViewData.h" -#include "glTF/CameraData.h" -#include "glTF/ImageData.h" -#include "glTF/MaterialData.h" -#include "glTF/MeshData.h" -#include "glTF/NodeData.h" -#include "glTF/PrimitiveData.h" -#include "glTF/SamplerData.h" -#include "glTF/SceneData.h" -#include "glTF/SkinData.h" -#include "glTF/TextureData.h" +#include "gltf/properties/AccessorData.hpp" +#include "gltf/properties/AnimationData.hpp" +#include "gltf/properties/BufferData.hpp" +#include "gltf/properties/BufferViewData.hpp" +#include "gltf/properties/CameraData.hpp" +#include "gltf/properties/ImageData.hpp" +#include "gltf/properties/MaterialData.hpp" +#include "gltf/properties/MeshData.hpp" +#include "gltf/properties/NodeData.hpp" +#include "gltf/properties/PrimitiveData.hpp" +#include "gltf/properties/SamplerData.hpp" +#include "gltf/properties/SceneData.hpp" +#include "gltf/properties/SkinData.hpp" +#include "gltf/properties/TextureData.hpp" typedef uint32_t TriangleIndex; -extern bool verboseOutput; - -const static std::string defaultSceneName = "Root Scene"; +#define DEFAULT_SCENE_NAME "Root Scene" /** * glTF 2.0 is based on the idea that data structs within a file are referenced by index; an accessor will @@ -595,7 +593,7 @@ ModelData *Raw2Gltf( auto bufferView = gltf->AddBufferViewForFile(buffer, rawTexture.fileLocation); if (bufferView) { std::string suffix = StringUtils::GetFileSuffixString(rawTexture.fileLocation); - image = new ImageData(relativeFilename, *bufferView, suffixToMimeType(suffix)); + image = new ImageData(relativeFilename, *bufferView, ImageUtils::suffixToMimeType(suffix)); } } else if (!relativeFilename.empty()) { @@ -705,7 +703,7 @@ ModelData *Raw2Gltf( aoMetRoughTex = merge3Tex("ao_met_rough", RAW_TEXTURE_USAGE_OCCLUSION, RAW_TEXTURE_USAGE_METALLIC, RAW_TEXTURE_USAGE_ROUGHNESS, [&](const std::vector pixels) -> pixel { - return { (*pixels[0])[0], (*pixels[2])[0], (*pixels[1])[0], 1 }; + return { {(*pixels[0])[0], (*pixels[2])[0], (*pixels[1])[0], 1} }; }, false); baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO); @@ -748,7 +746,7 @@ ModelData *Raw2Gltf( [&](const std::vector pixels) -> pixel { // do not multiply with props->shininess; that doesn't work like the other factors. float shininess = props->shininess * (*pixels[0])[0]; - return { 0, getRoughness(shininess), metallic, 1 }; + return { {0, getRoughness(shininess), metallic, 1} }; }, false); if (aoMetRoughTex != nullptr) { @@ -1068,7 +1066,7 @@ ModelData *Raw2Gltf( } NodeData &rootNode = require(nodesById, raw.GetRootNode()); - const SceneData &rootScene = *gltf->scenes.hold(new SceneData(defaultSceneName, rootNode)); + const SceneData &rootScene = *gltf->scenes.hold(new SceneData(DEFAULT_SCENE_NAME, rootNode)); if (options.outputBinary) { // note: glTF binary is little-endian diff --git a/src/Raw2Gltf.h b/src/gltf/Raw2Gltf.hpp similarity index 98% rename from src/Raw2Gltf.h rename to src/gltf/Raw2Gltf.hpp index 142e242..612cb0d 100644 --- a/src/Raw2Gltf.h +++ b/src/gltf/Raw2Gltf.hpp @@ -7,8 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef __RAW2GLTF_H__ -#define __RAW2GLTF_H__ +#pragma once #include #include @@ -26,7 +25,7 @@ using workaround_fifo_map = nlohmann::fifo_map; #include "FBX2glTF.h" -#include "RawModel.h" +#include "raw/RawModel.hpp" const std::string KHR_DRACO_MESH_COMPRESSION = "KHR_draco_mesh_compression"; const std::string KHR_MATERIALS_CMN_UNLIT = "KHR_materials_unlit"; @@ -193,5 +192,3 @@ ModelData *Raw2Gltf( const RawModel &raw, const GltfOptions &options ); - -#endif // !__RAW2GLTF_H__ diff --git a/src/gltf/TextureBuilder.cpp b/src/gltf/TextureBuilder.cpp new file mode 100644 index 0000000..ff9c063 --- /dev/null +++ b/src/gltf/TextureBuilder.cpp @@ -0,0 +1,206 @@ +/** +* 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 "TextureBuilder.hpp" + +#include +#include + +#include +#include +#include + +// 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 {}; +}; + +std::shared_ptr TextureBuilder::combine( + const std::vector &ixVec, + const std::string &tag, + const pixel_merger &computePixel) +{ + const std::string key = texIndicesKey(ixVec, tag); + auto iter = textureByIndicesKey.find(key); + if (iter != textureByIndicesKey.end()) { + return iter->second; + } + + int width = -1, height = -1; + std::string mergedFilename = tag; + std::vector texes { }; + for (const int rawTexIx : ixVec) { + TexInfo info(rawTexIx); + if (rawTexIx >= 0) { + const RawTexture &rawTex = raw.GetTexture(rawTexIx); + const std::string &fileLoc = rawTex.fileLocation; + const std::string &name = StringUtils::GetFileBaseString(StringUtils::GetFileNameString(fileLoc)); + if (!fileLoc.empty()) { + info.pixels = stbi_load(fileLoc.c_str(), &info.width, &info.height, &info.channels, 0); + if (!info.pixels) { + fmt::printf("Warning: merge texture [%d](%s) could not be loaded.\n", + rawTexIx, + name); + } 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", + name, + info.width, info.height, width, height); + // this is bad enough that we abort the whole merge + return nullptr; + } + mergedFilename += "_" + name; + } + } + } + texes.push_back(info); + } + // at the moment, the best choice of filename is also the best choice of name + const std::string mergedName = mergedFilename; + + if (width < 0) { + // no textures to merge; bail + return nullptr; + } + // TODO: which channel combinations make sense in input files? + + // write 3 or 4 channels depending on whether or not we need transparency + int channels = transparentOutput ? 4 : 3; + + std::vector mergedPixels(static_cast(channels * width * height)); + std::vector pixels(texes.size()); + std::vector pixelPointers(texes.size()); + for (int xx = 0; xx < width; xx ++) { + for (int yy = 0; yy < height; yy ++) { + pixels.clear(); + for (int jj = 0; jj < texes.size(); jj ++) { + const TexInfo &tex = texes[jj]; + // each texture's structure will depend on its channel count + int ii = tex.channels * (xx + yy*width); + int kk = 0; + if (tex.pixels != nullptr) { + for (; kk < tex.channels; kk ++) { + pixels[jj][kk] = tex.pixels[ii++] / 255.0f; + } + } + for (; kk < pixels[jj].size(); kk ++) { + pixels[jj][kk] = 1.0f; + } + pixelPointers[jj] = &pixels[jj]; + } + const pixel merged = computePixel(pixelPointers); + int ii = channels * (xx + yy*width); + for (int jj = 0; jj < channels; jj ++) { + mergedPixels[ii + jj] = static_cast(fmax(0, fmin(255.0f, merged[jj] * 255.0f))); + } + } + } + + // write a .png iff we need transparency in the destination texture + bool png = transparentOutput; + + std::vector imgBuffer; + int res; + if (png) { + res = stbi_write_png_to_func(WriteToVectorContext, &imgBuffer, + width, height, channels, mergedPixels.data(), width * channels); + } else { + res = stbi_write_jpg_to_func(WriteToVectorContext, &imgBuffer, + width, height, channels, mergedPixels.data(), 80); + } + if (!res) { + fmt::printf("Warning: failed to generate merge texture '%s'.\n", mergedFilename); + return nullptr; + } + + ImageData *image; + if (options.outputBinary) { + const auto bufferView = gltf->AddRawBufferView(buffer, imgBuffer.data(), imgBuffer.size()); + return std::make_unique(mergedName, *bufferView, png ? "image/png" : "image/jpeg"); + } + const std::string imageFilename = mergedFilename + (png ? ".png" : ".jpg"); + const std::string imagePath = outputFolder + imageFilename; + FILE *fp = fopen(imagePath.c_str(), "wb"); + if (fp == nullptr) { + fmt::printf("Warning:: Couldn't write file '%s' for writing.\n", imagePath); + return nullptr; + } + + if (fwrite(imgBuffer.data(), imgBuffer.size(), 1, fp) != 1) { + fmt::printf("Warning: Failed to write %lu bytes to file '%s'.\n", imgBuffer.size(), imagePath); + fclose(fp); + return nullptr; + } + fclose(fp); + if (verboseOutput) { + fmt::printf("Wrote %lu bytes to texture '%s'.\n", imgBuffer.size(), imagePath); + } + std::shared_ptr texDat = gltf->textures.hold( + new TextureData(mergedName, defaultSampler, *image)); + textureByIndicesKey.insert(std::make_pair(key, texDat)); + + return std::make_unique(mergedName, imageFilename); +} + +/** Create a new TextureData for the given RawTexture index, or return a previously created one. */ +std::shared_ptr TextureBuilder::simple(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; + } + + const RawTexture &rawTexture = raw.GetTexture(rawTexIndex); + const std::string textureName = StringUtils::GetFileBaseString(rawTexture.name); + const std::string relativeFilename = StringUtils::GetFileNameString(rawTexture.fileLocation); + + ImageData *image = nullptr; + if (options.outputBinary) { + auto bufferView = gltf->AddBufferViewForFile(buffer, rawTexture.fileLocation); + if (bufferView) { + std::string suffix = StringUtils::GetFileSuffixString(rawTexture.fileLocation); + image = new ImageData(relativeFilename, *bufferView, ImageUtils::suffixToMimeType(suffix)); + } + + } else if (!relativeFilename.empty()) { + image = new ImageData(relativeFilename, relativeFilename); + std::string outputPath = outputFolder + relativeFilename; + if (FileUtils::CopyFile(rawTexture.fileLocation, outputPath)) { + if (verboseOutput) { + fmt::printf("Copied texture '%s' to output folder: %s\n", textureName, outputPath); + } + } else { + // no point commenting further on read/write error; CopyFile() does enough of that, and we + // certainly want to to add an image struct to the glTF JSON, with the correct relative path + // reference, even if the copy failed. + } + } + if (!image) { + // fallback is tiny transparent PNG + image = new ImageData( + textureName, + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==" + ); + } + + std::shared_ptr texDat = gltf->textures.hold( + new TextureData(textureName, defaultSampler, *gltf->images.hold(image))); + textureByIndicesKey.insert(std::make_pair(key, texDat)); + return texDat; + +} \ No newline at end of file diff --git a/src/gltf/TextureBuilder.hpp b/src/gltf/TextureBuilder.hpp new file mode 100644 index 0000000..0e4bc8e --- /dev/null +++ b/src/gltf/TextureBuilder.hpp @@ -0,0 +1,68 @@ +/** +* 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 + +#include "FBX2glTF.h" + +#include + +using pixel = std::array; // pixel components are floats in [0, 1] +using pixel_merger = std::function)>; + +class TextureBuilder +{ +public: + TextureBuilder(const RawModel &raw, GltfModel &gltf) + : raw(raw) + , gltf(gltf) + {} + ~TextureBuilder() {} + + std::shared_ptr combine( + const std::vector &ixVec, + const std::string &tag, + const pixel_merger &mergeFunction + ); + + std::shared_ptr simple(int rawTexIndex, const std::string &tag); + + static std::string texIndicesKey(const std::vector &ixVec, const std::string &tag) { + std::string result = tag; + for (int ix : ixVec) { + result += "_" + std::to_string(ix); + } + return result; + }; + + static std::string describeChannel(int channels) { + switch(channels) { + case 1: return "G"; + case 2: return "GA"; + case 3: return "RGB"; + case 4: return "RGBA"; + default: + return fmt::format("?%d?", channels); + } + }; + + static void WriteToVectorContext(void *context, void *data, int size) { + auto *vec = static_cast *>(context); + for (int ii = 0; ii < size; ii ++) { + vec->push_back(((char *) data)[ii]); + } + } + +private: + const RawModel &raw; + GltfModel &gltf; + std::map> textureByIndicesKey; +}; diff --git a/src/glTF/AccessorData.cpp b/src/gltf/properties/AccessorData.cpp similarity index 95% rename from src/glTF/AccessorData.cpp rename to src/gltf/properties/AccessorData.cpp index 7927289..64ea1f1 100644 --- a/src/glTF/AccessorData.cpp +++ b/src/gltf/properties/AccessorData.cpp @@ -7,8 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "AccessorData.h" -#include "BufferViewData.h" +#include "AccessorData.hpp" +#include "BufferViewData.hpp" AccessorData::AccessorData(const BufferViewData &bufferView, GLType type, std::string name) : Holdable(), diff --git a/src/glTF/AccessorData.h b/src/gltf/properties/AccessorData.hpp similarity index 91% rename from src/glTF/AccessorData.h rename to src/gltf/properties/AccessorData.hpp index 6daf5ec..685bc11 100644 --- a/src/glTF/AccessorData.h +++ b/src/gltf/properties/AccessorData.hpp @@ -7,10 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_ACCESSORDATA_H -#define FBX2GLTF_ACCESSORDATA_H +#pragma once -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" struct AccessorData : Holdable { @@ -45,5 +44,3 @@ struct AccessorData : Holdable std::vector max; std::string name; }; - -#endif //FBX2GLTF_ACCESSORDATA_H diff --git a/src/glTF/AnimationData.cpp b/src/gltf/properties/AnimationData.cpp similarity index 95% rename from src/glTF/AnimationData.cpp rename to src/gltf/properties/AnimationData.cpp index 60c2f35..9d97946 100644 --- a/src/glTF/AnimationData.cpp +++ b/src/gltf/properties/AnimationData.cpp @@ -7,12 +7,12 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "AnimationData.h" +#include "AnimationData.hpp" #include -#include "AccessorData.h" -#include "NodeData.h" +#include "AccessorData.hpp" +#include "NodeData.hpp" AnimationData::AnimationData(std::string name, const AccessorData &timeAccessor) : Holdable(), diff --git a/src/glTF/AnimationData.h b/src/gltf/properties/AnimationData.hpp similarity index 91% rename from src/glTF/AnimationData.h rename to src/gltf/properties/AnimationData.hpp index cf434e7..90c7d0a 100644 --- a/src/glTF/AnimationData.h +++ b/src/gltf/properties/AnimationData.hpp @@ -7,10 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_ANIMATIONDATA_H -#define FBX2GLTF_ANIMATIONDATA_H +#pragma once -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" struct AnimationData : Holdable { @@ -47,5 +46,3 @@ struct AnimationData : Holdable void to_json(json &j, const AnimationData::channel_t &data); void to_json(json &j, const AnimationData::sampler_t &data); - -#endif //FBX2GLTF_ANIMATIONDATA_H diff --git a/src/glTF/BufferData.cpp b/src/gltf/properties/BufferData.cpp similarity index 97% rename from src/glTF/BufferData.cpp rename to src/gltf/properties/BufferData.cpp index bac4656..eac3d31 100644 --- a/src/glTF/BufferData.cpp +++ b/src/gltf/properties/BufferData.cpp @@ -9,7 +9,7 @@ #include -#include "BufferData.h" +#include "BufferData.hpp" BufferData::BufferData(const std::shared_ptr > &binData) : Holdable(), diff --git a/src/glTF/BufferData.h b/src/gltf/properties/BufferData.hpp similarity index 87% rename from src/glTF/BufferData.h rename to src/gltf/properties/BufferData.hpp index d46baa0..0021184 100644 --- a/src/glTF/BufferData.h +++ b/src/gltf/properties/BufferData.hpp @@ -7,10 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_BUFFERDATA_H -#define FBX2GLTF_BUFFERDATA_H +#pragma once -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" struct BufferData : Holdable { @@ -24,6 +23,3 @@ struct BufferData : Holdable const std::string uri; const std::shared_ptr > binData; // TODO this is just weird }; - - -#endif //FBX2GLTF_BUFFERDATA_H diff --git a/src/glTF/BufferViewData.cpp b/src/gltf/properties/BufferViewData.cpp similarity index 93% rename from src/glTF/BufferViewData.cpp rename to src/gltf/properties/BufferViewData.cpp index c795bc8..8d2346a 100644 --- a/src/glTF/BufferViewData.cpp +++ b/src/gltf/properties/BufferViewData.cpp @@ -7,8 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "BufferViewData.h" -#include "BufferData.h" +#include "BufferViewData.hpp" +#include "BufferData.hpp" BufferViewData::BufferViewData(const BufferData &_buffer, const size_t _byteOffset, const GL_ArrayType _target) : Holdable(), diff --git a/src/glTF/BufferViewData.h b/src/gltf/properties/BufferViewData.hpp similarity index 87% rename from src/glTF/BufferViewData.h rename to src/gltf/properties/BufferViewData.hpp index 5f60300..dc46457 100644 --- a/src/glTF/BufferViewData.h +++ b/src/gltf/properties/BufferViewData.hpp @@ -7,10 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_BUFFERVIEW_H -#define FBX2GLTF_BUFFERVIEW_H +#pragma once -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" struct BufferViewData : Holdable { @@ -31,5 +30,3 @@ struct BufferViewData : Holdable unsigned int byteLength = 0; }; - -#endif //FBX2GLTF_BUFFERVIEW_H diff --git a/src/glTF/CameraData.cpp b/src/gltf/properties/CameraData.cpp similarity index 97% rename from src/glTF/CameraData.cpp rename to src/gltf/properties/CameraData.cpp index 2dc7220..d10febe 100644 --- a/src/glTF/CameraData.cpp +++ b/src/gltf/properties/CameraData.cpp @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "CameraData.h" +#include "CameraData.hpp" CameraData::CameraData() : Holdable(), diff --git a/src/glTF/CameraData.h b/src/gltf/properties/CameraData.hpp similarity index 84% rename from src/glTF/CameraData.h rename to src/gltf/properties/CameraData.hpp index bafe600..f3c70a8 100644 --- a/src/glTF/CameraData.h +++ b/src/gltf/properties/CameraData.hpp @@ -7,10 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_CAMERADATA_H -#define FBX2GLTF_CAMERADATA_H +#pragma once -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" // TODO: this class needs some work struct CameraData : Holdable @@ -27,5 +26,3 @@ struct CameraData : Holdable float znear; float zfar; }; - -#endif //FBX2GLTF_CAMERADATA_H diff --git a/src/glTF/ImageData.cpp b/src/gltf/properties/ImageData.cpp similarity index 94% rename from src/glTF/ImageData.cpp rename to src/gltf/properties/ImageData.cpp index e68a38e..210ddd2 100644 --- a/src/glTF/ImageData.cpp +++ b/src/gltf/properties/ImageData.cpp @@ -7,11 +7,11 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "ImageData.h" +#include "ImageData.hpp" #include -#include "BufferViewData.h" +#include "BufferViewData.hpp" ImageData::ImageData(std::string name, std::string uri) : Holdable(), diff --git a/src/glTF/ImageData.h b/src/gltf/properties/ImageData.hpp similarity index 86% rename from src/glTF/ImageData.h rename to src/gltf/properties/ImageData.hpp index 0be181a..5e62317 100644 --- a/src/glTF/ImageData.h +++ b/src/gltf/properties/ImageData.hpp @@ -7,10 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_IMAGEDATA_H -#define FBX2GLTF_IMAGEDATA_H +#pragma once -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" struct ImageData : Holdable { @@ -24,5 +23,3 @@ struct ImageData : Holdable const int32_t bufferView; // non-negative in glb mode const std::string mimeType; }; - -#endif //FBX2GLTF_IMAGEDATA_H diff --git a/src/glTF/MaterialData.cpp b/src/gltf/properties/MaterialData.cpp similarity index 98% rename from src/glTF/MaterialData.cpp rename to src/gltf/properties/MaterialData.cpp index 4a28d26..f61ef12 100644 --- a/src/glTF/MaterialData.cpp +++ b/src/gltf/properties/MaterialData.cpp @@ -7,8 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "MaterialData.h" -#include "TextureData.h" +#include "MaterialData.hpp" +#include "TextureData.hpp" // TODO: retrieve & pass in correct UV set from FBX std::unique_ptr Tex::ref(const TextureData *tex, uint32_t texCoord) diff --git a/src/glTF/MaterialData.h b/src/gltf/properties/MaterialData.hpp similarity index 94% rename from src/glTF/MaterialData.h rename to src/gltf/properties/MaterialData.hpp index 170c093..a168e5f 100644 --- a/src/glTF/MaterialData.h +++ b/src/gltf/properties/MaterialData.hpp @@ -7,12 +7,11 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_MATERIALDATA_H -#define FBX2GLTF_MATERIALDATA_H +#pragma once #include -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" struct Tex { @@ -67,5 +66,3 @@ struct MaterialData : Holdable void to_json(json &j, const Tex &data); void to_json(json &j, const KHRCmnUnlitMaterial &d); void to_json(json &j, const PBRMetallicRoughness &d); - -#endif //FBX2GLTF_MATERIALDATA_H diff --git a/src/glTF/MeshData.cpp b/src/gltf/properties/MeshData.cpp similarity index 93% rename from src/glTF/MeshData.cpp rename to src/gltf/properties/MeshData.cpp index a236c53..cf31e89 100644 --- a/src/glTF/MeshData.cpp +++ b/src/gltf/properties/MeshData.cpp @@ -7,8 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "MeshData.h" -#include "PrimitiveData.h" +#include "MeshData.hpp" +#include "PrimitiveData.hpp" MeshData::MeshData(const std::string &name, const std::vector &weights) : Holdable(), diff --git a/src/glTF/MeshData.h b/src/gltf/properties/MeshData.hpp similarity index 85% rename from src/glTF/MeshData.h rename to src/gltf/properties/MeshData.hpp index a88f0c4..2f3658a 100644 --- a/src/glTF/MeshData.h +++ b/src/gltf/properties/MeshData.hpp @@ -7,16 +7,15 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_MESHDATA_H -#define FBX2GLTF_MESHDATA_H +#pragma once #include #include -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" -#include "PrimitiveData.h" +#include "PrimitiveData.hpp" struct MeshData : Holdable { @@ -33,5 +32,3 @@ struct MeshData : Holdable const std::vector weights; std::vector> primitives; }; - -#endif //FBX2GLTF_MESHDATA_H diff --git a/src/glTF/NodeData.cpp b/src/gltf/properties/NodeData.cpp similarity index 98% rename from src/glTF/NodeData.cpp rename to src/gltf/properties/NodeData.cpp index e582126..cba9e77 100644 --- a/src/glTF/NodeData.cpp +++ b/src/gltf/properties/NodeData.cpp @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "NodeData.h" +#include "NodeData.hpp" NodeData::NodeData( std::string name, const Vec3f &translation, @@ -55,7 +55,7 @@ json NodeData::serialize() const json result = { { "name", name } }; // if any of the T/R/S have NaN components, just leave them out of the glTF - auto maybeAdd = [&](std::string key, std::vector vec) { + auto maybeAdd = [&](std::string key, std::vector vec) -> void { if (std::none_of(vec.begin(), vec.end(), [&](float n) { return isnan(n); })) { result[key] = vec; } diff --git a/src/glTF/NodeData.h b/src/gltf/properties/NodeData.hpp similarity index 90% rename from src/glTF/NodeData.h rename to src/gltf/properties/NodeData.hpp index 6fdd2e7..fb350fa 100644 --- a/src/glTF/NodeData.h +++ b/src/gltf/properties/NodeData.hpp @@ -7,10 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_NODEDATA_H -#define FBX2GLTF_NODEDATA_H +#pragma once -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" struct NodeData : Holdable { @@ -34,5 +33,3 @@ struct NodeData : Holdable int32_t skin; std::vector skeletons; }; - -#endif //FBX2GLTF_NODEDATA_H diff --git a/src/glTF/PrimitiveData.cpp b/src/gltf/properties/PrimitiveData.cpp similarity index 91% rename from src/glTF/PrimitiveData.cpp rename to src/gltf/properties/PrimitiveData.cpp index 54c504a..b0b8d82 100644 --- a/src/glTF/PrimitiveData.cpp +++ b/src/gltf/properties/PrimitiveData.cpp @@ -7,11 +7,11 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "PrimitiveData.h" +#include "PrimitiveData.hpp" -#include "MaterialData.h" -#include "AccessorData.h" -#include "BufferViewData.h" +#include "MaterialData.hpp" +#include "AccessorData.hpp" +#include "BufferViewData.hpp" PrimitiveData::PrimitiveData(const AccessorData &indices, const MaterialData &material, std::shared_ptr dracoMesh) : indices(indices.ix), @@ -43,8 +43,8 @@ void PrimitiveData::AddTarget(const AccessorData *positions, const AccessorData { targetAccessors.push_back(std::make_tuple( positions->ix, - normals ? normals->ix : -1, - tangents ? tangents ->ix : -1 + normals != nullptr ? normals->ix : -1, + tangents != nullptr ? tangents ->ix : -1 )); } diff --git a/src/glTF/PrimitiveData.h b/src/gltf/properties/PrimitiveData.hpp similarity index 94% rename from src/glTF/PrimitiveData.h rename to src/gltf/properties/PrimitiveData.hpp index 892c662..64fc2a3 100644 --- a/src/glTF/PrimitiveData.h +++ b/src/gltf/properties/PrimitiveData.hpp @@ -7,10 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_PRIMITIVEDATA_H -#define FBX2GLTF_PRIMITIVEDATA_H +#pragma once -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" struct PrimitiveData { @@ -71,5 +70,3 @@ struct PrimitiveData }; void to_json(json &j, const PrimitiveData &d); - -#endif //FBX2GLTF_PRIMITIVEDATA_H diff --git a/src/glTF/SamplerData.h b/src/gltf/properties/SamplerData.hpp similarity index 78% rename from src/glTF/SamplerData.h rename to src/gltf/properties/SamplerData.hpp index 7804b98..43978b5 100644 --- a/src/glTF/SamplerData.h +++ b/src/gltf/properties/SamplerData.hpp @@ -7,10 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_SAMPLERDATA_H -#define FBX2GLTF_SAMPLERDATA_H +#pragma once -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" struct SamplerData : Holdable { @@ -20,9 +19,7 @@ struct SamplerData : Holdable { } - json serialize() const { + json serialize() const override { return json::object(); } }; - -#endif //FBX2GLTF_SAMPLERDATA_H diff --git a/src/glTF/SceneData.cpp b/src/gltf/properties/SceneData.cpp similarity index 92% rename from src/glTF/SceneData.cpp rename to src/gltf/properties/SceneData.cpp index 2b21994..8512250 100644 --- a/src/glTF/SceneData.cpp +++ b/src/gltf/properties/SceneData.cpp @@ -7,9 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "SceneData.h" +#include "SceneData.hpp" -#include "NodeData.h" +#include "NodeData.hpp" SceneData::SceneData(std::string name, const NodeData &rootNode) : Holdable(), diff --git a/src/glTF/SceneData.h b/src/gltf/properties/SceneData.hpp similarity index 81% rename from src/glTF/SceneData.h rename to src/gltf/properties/SceneData.hpp index 1550eed..d4b1777 100644 --- a/src/glTF/SceneData.h +++ b/src/gltf/properties/SceneData.hpp @@ -7,10 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_SCENEDATA_H -#define FBX2GLTF_SCENEDATA_H - -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" struct SceneData : Holdable { @@ -21,5 +18,3 @@ struct SceneData : Holdable const std::string name; std::vector nodes; }; - -#endif //FBX2GLTF_SCENEDATA_H diff --git a/src/glTF/SkinData.cpp b/src/gltf/properties/SkinData.cpp similarity index 91% rename from src/glTF/SkinData.cpp rename to src/gltf/properties/SkinData.cpp index 7d53496..db66e40 100644 --- a/src/glTF/SkinData.cpp +++ b/src/gltf/properties/SkinData.cpp @@ -7,10 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "SkinData.h" +#include "SkinData.hpp" -#include "AccessorData.h" -#include "NodeData.h" +#include "AccessorData.hpp" +#include "NodeData.hpp" SkinData::SkinData( const std::vector joints, const AccessorData &inverseBindMatricesAccessor, diff --git a/src/glTF/SkinData.h b/src/gltf/properties/SkinData.hpp similarity index 86% rename from src/glTF/SkinData.h rename to src/gltf/properties/SkinData.hpp index 1831a02..3733f7e 100644 --- a/src/glTF/SkinData.h +++ b/src/gltf/properties/SkinData.hpp @@ -7,10 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_SKINDATA_H -#define FBX2GLTF_SKINDATA_H - -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" struct SkinData : Holdable { @@ -24,5 +21,3 @@ struct SkinData : Holdable const uint32_t skeletonRootNode; const uint32_t inverseBindMatrices; }; - -#endif //FBX2GLTF_SKINDATA_H diff --git a/src/glTF/TextureData.cpp b/src/gltf/properties/TextureData.cpp similarity index 89% rename from src/glTF/TextureData.cpp rename to src/gltf/properties/TextureData.cpp index 538f1ab..f645fcf 100644 --- a/src/glTF/TextureData.cpp +++ b/src/gltf/properties/TextureData.cpp @@ -7,10 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "TextureData.h" +#include "TextureData.hpp" -#include "ImageData.h" -#include "SamplerData.h" +#include "ImageData.hpp" +#include "SamplerData.hpp" TextureData::TextureData(std::string name, const SamplerData &sampler, const ImageData &source) : Holdable(), diff --git a/src/glTF/TextureData.h b/src/gltf/properties/TextureData.hpp similarity index 82% rename from src/glTF/TextureData.h rename to src/gltf/properties/TextureData.hpp index 1106059..cdf8670 100644 --- a/src/glTF/TextureData.h +++ b/src/gltf/properties/TextureData.hpp @@ -7,10 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef FBX2GLTF_TEXTUREDATA_H -#define FBX2GLTF_TEXTUREDATA_H - -#include "Raw2Gltf.h" +#include "gltf/Raw2Gltf.hpp" struct TextureData : Holdable { @@ -22,5 +19,3 @@ struct TextureData : Holdable const uint32_t sampler; const uint32_t source; }; - -#endif //FBX2GLTF_TEXTUREDATA_H diff --git a/src/mathfu.h b/src/mathfu.hpp similarity index 80% rename from src/mathfu.h rename to src/mathfu.hpp index 11cca93..3a15931 100644 --- a/src/mathfu.h +++ b/src/mathfu.hpp @@ -3,11 +3,11 @@ * 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 + * 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. */ -#ifndef FBX2GLTF_MATHFU_H -#define FBX2GLTF_MATHFU_H + +#pragma once #include @@ -58,12 +58,12 @@ typedef mathfu::Matrix Mat4f; typedef mathfu::Quaternion Quatf; typedef Bounds Boundsf; -const Vec3f VEC3F_ONE = Vec3f {1.0f}; -const Vec3f VEC3F_ZERO = Vec3f {0.0f}; -const Vec4f VEC4F_ONE = Vec4f {1.0f}; -const Vec4f VEC4F_ZERO = Vec4f {0.0f}; +#define VEC3F_ONE (Vec3f {1.0f}) +#define VEC3F_ZERO (Vec3f {0.0f}) +#define VEC4F_ONE (Vec4f {1.0f}) +#define VEC4F_ZERO (Vec4f {0.0f}) -template static inline std::vector toStdVec(const mathfu::Vector &vec) +template inline std::vector toStdVec(const mathfu::Vector &vec) { std::vector result(d); for (int ii = 0; ii < d; ii ++) { @@ -76,15 +76,15 @@ template std::vector toStdVec(const mathfu::Quaternion &quat) { return std::vector { quat.vector()[0], quat.vector()[1], quat.vector()[2], quat.scalar() }; } -static inline Vec3f toVec3f(const FbxVector4 &v) { +inline Vec3f toVec3f(const FbxVector4 &v) { return Vec3f((float) v[0], (float) v[1], (float) v[2]); } -static inline Vec4f toVec4f(const FbxVector4 &v) { +inline Vec4f toVec4f(const FbxVector4 &v) { return Vec4f((float) v[0], (float) v[1], (float) v[2], (float) v[3]); } -static inline Mat4f toMat4f(const FbxAMatrix &m) { +inline Mat4f toMat4f(const FbxAMatrix &m) { auto result = Mat4f(); for (int row = 0; row < 4; row ++) { for (int col = 0; col < 4; col ++) { @@ -94,8 +94,6 @@ static inline Mat4f toMat4f(const FbxAMatrix &m) { return result; } -static inline Quatf toQuatf(const FbxQuaternion &q) { +inline Quatf toQuatf(const FbxQuaternion &q) { return Quatf((float) q[3], (float) q[0], (float) q[1], (float) q[2]); } - -#endif //FBX2GLTF_MATHFU_H diff --git a/src/RawModel.cpp b/src/raw/RawModel.cpp similarity index 98% rename from src/RawModel.cpp rename to src/raw/RawModel.cpp index 148776d..4d3ad85 100644 --- a/src/RawModel.cpp +++ b/src/raw/RawModel.cpp @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#include "RawModel.hpp" + #include #include #include @@ -18,12 +20,8 @@ #include #endif -#include "FBX2glTF.h" -#include "utils/String_Utils.h" -#include "utils/Image_Utils.h" -#include "RawModel.h" - -extern bool verboseOutput; +#include "utils/String_Utils.hpp" +#include "utils/Image_Utils.hpp" bool RawVertex::operator==(const RawVertex &other) const { @@ -96,7 +94,8 @@ int RawModel::AddTexture(const std::string &name, const std::string &fileName, c } } - const ImageProperties properties = GetImageProperties(!fileLocation.empty() ? fileLocation.c_str() : fileName.c_str()); + const ImageUtils::ImageProperties + properties = ImageUtils::GetImageProperties(!fileLocation.empty() ? fileLocation.c_str() : fileName.c_str()); RawTexture texture; texture.name = name; @@ -104,7 +103,7 @@ int RawModel::AddTexture(const std::string &name, const std::string &fileName, c texture.height = properties.height; texture.mipLevels = (int) ceilf(log2f(std::max((float) properties.width, (float) properties.height))); texture.usage = usage; - texture.occlusion = (properties.occlusion == IMAGE_TRANSPARENT) ? + texture.occlusion = (properties.occlusion == ImageUtils::IMAGE_TRANSPARENT) ? RAW_TEXTURE_OCCLUSION_TRANSPARENT : RAW_TEXTURE_OCCLUSION_OPAQUE; texture.fileName = fileName; texture.fileLocation = fileLocation; @@ -461,7 +460,7 @@ void RawModel::CreateMaterialModels( // Overestimate the number of models that will be created to avoid massive reallocation. int discreteCount = 0; for (const auto &surface : surfaces) { - discreteCount += (surface.discrete != false); + discreteCount += surface.discrete ? 1 : 0; } materialModels.clear(); diff --git a/src/RawModel.h b/src/raw/RawModel.hpp similarity index 98% rename from src/RawModel.h rename to src/raw/RawModel.hpp index ac17dd6..61c6797 100644 --- a/src/RawModel.h +++ b/src/raw/RawModel.hpp @@ -7,13 +7,14 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef __RAWMODEL_H__ -#define __RAWMODEL_H__ +#pragma once #include #include #include +#include "FBX2glTF.h" + /** * The variuos situations in which the user may wish for us to (re-)compute normals for our vertices. */ @@ -165,7 +166,7 @@ enum RawShadingModel RAW_SHADING_MODEL_MAX }; -static inline std::string Describe(RawShadingModel model) { +inline std::string Describe(RawShadingModel model) { switch(model) { case RAW_SHADING_MODEL_UNKNOWN: return ""; case RAW_SHADING_MODEL_CONSTANT: return "Constant"; @@ -194,7 +195,7 @@ enum RawTextureUsage RAW_TEXTURE_USAGE_MAX }; -static inline std::string Describe(RawTextureUsage usage) +inline std::string Describe(RawTextureUsage usage) { switch (usage) { case RAW_TEXTURE_USAGE_NONE: return ""; @@ -515,5 +516,3 @@ void RawModel::GetAttributeArray(std::vector<_attrib_type_> &out, const _attrib_ out[i] = vertices[i].*ptr; } } - -#endif // !__RAWMODEL_H__ diff --git a/src/utils/File_Utils.cpp b/src/utils/File_Utils.cpp index 30ad6bf..9de2c5c 100644 --- a/src/utils/File_Utils.cpp +++ b/src/utils/File_Utils.cpp @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#include "File_Utils.hpp" + #include #include #include @@ -35,7 +37,7 @@ #include #include "FBX2glTF.h" -#include "String_Utils.h" +#include "String_Utils.hpp" namespace FileUtils { diff --git a/src/utils/File_Utils.h b/src/utils/File_Utils.hpp similarity index 90% rename from src/utils/File_Utils.h rename to src/utils/File_Utils.hpp index 54795ce..4784d38 100644 --- a/src/utils/File_Utils.h +++ b/src/utils/File_Utils.hpp @@ -7,10 +7,13 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#ifndef __FILE_UTILS_H__ -#define __FILE_UTILS_H__ +#pragma once + +#include +#include namespace FileUtils { + std::string GetCurrentFolder(); bool FileExists(const std::string &folderPath); @@ -22,6 +25,5 @@ namespace FileUtils { bool CreatePath(const char *path); bool CopyFile(const std::string &srcFilename, const std::string &dstFilename); -} -#endif // !__FILE_UTILS_H__ +} diff --git a/src/utils/Image_Utils.cpp b/src/utils/Image_Utils.cpp index 9faf411..cac8e6f 100644 --- a/src/utils/Image_Utils.cpp +++ b/src/utils/Image_Utils.cpp @@ -7,50 +7,71 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#include "Image_Utils.hpp" + #include +#include #define STB_IMAGE_IMPLEMENTATION + #include + #define STB_IMAGE_WRITE_IMPLEMENTATION + #include -#include "Image_Utils.h" +namespace ImageUtils { -static bool imageHasTransparentPixels(FILE *f) { - int width, height, channels; - // RGBA: we have to load the pixels to figure out if the image is fully opaque - uint8_t *pixels = stbi_load_from_file(f, &width, &height, &channels, 0); - if (pixels != nullptr) { - int pixelCount = width * height; - for (int ix = 0; ix < pixelCount; ix ++) { - // test fourth byte (alpha); 255 is 1.0 - if (pixels[4*ix + 3] != 255) { - return true; + static bool imageHasTransparentPixels(FILE *f) + { + int width, height, channels; + // RGBA: we have to load the pixels to figure out if the image is fully opaque + uint8_t *pixels = stbi_load_from_file(f, &width, &height, &channels, 0); + if (pixels != nullptr) { + int pixelCount = width * height; + for (int ix = 0; ix < pixelCount; ix++) { + // test fourth byte (alpha); 255 is 1.0 + if (pixels[4 * ix + 3] != 255) { + return true; + } } } + return false; } - return false; -} -ImageProperties GetImageProperties(char const *filePath) -{ - ImageProperties result = { - 1, - 1, - IMAGE_OPAQUE, - }; + ImageProperties GetImageProperties(char const *filePath) + { + ImageProperties result = { + 1, + 1, + IMAGE_OPAQUE, + }; - FILE *f = fopen(filePath, "rb"); - if (f == nullptr) { + FILE *f = fopen(filePath, "rb"); + if (f == nullptr) { + return result; + } + + int channels; + int success = stbi_info_from_file(f, &result.width, &result.height, &channels); + + if (success && channels == 4 && imageHasTransparentPixels(f)) { + result.occlusion = IMAGE_TRANSPARENT; + } return result; } - int channels; - int success = stbi_info_from_file(f, &result.width, &result.height, &channels); + std::string suffixToMimeType(std::string suffix) + { + std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower); - if (success && channels == 4 && imageHasTransparentPixels(f)) { - result.occlusion = IMAGE_TRANSPARENT; + if (suffix == "jpg" || suffix == "jpeg") { + return "image/jpeg"; + } + if (suffix == "png") { + return "image/png"; + } + return "image/unknown"; } - return result; -} +} diff --git a/src/utils/Image_Utils.h b/src/utils/Image_Utils.h deleted file mode 100644 index 2b8f40a..0000000 --- a/src/utils/Image_Utils.h +++ /dev/null @@ -1,46 +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. - */ - -#ifndef __IMAGE_UTILS_H__ -#define __IMAGE_UTILS_H__ - -#include - -enum ImageOcclusion -{ - IMAGE_OPAQUE, - IMAGE_TRANSPARENT -}; - -struct ImageProperties -{ - int width; - int height; - ImageOcclusion occlusion; -}; - -ImageProperties GetImageProperties(char const *filePath); - -/** - * Very simple method for mapping filename suffix to mime type. The glTF 2.0 spec only accepts values - * "image/jpeg" and "image/png" so we don't need to get too fancy. - */ -inline std::string suffixToMimeType(std::string suffix) { - std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower); - - if (suffix == "jpg" || suffix == "jpeg") { - return "image/jpeg"; - } - if (suffix == "png") { - return "image/png"; - } - return "image/unknown"; -} - -#endif // !__IMAGE_UTILS_H__ diff --git a/src/utils/Image_Utils.hpp b/src/utils/Image_Utils.hpp new file mode 100644 index 0000000..eb4d8e9 --- /dev/null +++ b/src/utils/Image_Utils.hpp @@ -0,0 +1,37 @@ +/** + * 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 + +namespace ImageUtils { + + enum ImageOcclusion + { + IMAGE_OPAQUE, + IMAGE_TRANSPARENT + }; + + struct ImageProperties + { + int width; + int height; + ImageOcclusion occlusion; + }; + + ImageProperties GetImageProperties(char const *filePath); + + /** + * Very simple method for mapping filename suffix to mime type. The glTF 2.0 spec only accepts values + * "image/jpeg" and "image/png" so we don't need to get too fancy. + */ + std::string suffixToMimeType(std::string suffix); + +} diff --git a/src/utils/String_Utils.cpp b/src/utils/String_Utils.cpp index 4fb0712..4746402 100644 --- a/src/utils/String_Utils.cpp +++ b/src/utils/String_Utils.cpp @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "String_Utils.h" +#include "String_Utils.hpp" namespace StringUtils { @@ -16,4 +16,48 @@ namespace StringUtils { return (s == PATH_WIN) ? PATH_UNIX : PATH_WIN; } + const std::string GetFolderString(const std::string &path) + { + size_t s = path.rfind(PATH_WIN); + s = (s != std::string::npos) ? s : path.rfind(PATH_UNIX); + return path.substr(0, s + 1); + } + + const std::string GetCleanPathString(const std::string &path, const PathSeparator separator) + { + std::string cleanPath = path; + for (size_t s = cleanPath.find(!separator, 0); s != std::string::npos; s = cleanPath.find(!separator, s)) { + cleanPath[s] = separator; + } + return cleanPath; + } + + const std::string GetFileNameString(const std::string &path) + { + size_t s = path.rfind(PATH_WIN); + s = (s != std::string::npos) ? s : path.rfind(PATH_UNIX); + return path.substr(s + 1, std::string::npos); + } + + const std::string GetFileBaseString(const std::string &path) + { + const std::string fileName = GetFileNameString(path); + return fileName.substr(0, fileName.rfind('.')).c_str(); + } + + const std::string GetFileSuffixString(const std::string &path) + { + const std::string fileName = GetFileNameString(path); + size_t pos = fileName.rfind('.'); + if (pos == std::string::npos) { + return ""; + } + return fileName.substr(++pos); + } + + int CompareNoCase(const std::string &s1, const std::string &s2) + { + return strncasecmp(s1.c_str(), s2.c_str(), MAX_PATH_LENGTH); + } + } diff --git a/src/utils/String_Utils.h b/src/utils/String_Utils.h deleted file mode 100644 index 7b78078..0000000 --- a/src/utils/String_Utils.h +++ /dev/null @@ -1,91 +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. - */ - -#ifndef _STRING_UTILS_H__ -#define _STRING_UTILS_H__ - -#include -#include -#include -#include - -#if defined( _MSC_VER ) -#define strncasecmp _strnicmp -#define strcasecmp _stricmp -#endif - -namespace StringUtils { - - static const unsigned int MAX_PATH_LENGTH = 1024; - - enum PathSeparator - { - PATH_WIN = '\\', - PATH_UNIX = '/' - }; - - PathSeparator operator!(const PathSeparator &s); - - inline const std::string GetCleanPathString(const std::string &path, const PathSeparator separator = PATH_WIN) - { - std::string cleanPath = path; - for (size_t s = cleanPath.find(!separator, 0); s != std::string::npos; s = cleanPath.find(!separator, s)) { - cleanPath[s] = separator; - } - return cleanPath; - } - template - inline void GetCleanPath(char (&dest)[size], const char *path, const PathSeparator separator = PATH_WIN) - { - size_t len = size - 1; - strncpy(dest, path, len); - char *destPtr = dest; - while ((destPtr = strchr(destPtr, !separator)) != nullptr) { - *destPtr = separator; - } - } - - inline const std::string GetFolderString(const std::string &path) - { - size_t s = path.rfind(PATH_WIN); - s = (s != std::string::npos) ? s : path.rfind(PATH_UNIX); - return path.substr(0, s + 1); - } - - inline const std::string GetFileNameString(const std::string &path) - { - size_t s = path.rfind(PATH_WIN); - s = (s != std::string::npos) ? s : path.rfind(PATH_UNIX); - return path.substr(s + 1, std::string::npos); - } - - inline const std::string GetFileBaseString(const std::string &path) - { - const std::string fileName = GetFileNameString(path); - return fileName.substr(0, fileName.rfind('.')).c_str(); - } - - inline const std::string GetFileSuffixString(const std::string &path) - { - const std::string fileName = GetFileNameString(path); - size_t pos = fileName.rfind('.'); - if (pos == std::string::npos) { - return ""; - } - return fileName.substr(++pos); - } - - inline int CompareNoCase(const std::string &s1, const std::string &s2) - { - return strncasecmp(s1.c_str(), s2.c_str(), MAX_PATH_LENGTH); - } - -} // StringUtils -#endif // _STRING_UTILS_H__ - diff --git a/src/utils/String_Utils.hpp b/src/utils/String_Utils.hpp new file mode 100644 index 0000000..46ace88 --- /dev/null +++ b/src/utils/String_Utils.hpp @@ -0,0 +1,54 @@ +/** + * 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 +#include +#include +#include + +#if defined( _MSC_VER ) +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +namespace StringUtils { + + static const unsigned int MAX_PATH_LENGTH = 1024; + + enum PathSeparator + { + PATH_WIN = '\\', + PATH_UNIX = '/' + }; + + PathSeparator operator!(const PathSeparator &s); + + const std::string GetCleanPathString(const std::string &path, const PathSeparator separator = PATH_WIN); + + template + void GetCleanPath(char (&dest)[size], const char *path, const PathSeparator separator = PATH_WIN) + { + size_t len = size - 1; + strncpy(dest, path, len); + char *destPtr = dest; + while ((destPtr = strchr(destPtr, !separator)) != nullptr) { + *destPtr = separator; + } + } + + const std::string GetFolderString(const std::string &path); + const std::string GetFileNameString(const std::string &path); + const std::string GetFileBaseString(const std::string &path); + const std::string GetFileSuffixString(const std::string &path); + + int CompareNoCase(const std::string &s1, const std::string &s2); + +}