Merge remote-tracking branch 'upstream/master' into blend-shape-keys-to-accessor-names
This commit is contained in:
commit
9d6f42aba4
135
CMakeLists.txt
135
CMakeLists.txt
|
@ -14,24 +14,30 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
|
|
||||||
# FBX
|
# 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)
|
if (NOT FBXSDK_FOUND)
|
||||||
message(FATAL_ERROR
|
message(FATAL_ERROR
|
||||||
"Can't find FBX SDK in either:\n"
|
"Can't find FBX SDK in either:\n"
|
||||||
" - Mac OS X: ${FBXSDK_APPLE_ROOT}\n"
|
" - Mac OS X: ${FBXSDK_APPLE_ROOT}\n"
|
||||||
" - Windows: ${FBXSDK_WINDOWS_ROOT}\n"
|
" - Windows: ${FBXSDK_WINDOWS_ROOT}\n"
|
||||||
" - Linux: ${FBXSDK_LINUX_ROOT}"
|
" - Linux: ${FBXSDK_LINUX_ROOT}"
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# DRACO
|
# DRACO
|
||||||
ExternalProject_Add(Draco
|
ExternalProject_Add(Draco
|
||||||
GIT_REPOSITORY https://github.com/google/draco
|
GIT_REPOSITORY https://github.com/google/draco
|
||||||
GIT_TAG 1.3.1
|
GIT_TAG 1.3.4
|
||||||
PREFIX draco
|
PREFIX draco
|
||||||
INSTALL_DIR
|
INSTALL_DIR
|
||||||
CMAKE_ARGS
|
CMAKE_ARGS
|
||||||
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
|
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
|
||||||
|
-DBUILD_FOR_GLTF=1
|
||||||
)
|
)
|
||||||
set(DRACO_INCLUDE_DIR "${CMAKE_BINARY_DIR}/draco/include")
|
set(DRACO_INCLUDE_DIR "${CMAKE_BINARY_DIR}/draco/include")
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
@ -129,54 +135,70 @@ if (APPLE)
|
||||||
set(FRAMEWORKS ${CF_FRAMEWORK})
|
set(FRAMEWORKS ${CF_FRAMEWORK})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(LIB_SOURCE_FILES
|
||||||
src/FBX2glTF.h
|
src/FBX2glTF.h
|
||||||
src/Fbx2Raw.cpp
|
src/fbx/Fbx2Raw.cpp
|
||||||
src/Fbx2Raw.h
|
src/fbx/Fbx2Raw.hpp
|
||||||
src/Raw2Gltf.cpp
|
src/fbx/FbxLayerElementAccess.hpp
|
||||||
src/Raw2Gltf.h
|
src/fbx/FbxBlendShapesAccess.hpp
|
||||||
src/RawModel.cpp
|
src/fbx/FbxMaterialInfo.hpp
|
||||||
src/RawModel.h
|
src/gltf/Raw2Gltf.cpp
|
||||||
src/glTF/AccessorData.cpp
|
src/gltf/Raw2Gltf.hpp
|
||||||
src/glTF/AccessorData.h
|
src/raw/RawModel.cpp
|
||||||
src/glTF/AnimationData.cpp
|
src/raw/RawModel.hpp
|
||||||
src/glTF/AnimationData.h
|
src/gltf/properties/AccessorData.cpp
|
||||||
src/glTF/BufferData.cpp
|
src/gltf/properties/AccessorData.hpp
|
||||||
src/glTF/BufferData.h
|
src/gltf/properties/AnimationData.cpp
|
||||||
src/glTF/BufferViewData.cpp
|
src/gltf/properties/AnimationData.hpp
|
||||||
src/glTF/BufferViewData.h
|
src/gltf/properties/BufferData.cpp
|
||||||
src/glTF/CameraData.cpp
|
src/gltf/properties/BufferData.hpp
|
||||||
src/glTF/CameraData.h
|
src/gltf/properties/BufferViewData.cpp
|
||||||
src/glTF/ImageData.cpp
|
src/gltf/properties/BufferViewData.hpp
|
||||||
src/glTF/ImageData.h
|
src/gltf/properties/CameraData.cpp
|
||||||
src/glTF/MaterialData.cpp
|
src/gltf/properties/CameraData.hpp
|
||||||
src/glTF/MaterialData.h
|
src/gltf/properties/ImageData.cpp
|
||||||
src/glTF/MeshData.cpp
|
src/gltf/properties/ImageData.hpp
|
||||||
src/glTF/MeshData.h
|
src/gltf/properties/MaterialData.cpp
|
||||||
src/glTF/NodeData.cpp
|
src/gltf/properties/MaterialData.hpp
|
||||||
src/glTF/NodeData.h
|
src/gltf/properties/MeshData.cpp
|
||||||
src/glTF/PrimitiveData.cpp
|
src/gltf/properties/MeshData.hpp
|
||||||
src/glTF/PrimitiveData.h
|
src/gltf/properties/NodeData.cpp
|
||||||
src/glTF/SamplerData.h
|
src/gltf/properties/NodeData.hpp
|
||||||
src/glTF/SceneData.cpp
|
src/gltf/properties/PrimitiveData.cpp
|
||||||
src/glTF/SceneData.h
|
src/gltf/properties/PrimitiveData.hpp
|
||||||
src/glTF/SkinData.cpp
|
src/gltf/properties/SamplerData.hpp
|
||||||
src/glTF/SkinData.h
|
src/gltf/properties/SceneData.cpp
|
||||||
src/glTF/TextureData.cpp
|
src/gltf/properties/SceneData.hpp
|
||||||
src/glTF/TextureData.h
|
src/gltf/properties/SkinData.cpp
|
||||||
src/main.cpp
|
src/gltf/properties/SkinData.hpp
|
||||||
src/mathfu.h
|
src/gltf/properties/TextureData.cpp
|
||||||
|
src/gltf/properties/TextureData.hpp
|
||||||
|
src/mathfu.hpp
|
||||||
src/utils/File_Utils.cpp
|
src/utils/File_Utils.cpp
|
||||||
src/utils/File_Utils.h
|
src/utils/File_Utils.hpp
|
||||||
src/utils/Image_Utils.cpp
|
src/utils/Image_Utils.cpp
|
||||||
src/utils/Image_Utils.h
|
src/utils/Image_Utils.hpp
|
||||||
src/utils/String_Utils.cpp
|
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
|
Draco
|
||||||
MathFu
|
MathFu
|
||||||
FiFoMap
|
FiFoMap
|
||||||
|
@ -189,33 +211,44 @@ add_dependencies(FBX2glTF
|
||||||
|
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
# Disable annoying & spammy warning from FBX SDK header file
|
# 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"
|
"-Wno-null-dereference"
|
||||||
"-Wunused"
|
"-Wunused"
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(FBX2glTF
|
target_link_libraries(libFBX2glTF
|
||||||
${FRAMEWORKS}
|
${FRAMEWORKS}
|
||||||
${CMAKE_DL_LIBS}
|
|
||||||
${CMAKE_THREAD_LIBS_INIT}
|
|
||||||
${DRACO_LIB}
|
${DRACO_LIB}
|
||||||
${FMT_LIB}
|
${FMT_LIB}
|
||||||
optimized ${FBXSDK_LIBRARY}
|
optimized ${FBXSDK_LIBRARY}
|
||||||
debug ${FBXSDK_LIBRARY_DEBUG}
|
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
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
${FBXSDK_INCLUDE_DIR}
|
${FBXSDK_INCLUDE_DIR}
|
||||||
${DRACO_INCLUDE_DIR}
|
${DRACO_INCLUDE_DIR}
|
||||||
${MATHFU_INCLUDE_DIRS}
|
${MATHFU_INCLUDE_DIRS}
|
||||||
${FIFO_MAP_INCLUDE_DIR}
|
${FIFO_MAP_INCLUDE_DIR}
|
||||||
${JSON_INCLUDE_DIR}
|
${JSON_INCLUDE_DIR}
|
||||||
${CXXOPTS_INCLUDE_DIR}
|
|
||||||
${STB_INCLUDE_DIR}
|
${STB_INCLUDE_DIR}
|
||||||
${CPPCODEC_INCLUDE_DIR}
|
${CPPCODEC_INCLUDE_DIR}
|
||||||
${FMT_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
|
||||||
|
)
|
||||||
|
|
|
@ -21,7 +21,9 @@ else()
|
||||||
set(ARCH_32 OFF)
|
set(ARCH_32 OFF)
|
||||||
endif()
|
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")
|
set(_fbxsdk_vstudio_version "vs2015")
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -23,10 +23,10 @@
|
||||||
#include <cxxopts.hpp>
|
#include <cxxopts.hpp>
|
||||||
|
|
||||||
#include "FBX2glTF.h"
|
#include "FBX2glTF.h"
|
||||||
#include "utils/String_Utils.h"
|
#include "utils/String_Utils.hpp"
|
||||||
#include "utils/File_Utils.h"
|
#include "utils/File_Utils.hpp"
|
||||||
#include "Fbx2Raw.h"
|
#include "fbx/Fbx2Raw.hpp"
|
||||||
#include "Raw2Gltf.h"
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
|
|
||||||
bool verboseOutput = false;
|
bool verboseOutput = false;
|
||||||
|
|
|
@ -7,8 +7,9 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __FBX2GLTF_H__
|
#pragma once
|
||||||
#define __FBX2GLTF_H__
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#if defined ( _WIN32 )
|
#if defined ( _WIN32 )
|
||||||
// Tell Windows not to define min() and max() macros
|
// Tell Windows not to define min() and max() macros
|
||||||
|
@ -16,7 +17,7 @@
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const std::string FBX2GLTF_VERSION = "0.9.5";
|
#define FBX2GLTF_VERSION std::string("0.9.5")
|
||||||
|
|
||||||
#include <fmt/printf.h>
|
#include <fmt/printf.h>
|
||||||
#include <fbxsdk.h>
|
#include <fbxsdk.h>
|
||||||
|
@ -26,6 +27,6 @@ const std::string FBX2GLTF_VERSION = "0.9.5";
|
||||||
#undef isnan
|
#undef isnan
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "mathfu.h"
|
#include "mathfu.hpp"
|
||||||
|
|
||||||
#endif // !__FBX2GLTF_H__
|
extern bool verboseOutput;
|
||||||
|
|
|
@ -20,658 +20,19 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "FBX2glTF.h"
|
#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;
|
float scaleFactor;
|
||||||
|
|
||||||
template<typename _type_>
|
|
||||||
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<int> *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<FbxRoughMetMaterialInfo> From(
|
|
||||||
FbxSurfaceMaterial *fbxMaterial,
|
|
||||||
const std::map<const FbxTexture *, FbxString> &textureLocations)
|
|
||||||
{
|
|
||||||
std::unique_ptr<FbxRoughMetMaterialInfo> 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<bool>()) {
|
|
||||||
const FbxProperty texProp = mayaProp.FindHierarchical(("TEX_" + propName + "_map").c_str());
|
|
||||||
if (texProp.IsValid()) {
|
|
||||||
ptr = texProp.GetSrcObject<FbxFileTexture>();
|
|
||||||
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>() : FbxDouble3(1, 1, 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto getVal = [&](std::string propName) -> FbxDouble {
|
|
||||||
const FbxProperty vecProp = mayaProp.FindHierarchical(propName .c_str());
|
|
||||||
return vecProp.IsValid() ? vecProp.Get<FbxDouble>() : 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<FbxTraditionalMaterialInfo> From(
|
|
||||||
FbxSurfaceMaterial *fbxMaterial,
|
|
||||||
const std::map<const FbxTexture *, FbxString> &textureLocations)
|
|
||||||
{
|
|
||||||
auto getSurfaceScalar = [&](const char *propName) -> std::tuple<FbxDouble, FbxFileTexture *> {
|
|
||||||
const FbxProperty prop = fbxMaterial->FindProperty(propName);
|
|
||||||
|
|
||||||
FbxDouble val(0);
|
|
||||||
FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>();
|
|
||||||
if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) {
|
|
||||||
tex = nullptr;
|
|
||||||
}
|
|
||||||
if (tex == nullptr && prop.IsValid()) {
|
|
||||||
val = prop.Get<FbxDouble>();
|
|
||||||
}
|
|
||||||
return std::make_tuple(val, tex);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto getSurfaceVector = [&](const char *propName) -> std::tuple<FbxDouble3, FbxFileTexture *> {
|
|
||||||
const FbxProperty prop = fbxMaterial->FindProperty(propName);
|
|
||||||
|
|
||||||
FbxDouble3 val(1, 1, 1);
|
|
||||||
FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>();
|
|
||||||
if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) {
|
|
||||||
tex = nullptr;
|
|
||||||
}
|
|
||||||
if (tex == nullptr && prop.IsValid()) {
|
|
||||||
val = prop.Get<FbxDouble3>();
|
|
||||||
}
|
|
||||||
return std::make_tuple(val, tex);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto getSurfaceValues = [&](const char *colName, const char *facName) -> std::tuple<FbxVector4, FbxFileTexture *, FbxFileTexture *> {
|
|
||||||
const FbxProperty colProp = fbxMaterial->FindProperty(colName);
|
|
||||||
const FbxProperty facProp = fbxMaterial->FindProperty(facName);
|
|
||||||
|
|
||||||
FbxDouble3 colorVal(1, 1, 1);
|
|
||||||
FbxDouble factorVal(1);
|
|
||||||
|
|
||||||
FbxFileTexture *colTex = colProp.GetSrcObject<FbxFileTexture>();
|
|
||||||
if (colTex != nullptr && textureLocations.find(colTex) == textureLocations.end()) {
|
|
||||||
colTex = nullptr;
|
|
||||||
}
|
|
||||||
if (colTex == nullptr && colProp.IsValid()) {
|
|
||||||
colorVal = colProp.Get<FbxDouble3>();
|
|
||||||
}
|
|
||||||
FbxFileTexture *facTex = facProp.GetSrcObject<FbxFileTexture>();
|
|
||||||
if (facTex != nullptr && textureLocations.find(facTex) == textureLocations.end()) {
|
|
||||||
facTex = nullptr;
|
|
||||||
}
|
|
||||||
if (facTex == nullptr && facProp.IsValid()) {
|
|
||||||
factorVal = facProp.Get<FbxDouble>();
|
|
||||||
}
|
|
||||||
|
|
||||||
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<FbxTraditionalMaterialInfo> res(new FbxTraditionalMaterialInfo(name.c_str(), fbxMaterial->ShadingModel.Get()));
|
|
||||||
|
|
||||||
// four properties are on the same structure and follow the same rules
|
|
||||||
auto handleBasicProperty = [&](const char *colName, const char *facName) -> std::tuple<FbxVector4, FbxFileTexture *>{
|
|
||||||
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<FbxMaterialInfo>
|
|
||||||
GetMaterialInfo(FbxSurfaceMaterial *material, const std::map<const FbxTexture *, FbxString> &textureLocations)
|
|
||||||
{
|
|
||||||
std::unique_ptr<FbxMaterialInfo> 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<const FbxTexture *, FbxString> &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<FbxSurfaceMaterial>(materialNum),
|
|
||||||
textureLocations);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::shared_ptr<FbxMaterialInfo> 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<std::shared_ptr<FbxMaterialInfo>> summaries {};
|
|
||||||
const FbxMesh *mesh;
|
|
||||||
const FbxLayerElementArrayTemplate<int> *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<long> jointIds;
|
|
||||||
std::vector<FbxNode *> jointNodes;
|
|
||||||
std::vector<FbxMatrix> jointSkinningTransforms;
|
|
||||||
std::vector<FbxMatrix> jointInverseGlobalTransforms;
|
|
||||||
std::vector<FbxAMatrix> inverseBindMatrices;
|
|
||||||
std::vector<Vec4i> vertexJointIndices;
|
|
||||||
std::vector<Vec4f> 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<FbxVector4>(shape->GetElementNormal(), shape->GetElementNormalCount())),
|
|
||||||
tangents(FbxLayerElementAccess<FbxVector4>(shape->GetElementTangent(), shape->GetElementTangentCount()))
|
|
||||||
{}
|
|
||||||
|
|
||||||
const FbxShape *shape;
|
|
||||||
const FbxDouble fullWeight;
|
|
||||||
const unsigned int count;
|
|
||||||
const FbxVector4 *positions;
|
|
||||||
const FbxLayerElementAccess<FbxVector4> normals;
|
|
||||||
const FbxLayerElementAccess<FbxVector4> 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<TargetShape> &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<FbxAnimStack>(animIx);
|
|
||||||
FbxAnimLayer *layer = stack->GetMember<FbxAnimLayer>(0);
|
|
||||||
return mesh->GetShapeChannel(blendShapeIx, channelIx, layer, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
FbxMesh *const mesh;
|
|
||||||
|
|
||||||
const unsigned int blendShapeIx;
|
|
||||||
const unsigned int channelIx;
|
|
||||||
const std::vector<TargetShape> 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<BlendChannel> extractChannels(FbxMesh *mesh) const {
|
|
||||||
std::vector<BlendChannel> channels;
|
|
||||||
for (int shapeIx = 0; shapeIx < mesh->GetDeformerCount(FbxDeformer::eBlendShape); shapeIx++) {
|
|
||||||
auto *fbxBlendShape = static_cast<FbxBlendShape *>(mesh->GetDeformer(shapeIx, FbxDeformer::eBlendShape));
|
|
||||||
|
|
||||||
for (int channelIx = 0; channelIx < fbxBlendShape->GetBlendShapeChannelCount(); ++channelIx) {
|
|
||||||
FbxBlendShapeChannel *fbxChannel = fbxBlendShape->GetBlendShapeChannel(channelIx);
|
|
||||||
|
|
||||||
if (fbxChannel->GetTargetShapeCount() > 0) {
|
|
||||||
std::vector<TargetShape> 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<BlendChannel> channels;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool TriangleTexturePolarity(const Vec2f &uv0, const Vec2f &uv1, const Vec2f &uv2)
|
static bool TriangleTexturePolarity(const Vec2f &uv0, const Vec2f &uv1, const Vec2f &uv2)
|
||||||
{
|
{
|
||||||
const Vec2f d0 = uv1 - uv0;
|
const Vec2f d0 = uv1 - uv0;
|
|
@ -7,11 +7,8 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __FBX2RAW_H__
|
#pragma once
|
||||||
#define __FBX2RAW_H__
|
|
||||||
|
|
||||||
#include "RawModel.h"
|
#include "raw/RawModel.hpp"
|
||||||
|
|
||||||
bool LoadFBXFile(RawModel &raw, const char *fbxFileName, const char *textureExtensions);
|
bool LoadFBXFile(RawModel &raw, const char *fbxFileName, const char *textureExtensions);
|
||||||
|
|
||||||
#endif // !__FBX2RAW_H__
|
|
|
@ -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<FbxVector4>(shape->GetElementNormal(), shape->GetElementNormalCount())),
|
||||||
|
tangents(FbxLayerElementAccess<FbxVector4>(shape->GetElementTangent(), shape->GetElementTangentCount()))
|
||||||
|
{}
|
||||||
|
|
||||||
|
FbxAnimCurve *FbxBlendShapesAccess::BlendChannel::ExtractAnimation(unsigned int animIx) const
|
||||||
|
{
|
||||||
|
FbxAnimStack *stack = mesh->GetScene()->GetSrcObject<FbxAnimStack>(animIx);
|
||||||
|
FbxAnimLayer *layer = stack->GetMember<FbxAnimLayer>(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<FbxBlendShapesAccess::TargetShape> &targetShapes) : mesh(mesh),
|
||||||
|
blendShapeIx(blendShapeIx),
|
||||||
|
channelIx(channelIx),
|
||||||
|
deformPercent(deformPercent),
|
||||||
|
targetShapes(targetShapes)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::vector<FbxBlendShapesAccess::BlendChannel> FbxBlendShapesAccess::extractChannels(FbxMesh *mesh) const
|
||||||
|
{
|
||||||
|
std::vector<BlendChannel> channels;
|
||||||
|
for (int shapeIx = 0; shapeIx < mesh->GetDeformerCount(FbxDeformer::eBlendShape); shapeIx++) {
|
||||||
|
auto *fbxBlendShape = dynamic_cast<FbxBlendShape *>(mesh->GetDeformer(shapeIx, FbxDeformer::eBlendShape));
|
||||||
|
|
||||||
|
for (int channelIx = 0; channelIx < fbxBlendShape->GetBlendShapeChannelCount(); ++channelIx) {
|
||||||
|
FbxBlendShapeChannel *fbxChannel = fbxBlendShape->GetBlendShapeChannel(channelIx);
|
||||||
|
|
||||||
|
if (fbxChannel->GetTargetShapeCount() > 0) {
|
||||||
|
std::vector<TargetShape> 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;
|
||||||
|
}
|
|
@ -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 <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#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<FbxVector4> normals;
|
||||||
|
const FbxLayerElementAccess<FbxVector4> 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<TargetShape> &targetShapes
|
||||||
|
);
|
||||||
|
|
||||||
|
FbxAnimCurve *ExtractAnimation(unsigned int animIx) const;
|
||||||
|
|
||||||
|
FbxMesh *const mesh;
|
||||||
|
|
||||||
|
const unsigned int blendShapeIx;
|
||||||
|
const unsigned int channelIx;
|
||||||
|
const std::vector<TargetShape> 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<BlendChannel> extractChannels(FbxMesh *mesh) const;
|
||||||
|
|
||||||
|
const std::vector<BlendChannel> channels;
|
||||||
|
};
|
|
@ -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<typename _type_>
|
||||||
|
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<int> *indices;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename _type_>
|
||||||
|
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<typename _type_>
|
||||||
|
_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<typename _type_>
|
||||||
|
_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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
|
@ -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<const FbxTexture *, FbxString> &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<FbxSurfaceMaterial>(materialNum),
|
||||||
|
textureLocations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::shared_ptr<FbxMaterialInfo> 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<FbxMaterialInfo>
|
||||||
|
FbxMaterialsAccess::GetMaterialInfo(FbxSurfaceMaterial *material, const std::map<const FbxTexture *, FbxString> &textureLocations)
|
||||||
|
{
|
||||||
|
std::unique_ptr<FbxMaterialInfo> res;
|
||||||
|
res = FbxRoughMetMaterialInfo::From(material, textureLocations);
|
||||||
|
if (!res) {
|
||||||
|
res = FbxTraditionalMaterialInfo::From(material, textureLocations);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
|
@ -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<const FbxTexture *, FbxString> &textureLocations);
|
||||||
|
|
||||||
|
const std::shared_ptr<FbxMaterialInfo> GetMaterial(const int polygonIndex) const;
|
||||||
|
|
||||||
|
std::unique_ptr<FbxMaterialInfo>
|
||||||
|
GetMaterialInfo(FbxSurfaceMaterial *material, const std::map<const FbxTexture *, FbxString> &textureLocations);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FbxGeometryElement::EMappingMode mappingMode;
|
||||||
|
std::vector<std::shared_ptr<FbxMaterialInfo>> summaries {};
|
||||||
|
const FbxMesh *mesh;
|
||||||
|
const FbxLayerElementArrayTemplate<int> *indices;
|
||||||
|
};
|
|
@ -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>
|
||||||
|
FbxRoughMetMaterialInfo::From(FbxSurfaceMaterial *fbxMaterial, const std::map<const FbxTexture *, FbxString> &textureLocations)
|
||||||
|
{
|
||||||
|
std::unique_ptr<FbxRoughMetMaterialInfo> 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<bool>()) {
|
||||||
|
const FbxProperty texProp = mayaProp.FindHierarchical(("TEX_" + propName + "_map").c_str());
|
||||||
|
if (texProp.IsValid()) {
|
||||||
|
ptr = texProp.GetSrcObject<FbxFileTexture>();
|
||||||
|
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>() : FbxDouble3(1, 1, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getVal = [&](std::string propName) -> FbxDouble {
|
||||||
|
const FbxProperty vecProp = mayaProp.FindHierarchical(propName .c_str());
|
||||||
|
return vecProp.IsValid() ? vecProp.Get<FbxDouble>() : 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;
|
||||||
|
}
|
|
@ -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 <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "FbxMaterialInfo.hpp"
|
||||||
|
|
||||||
|
struct FbxRoughMetMaterialInfo : FbxMaterialInfo {
|
||||||
|
static constexpr const char *FBX_SHADER_METROUGH = "MetallicRoughness";
|
||||||
|
|
||||||
|
static std::unique_ptr<FbxRoughMetMaterialInfo> From(
|
||||||
|
FbxSurfaceMaterial *fbxMaterial,
|
||||||
|
const std::map<const FbxTexture *, FbxString> &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 {};
|
||||||
|
};
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#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<long> jointIds;
|
||||||
|
std::vector<FbxNode *> jointNodes;
|
||||||
|
std::vector<FbxMatrix> jointSkinningTransforms;
|
||||||
|
std::vector<FbxMatrix> jointInverseGlobalTransforms;
|
||||||
|
std::vector<FbxAMatrix> inverseBindMatrices;
|
||||||
|
std::vector<Vec4i> vertexJointIndices;
|
||||||
|
std::vector<Vec4f> vertexJointWeights;
|
||||||
|
};
|
|
@ -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>
|
||||||
|
FbxTraditionalMaterialInfo::From(FbxSurfaceMaterial *fbxMaterial, const std::map<const FbxTexture *, FbxString> &textureLocations)
|
||||||
|
{
|
||||||
|
auto getSurfaceScalar = [&](const char *propName) -> std::tuple<FbxDouble, FbxFileTexture *> {
|
||||||
|
const FbxProperty prop = fbxMaterial->FindProperty(propName);
|
||||||
|
|
||||||
|
FbxDouble val(0);
|
||||||
|
FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>();
|
||||||
|
if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) {
|
||||||
|
tex = nullptr;
|
||||||
|
}
|
||||||
|
if (tex == nullptr && prop.IsValid()) {
|
||||||
|
val = prop.Get<FbxDouble>();
|
||||||
|
}
|
||||||
|
return std::make_tuple(val, tex);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getSurfaceVector = [&](const char *propName) -> std::tuple<FbxDouble3, FbxFileTexture *> {
|
||||||
|
const FbxProperty prop = fbxMaterial->FindProperty(propName);
|
||||||
|
|
||||||
|
FbxDouble3 val(1, 1, 1);
|
||||||
|
FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>();
|
||||||
|
if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) {
|
||||||
|
tex = nullptr;
|
||||||
|
}
|
||||||
|
if (tex == nullptr && prop.IsValid()) {
|
||||||
|
val = prop.Get<FbxDouble3>();
|
||||||
|
}
|
||||||
|
return std::make_tuple(val, tex);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getSurfaceValues = [&](const char *colName, const char *facName) -> std::tuple<FbxVector4, FbxFileTexture *, FbxFileTexture *> {
|
||||||
|
const FbxProperty colProp = fbxMaterial->FindProperty(colName);
|
||||||
|
const FbxProperty facProp = fbxMaterial->FindProperty(facName);
|
||||||
|
|
||||||
|
FbxDouble3 colorVal(1, 1, 1);
|
||||||
|
FbxDouble factorVal(1);
|
||||||
|
|
||||||
|
FbxFileTexture *colTex = colProp.GetSrcObject<FbxFileTexture>();
|
||||||
|
if (colTex != nullptr && textureLocations.find(colTex) == textureLocations.end()) {
|
||||||
|
colTex = nullptr;
|
||||||
|
}
|
||||||
|
if (colTex == nullptr && colProp.IsValid()) {
|
||||||
|
colorVal = colProp.Get<FbxDouble3>();
|
||||||
|
}
|
||||||
|
FbxFileTexture *facTex = facProp.GetSrcObject<FbxFileTexture>();
|
||||||
|
if (facTex != nullptr && textureLocations.find(facTex) == textureLocations.end()) {
|
||||||
|
facTex = nullptr;
|
||||||
|
}
|
||||||
|
if (facTex == nullptr && facProp.IsValid()) {
|
||||||
|
factorVal = facProp.Get<FbxDouble>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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<FbxTraditionalMaterialInfo> res(new FbxTraditionalMaterialInfo(name.c_str(), fbxMaterial->ShadingModel.Get()));
|
||||||
|
|
||||||
|
// four properties are on the same structure and follow the same rules
|
||||||
|
auto handleBasicProperty = [&](const char *colName, const char *facName) -> std::tuple<FbxVector4, FbxFileTexture *>{
|
||||||
|
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;
|
||||||
|
}
|
|
@ -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 <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#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<FbxTraditionalMaterialInfo> From(
|
||||||
|
FbxSurfaceMaterial *fbxMaterial,
|
||||||
|
const std::map<const FbxTexture *, FbxString> &textureLocations);
|
||||||
|
};
|
|
@ -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<FbxRoughMetMaterialInfo> 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<bool>()) {
|
||||||
|
const FbxProperty texProp = useProp.FindHierarchical((propName + "_map").c_str());
|
||||||
|
if (texProp.IsValid()) {
|
||||||
|
ptr = texProp.GetSrcObject<FbxFileTexture>();
|
||||||
|
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<FbxRoughMetMaterialInfo> 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;
|
||||||
|
}
|
|
@ -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<const FbxTexture *, FbxString> &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<FbxSurfaceMaterial>(materialNum),
|
||||||
|
textureLocations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::shared_ptr<FbxMaterialInfo> 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<FbxMaterialInfo>
|
||||||
|
FbxMaterialsAccess::GetMaterialInfo(FbxSurfaceMaterial *material, const std::map<const FbxTexture *, FbxString> &textureLocations)
|
||||||
|
{
|
||||||
|
std::unique_ptr<FbxMaterialInfo> res = FbxStingrayPBSMaterialResolver(material, textureLocations).resolve();
|
||||||
|
if (res == nullptr) {
|
||||||
|
res = Fbx3dsMaxPhysicalMaterialResolver(material, textureLocations).resolve();
|
||||||
|
if (res == nullptr) {
|
||||||
|
res = FbxTraditionalMaterialResolver(material, textureLocations).resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
|
@ -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 <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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 T>
|
||||||
|
class FbxMaterialResolver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FbxMaterialResolver(
|
||||||
|
FbxSurfaceMaterial *fbxMaterial,
|
||||||
|
const std::map<const FbxTexture *, FbxString> &textureLocations)
|
||||||
|
: fbxMaterial(fbxMaterial)
|
||||||
|
, textureLocations(textureLocations)
|
||||||
|
{}
|
||||||
|
virtual std::unique_ptr<T> resolve() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const FbxSurfaceMaterial *fbxMaterial;
|
||||||
|
const std::map<const FbxTexture *, FbxString> textureLocations;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FbxMaterialsAccess
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FbxMaterialsAccess(const FbxMesh *pMesh, const std::map<const FbxTexture *, FbxString> &textureLocations);
|
||||||
|
|
||||||
|
const std::shared_ptr<FbxMaterialInfo> GetMaterial(const int polygonIndex) const;
|
||||||
|
|
||||||
|
std::unique_ptr<FbxMaterialInfo>
|
||||||
|
GetMaterialInfo(FbxSurfaceMaterial *material, const std::map<const FbxTexture *, FbxString> &textureLocations);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FbxGeometryElement::EMappingMode mappingMode;
|
||||||
|
std::vector<std::shared_ptr<FbxMaterialInfo>> summaries {};
|
||||||
|
const FbxMesh *mesh;
|
||||||
|
const FbxLayerElementArrayTemplate<int> *indices;
|
||||||
|
};
|
|
@ -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 <map>
|
||||||
|
|
||||||
|
#include "FbxMaterials.hpp"
|
||||||
|
|
||||||
|
struct FbxRoughMetMaterialInfo : FbxMaterialInfo {
|
||||||
|
static constexpr const char *FBX_SHADER_METROUGH = "MetallicRoughness";
|
||||||
|
|
||||||
|
static std::unique_ptr<FbxRoughMetMaterialInfo> From(
|
||||||
|
FbxSurfaceMaterial *fbxMaterial,
|
||||||
|
const std::map<const FbxTexture *, FbxString> &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<FbxRoughMetMaterialInfo> {
|
||||||
|
public:
|
||||||
|
FbxStingrayPBSMaterialResolver(
|
||||||
|
FbxSurfaceMaterial *fbxMaterial,
|
||||||
|
const std::map<const FbxTexture *, FbxString> &textureLocations)
|
||||||
|
: FbxMaterialResolver(fbxMaterial, textureLocations)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual std::unique_ptr<FbxRoughMetMaterialInfo> resolve() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Fbx3dsMaxPhysicalMaterialResolver : FbxMaterialResolver<FbxRoughMetMaterialInfo> {
|
||||||
|
public:
|
||||||
|
Fbx3dsMaxPhysicalMaterialResolver(
|
||||||
|
FbxSurfaceMaterial *fbxMaterial,
|
||||||
|
const std::map<const FbxTexture *, FbxString> &textureLocations)
|
||||||
|
: FbxMaterialResolver(fbxMaterial, textureLocations)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual std::unique_ptr<FbxRoughMetMaterialInfo> resolve() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename T>
|
||||||
|
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<T>() : default;
|
||||||
|
}
|
||||||
|
};
|
|
@ -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<FbxRoughMetMaterialInfo> 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<bool>()) {
|
||||||
|
const FbxProperty texProp = mayaProp.FindHierarchical(("TEX_" + propName + "_map").c_str());
|
||||||
|
if (texProp.IsValid()) {
|
||||||
|
ptr = texProp.GetSrcObject<FbxFileTexture>();
|
||||||
|
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>() : FbxDouble3(1, 1, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getVal = [&](std::string propName) -> FbxDouble {
|
||||||
|
const FbxProperty vecProp = mayaProp.FindHierarchical(propName .c_str());
|
||||||
|
return vecProp.IsValid() ? vecProp.Get<FbxDouble>() : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
FbxDouble3 baseColor = getVec("base_color");
|
||||||
|
std::unique_ptr<FbxRoughMetMaterialInfo> 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;
|
||||||
|
};
|
|
@ -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<FbxTraditionalMaterialInfo> FbxTraditionalMaterialResolver::resolve() const
|
||||||
|
{
|
||||||
|
auto getSurfaceScalar = [&](const char *propName) -> std::tuple<FbxDouble, FbxFileTexture *> {
|
||||||
|
const FbxProperty prop = fbxMaterial->FindProperty(propName);
|
||||||
|
|
||||||
|
FbxDouble val(0);
|
||||||
|
FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>();
|
||||||
|
if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) {
|
||||||
|
tex = nullptr;
|
||||||
|
}
|
||||||
|
if (tex == nullptr && prop.IsValid()) {
|
||||||
|
val = prop.Get<FbxDouble>();
|
||||||
|
}
|
||||||
|
return std::make_tuple(val, tex);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getSurfaceVector = [&](const char *propName) -> std::tuple<FbxDouble3, FbxFileTexture *> {
|
||||||
|
const FbxProperty prop = fbxMaterial->FindProperty(propName);
|
||||||
|
|
||||||
|
FbxDouble3 val(1, 1, 1);
|
||||||
|
FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>();
|
||||||
|
if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) {
|
||||||
|
tex = nullptr;
|
||||||
|
}
|
||||||
|
if (tex == nullptr && prop.IsValid()) {
|
||||||
|
val = prop.Get<FbxDouble3>();
|
||||||
|
}
|
||||||
|
return std::make_tuple(val, tex);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getSurfaceValues = [&](const char *colName, const char *facName) -> std::tuple<FbxVector4, FbxFileTexture *, FbxFileTexture *> {
|
||||||
|
const FbxProperty colProp = fbxMaterial->FindProperty(colName);
|
||||||
|
const FbxProperty facProp = fbxMaterial->FindProperty(facName);
|
||||||
|
|
||||||
|
FbxDouble3 colorVal(1, 1, 1);
|
||||||
|
FbxDouble factorVal(1);
|
||||||
|
|
||||||
|
FbxFileTexture *colTex = colProp.GetSrcObject<FbxFileTexture>();
|
||||||
|
if (colTex != nullptr && textureLocations.find(colTex) == textureLocations.end()) {
|
||||||
|
colTex = nullptr;
|
||||||
|
}
|
||||||
|
if (colTex == nullptr && colProp.IsValid()) {
|
||||||
|
colorVal = colProp.Get<FbxDouble3>();
|
||||||
|
}
|
||||||
|
FbxFileTexture *facTex = facProp.GetSrcObject<FbxFileTexture>();
|
||||||
|
if (facTex != nullptr && textureLocations.find(facTex) == textureLocations.end()) {
|
||||||
|
facTex = nullptr;
|
||||||
|
}
|
||||||
|
if (facTex == nullptr && facProp.IsValid()) {
|
||||||
|
factorVal = facProp.Get<FbxDouble>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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<FbxTraditionalMaterialInfo> res(new FbxTraditionalMaterialInfo(name.c_str(), fbxMaterial->ShadingModel.Get()));
|
||||||
|
|
||||||
|
// four properties are on the same structure and follow the same rules
|
||||||
|
auto handleBasicProperty = [&](const char *colName, const char *facName) -> std::tuple<FbxVector4, FbxFileTexture *>{
|
||||||
|
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;
|
||||||
|
}
|
|
@ -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<FbxTraditionalMaterialInfo> {
|
||||||
|
public:
|
||||||
|
FbxTraditionalMaterialResolver(
|
||||||
|
FbxSurfaceMaterial *fbxMaterial,
|
||||||
|
const std::map<const FbxTexture *, FbxString> &textureLocations)
|
||||||
|
: FbxMaterialResolver(fbxMaterial, textureLocations)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual std::unique_ptr<FbxTraditionalMaterialInfo> resolve() const;
|
||||||
|
};
|
|
@ -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"
|
|
@ -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<T> 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<T>
|
||||||
|
* 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<typename T>
|
||||||
|
struct Holder
|
||||||
|
{
|
||||||
|
std::vector<std::shared_ptr<T>> ptrs;
|
||||||
|
std::shared_ptr<T> hold(T *ptr)
|
||||||
|
{
|
||||||
|
ptr->ix = ptrs.size();
|
||||||
|
ptrs.emplace_back(ptr);
|
||||||
|
return ptrs.back();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GltfModel
|
||||||
|
{
|
||||||
|
explicit GltfModel(bool _isGlb)
|
||||||
|
: binary(new std::vector<uint8_t>),
|
||||||
|
isGlb(_isGlb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<BufferViewData> 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<BufferViewData> 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<BufferViewData> 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<BufferViewData> 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<char> 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<class T>
|
||||||
|
std::shared_ptr<AccessorData> AddAccessorWithView(
|
||||||
|
BufferViewData &bufferView, const GLType &type, const std::vector<T> &source)
|
||||||
|
{
|
||||||
|
auto accessor = accessors.hold(new AccessorData(bufferView, type));
|
||||||
|
accessor->appendAsBinaryArray(source, *binary);
|
||||||
|
bufferView.byteLength = accessor->byteLength();
|
||||||
|
return accessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
std::shared_ptr<AccessorData> AddAccessorAndView(
|
||||||
|
BufferData &buffer, const GLType &type, const std::vector<T> &source)
|
||||||
|
{
|
||||||
|
auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE);
|
||||||
|
return AddAccessorWithView(*bufferView, type, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
std::shared_ptr<AccessorData> AddAttributeToPrimitive(
|
||||||
|
BufferData &buffer, const RawModel &surfaceModel, PrimitiveData &primitive,
|
||||||
|
const AttributeDefinition<T> &attrDef)
|
||||||
|
{
|
||||||
|
// copy attribute data into vector
|
||||||
|
std::vector<T> attribArr;
|
||||||
|
surfaceModel.GetAttributeArray<T>(attribArr, attrDef.rawAttributeIx);
|
||||||
|
|
||||||
|
std::shared_ptr<AccessorData> 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<class T>
|
||||||
|
void serializeHolder(json &glTFJson, std::string key, const Holder<T> holder)
|
||||||
|
{
|
||||||
|
if (!holder.ptrs.empty()) {
|
||||||
|
std::vector<json> 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<std::string, std::shared_ptr<BufferViewData>> filenameToBufferView;
|
||||||
|
|
||||||
|
std::shared_ptr<std::vector<uint8_t> > binary;
|
||||||
|
|
||||||
|
|
||||||
|
Holder<BufferData> buffers;
|
||||||
|
Holder<BufferViewData> bufferViews;
|
||||||
|
Holder<AccessorData> accessors;
|
||||||
|
Holder<ImageData> images;
|
||||||
|
Holder<SamplerData> samplers;
|
||||||
|
Holder<TextureData> textures;
|
||||||
|
Holder<MaterialData> materials;
|
||||||
|
Holder<MeshData> meshes;
|
||||||
|
Holder<SkinData> skins;
|
||||||
|
Holder<AnimationData> animations;
|
||||||
|
Holder<CameraData> cameras;
|
||||||
|
Holder<NodeData> nodes;
|
||||||
|
Holder<SceneData> scenes;
|
||||||
|
};
|
|
@ -7,6 +7,8 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Raw2Gltf.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -15,33 +17,29 @@
|
||||||
#include <stb_image.h>
|
#include <stb_image.h>
|
||||||
#include <stb_image_write.h>
|
#include <stb_image_write.h>
|
||||||
|
|
||||||
#include "FBX2glTF.h"
|
#include "utils/String_Utils.hpp"
|
||||||
#include "utils/String_Utils.h"
|
#include "utils/Image_Utils.hpp"
|
||||||
#include "utils/Image_Utils.h"
|
#include <utils/File_Utils.hpp>
|
||||||
#include <utils/File_Utils.h>
|
#include "raw/RawModel.hpp"
|
||||||
#include "RawModel.h"
|
|
||||||
#include "Raw2Gltf.h"
|
|
||||||
|
|
||||||
#include "glTF/AccessorData.h"
|
#include "gltf/properties/AccessorData.hpp"
|
||||||
#include "glTF/AnimationData.h"
|
#include "gltf/properties/AnimationData.hpp"
|
||||||
#include "glTF/BufferData.h"
|
#include "gltf/properties/BufferData.hpp"
|
||||||
#include "glTF/BufferViewData.h"
|
#include "gltf/properties/BufferViewData.hpp"
|
||||||
#include "glTF/CameraData.h"
|
#include "gltf/properties/CameraData.hpp"
|
||||||
#include "glTF/ImageData.h"
|
#include "gltf/properties/ImageData.hpp"
|
||||||
#include "glTF/MaterialData.h"
|
#include "gltf/properties/MaterialData.hpp"
|
||||||
#include "glTF/MeshData.h"
|
#include "gltf/properties/MeshData.hpp"
|
||||||
#include "glTF/NodeData.h"
|
#include "gltf/properties/NodeData.hpp"
|
||||||
#include "glTF/PrimitiveData.h"
|
#include "gltf/properties/PrimitiveData.hpp"
|
||||||
#include "glTF/SamplerData.h"
|
#include "gltf/properties/SamplerData.hpp"
|
||||||
#include "glTF/SceneData.h"
|
#include "gltf/properties/SceneData.hpp"
|
||||||
#include "glTF/SkinData.h"
|
#include "gltf/properties/SkinData.hpp"
|
||||||
#include "glTF/TextureData.h"
|
#include "gltf/properties/TextureData.hpp"
|
||||||
|
|
||||||
typedef uint32_t TriangleIndex;
|
typedef uint32_t TriangleIndex;
|
||||||
|
|
||||||
extern bool verboseOutput;
|
#define DEFAULT_SCENE_NAME "Root Scene"
|
||||||
|
|
||||||
const static std::string defaultSceneName = "Root Scene";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* glTF 2.0 is based on the idea that data structs within a file are referenced by index; an accessor will
|
* 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);
|
auto bufferView = gltf->AddBufferViewForFile(buffer, rawTexture.fileLocation);
|
||||||
if (bufferView) {
|
if (bufferView) {
|
||||||
std::string suffix = StringUtils::GetFileSuffixString(rawTexture.fileLocation);
|
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()) {
|
} else if (!relativeFilename.empty()) {
|
||||||
|
@ -705,7 +703,7 @@ ModelData *Raw2Gltf(
|
||||||
aoMetRoughTex = merge3Tex("ao_met_rough",
|
aoMetRoughTex = merge3Tex("ao_met_rough",
|
||||||
RAW_TEXTURE_USAGE_OCCLUSION, RAW_TEXTURE_USAGE_METALLIC, RAW_TEXTURE_USAGE_ROUGHNESS,
|
RAW_TEXTURE_USAGE_OCCLUSION, RAW_TEXTURE_USAGE_METALLIC, RAW_TEXTURE_USAGE_ROUGHNESS,
|
||||||
[&](const std::vector<const pixel *> pixels) -> pixel {
|
[&](const std::vector<const pixel *> 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);
|
false);
|
||||||
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
|
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
|
||||||
|
@ -748,7 +746,7 @@ ModelData *Raw2Gltf(
|
||||||
[&](const std::vector<const pixel *> pixels) -> pixel {
|
[&](const std::vector<const pixel *> pixels) -> pixel {
|
||||||
// do not multiply with props->shininess; that doesn't work like the other factors.
|
// do not multiply with props->shininess; that doesn't work like the other factors.
|
||||||
float shininess = props->shininess * (*pixels[0])[0];
|
float shininess = props->shininess * (*pixels[0])[0];
|
||||||
return { 0, getRoughness(shininess), metallic, 1 };
|
return { {0, getRoughness(shininess), metallic, 1} };
|
||||||
},
|
},
|
||||||
false);
|
false);
|
||||||
if (aoMetRoughTex != nullptr) {
|
if (aoMetRoughTex != nullptr) {
|
||||||
|
@ -1068,7 +1066,7 @@ ModelData *Raw2Gltf(
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeData &rootNode = require(nodesById, raw.GetRootNode());
|
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) {
|
if (options.outputBinary) {
|
||||||
// note: glTF binary is little-endian
|
// note: glTF binary is little-endian
|
|
@ -7,8 +7,7 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __RAW2GLTF_H__
|
#pragma once
|
||||||
#define __RAW2GLTF_H__
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -26,7 +25,7 @@ using workaround_fifo_map = nlohmann::fifo_map<K, V, nlohmann::fifo_map_compare<
|
||||||
using json = nlohmann::basic_json<workaround_fifo_map>;
|
using json = nlohmann::basic_json<workaround_fifo_map>;
|
||||||
|
|
||||||
#include "FBX2glTF.h"
|
#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_DRACO_MESH_COMPRESSION = "KHR_draco_mesh_compression";
|
||||||
const std::string KHR_MATERIALS_CMN_UNLIT = "KHR_materials_unlit";
|
const std::string KHR_MATERIALS_CMN_UNLIT = "KHR_materials_unlit";
|
||||||
|
@ -193,5 +192,3 @@ ModelData *Raw2Gltf(
|
||||||
const RawModel &raw,
|
const RawModel &raw,
|
||||||
const GltfOptions &options
|
const GltfOptions &options
|
||||||
);
|
);
|
||||||
|
|
||||||
#endif // !__RAW2GLTF_H__
|
|
|
@ -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 <stb_image.h>
|
||||||
|
#include <stb_image_write.h>
|
||||||
|
|
||||||
|
#include <utils/Image_Utils.hpp>
|
||||||
|
#include <utils/String_Utils.hpp>
|
||||||
|
#include <gltf/properties/ImageData.hpp>
|
||||||
|
|
||||||
|
// 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<TextureData> TextureBuilder::combine(
|
||||||
|
const std::vector<int> &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<TexInfo> 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<uint8_t> mergedPixels(static_cast<size_t>(channels * width * height));
|
||||||
|
std::vector<pixel> pixels(texes.size());
|
||||||
|
std::vector<const pixel *> 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<uint8_t>(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<char> 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<ImageData>(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<TextureData> texDat = gltf->textures.hold(
|
||||||
|
new TextureData(mergedName, defaultSampler, *image));
|
||||||
|
textureByIndicesKey.insert(std::make_pair(key, texDat));
|
||||||
|
|
||||||
|
return std::make_unique<TextureData>(mergedName, imageFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create a new TextureData for the given RawTexture index, or return a previously created one. */
|
||||||
|
std::shared_ptr<TextureData> 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<TextureData> texDat = gltf->textures.hold(
|
||||||
|
new TextureData(textureName, defaultSampler, *gltf->images.hold(image)));
|
||||||
|
textureByIndicesKey.insert(std::make_pair(key, texDat));
|
||||||
|
return texDat;
|
||||||
|
|
||||||
|
}
|
|
@ -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 <functional>
|
||||||
|
|
||||||
|
#include "FBX2glTF.h"
|
||||||
|
|
||||||
|
#include <gltf/properties/ImageData.hpp>
|
||||||
|
|
||||||
|
using pixel = std::array<float, 4>; // pixel components are floats in [0, 1]
|
||||||
|
using pixel_merger = std::function<pixel(const std::vector<const pixel *>)>;
|
||||||
|
|
||||||
|
class TextureBuilder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TextureBuilder(const RawModel &raw, GltfModel &gltf)
|
||||||
|
: raw(raw)
|
||||||
|
, gltf(gltf)
|
||||||
|
{}
|
||||||
|
~TextureBuilder() {}
|
||||||
|
|
||||||
|
std::shared_ptr<TextureData> combine(
|
||||||
|
const std::vector<int> &ixVec,
|
||||||
|
const std::string &tag,
|
||||||
|
const pixel_merger &mergeFunction
|
||||||
|
);
|
||||||
|
|
||||||
|
std::shared_ptr<TextureData> simple(int rawTexIndex, const std::string &tag);
|
||||||
|
|
||||||
|
static std::string texIndicesKey(const std::vector<int> &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<std::vector<char> *>(context);
|
||||||
|
for (int ii = 0; ii < size; ii ++) {
|
||||||
|
vec->push_back(((char *) data)[ii]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const RawModel &raw;
|
||||||
|
GltfModel &gltf;
|
||||||
|
std::map<std::string, std::shared_ptr<TextureData>> textureByIndicesKey;
|
||||||
|
};
|
|
@ -7,8 +7,8 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AccessorData.h"
|
#include "AccessorData.hpp"
|
||||||
#include "BufferViewData.h"
|
#include "BufferViewData.hpp"
|
||||||
|
|
||||||
AccessorData::AccessorData(const BufferViewData &bufferView, GLType type, std::string name)
|
AccessorData::AccessorData(const BufferViewData &bufferView, GLType type, std::string name)
|
||||||
: Holdable(),
|
: Holdable(),
|
|
@ -7,10 +7,9 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_ACCESSORDATA_H
|
#pragma once
|
||||||
#define FBX2GLTF_ACCESSORDATA_H
|
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
|
|
||||||
struct AccessorData : Holdable
|
struct AccessorData : Holdable
|
||||||
{
|
{
|
||||||
|
@ -45,5 +44,3 @@ struct AccessorData : Holdable
|
||||||
std::vector<float> max;
|
std::vector<float> max;
|
||||||
std::string name;
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FBX2GLTF_ACCESSORDATA_H
|
|
|
@ -7,12 +7,12 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AnimationData.h"
|
#include "AnimationData.hpp"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "AccessorData.h"
|
#include "AccessorData.hpp"
|
||||||
#include "NodeData.h"
|
#include "NodeData.hpp"
|
||||||
|
|
||||||
AnimationData::AnimationData(std::string name, const AccessorData &timeAccessor)
|
AnimationData::AnimationData(std::string name, const AccessorData &timeAccessor)
|
||||||
: Holdable(),
|
: Holdable(),
|
|
@ -7,10 +7,9 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_ANIMATIONDATA_H
|
#pragma once
|
||||||
#define FBX2GLTF_ANIMATIONDATA_H
|
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
|
|
||||||
struct AnimationData : Holdable
|
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::channel_t &data);
|
||||||
void to_json(json &j, const AnimationData::sampler_t &data);
|
void to_json(json &j, const AnimationData::sampler_t &data);
|
||||||
|
|
||||||
#endif //FBX2GLTF_ANIMATIONDATA_H
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
#include <cppcodec/base64_default_rfc4648.hpp>
|
#include <cppcodec/base64_default_rfc4648.hpp>
|
||||||
|
|
||||||
#include "BufferData.h"
|
#include "BufferData.hpp"
|
||||||
|
|
||||||
BufferData::BufferData(const std::shared_ptr<const std::vector<uint8_t> > &binData)
|
BufferData::BufferData(const std::shared_ptr<const std::vector<uint8_t> > &binData)
|
||||||
: Holdable(),
|
: Holdable(),
|
|
@ -7,10 +7,9 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_BUFFERDATA_H
|
#pragma once
|
||||||
#define FBX2GLTF_BUFFERDATA_H
|
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
|
|
||||||
struct BufferData : Holdable
|
struct BufferData : Holdable
|
||||||
{
|
{
|
||||||
|
@ -24,6 +23,3 @@ struct BufferData : Holdable
|
||||||
const std::string uri;
|
const std::string uri;
|
||||||
const std::shared_ptr<const std::vector<uint8_t> > binData; // TODO this is just weird
|
const std::shared_ptr<const std::vector<uint8_t> > binData; // TODO this is just weird
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif //FBX2GLTF_BUFFERDATA_H
|
|
|
@ -7,8 +7,8 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "BufferViewData.h"
|
#include "BufferViewData.hpp"
|
||||||
#include "BufferData.h"
|
#include "BufferData.hpp"
|
||||||
|
|
||||||
BufferViewData::BufferViewData(const BufferData &_buffer, const size_t _byteOffset, const GL_ArrayType _target)
|
BufferViewData::BufferViewData(const BufferData &_buffer, const size_t _byteOffset, const GL_ArrayType _target)
|
||||||
: Holdable(),
|
: Holdable(),
|
|
@ -7,10 +7,9 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_BUFFERVIEW_H
|
#pragma once
|
||||||
#define FBX2GLTF_BUFFERVIEW_H
|
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
|
|
||||||
struct BufferViewData : Holdable
|
struct BufferViewData : Holdable
|
||||||
{
|
{
|
||||||
|
@ -31,5 +30,3 @@ struct BufferViewData : Holdable
|
||||||
|
|
||||||
unsigned int byteLength = 0;
|
unsigned int byteLength = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FBX2GLTF_BUFFERVIEW_H
|
|
|
@ -7,7 +7,7 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "CameraData.h"
|
#include "CameraData.hpp"
|
||||||
|
|
||||||
CameraData::CameraData()
|
CameraData::CameraData()
|
||||||
: Holdable(),
|
: Holdable(),
|
|
@ -7,10 +7,9 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_CAMERADATA_H
|
#pragma once
|
||||||
#define FBX2GLTF_CAMERADATA_H
|
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
|
|
||||||
// TODO: this class needs some work
|
// TODO: this class needs some work
|
||||||
struct CameraData : Holdable
|
struct CameraData : Holdable
|
||||||
|
@ -27,5 +26,3 @@ struct CameraData : Holdable
|
||||||
float znear;
|
float znear;
|
||||||
float zfar;
|
float zfar;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FBX2GLTF_CAMERADATA_H
|
|
|
@ -7,11 +7,11 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ImageData.h"
|
#include "ImageData.hpp"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "BufferViewData.h"
|
#include "BufferViewData.hpp"
|
||||||
|
|
||||||
ImageData::ImageData(std::string name, std::string uri)
|
ImageData::ImageData(std::string name, std::string uri)
|
||||||
: Holdable(),
|
: Holdable(),
|
|
@ -7,10 +7,9 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_IMAGEDATA_H
|
#pragma once
|
||||||
#define FBX2GLTF_IMAGEDATA_H
|
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
|
|
||||||
struct ImageData : Holdable
|
struct ImageData : Holdable
|
||||||
{
|
{
|
||||||
|
@ -24,5 +23,3 @@ struct ImageData : Holdable
|
||||||
const int32_t bufferView; // non-negative in glb mode
|
const int32_t bufferView; // non-negative in glb mode
|
||||||
const std::string mimeType;
|
const std::string mimeType;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FBX2GLTF_IMAGEDATA_H
|
|
|
@ -7,8 +7,8 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "MaterialData.h"
|
#include "MaterialData.hpp"
|
||||||
#include "TextureData.h"
|
#include "TextureData.hpp"
|
||||||
|
|
||||||
// TODO: retrieve & pass in correct UV set from FBX
|
// TODO: retrieve & pass in correct UV set from FBX
|
||||||
std::unique_ptr<Tex> Tex::ref(const TextureData *tex, uint32_t texCoord)
|
std::unique_ptr<Tex> Tex::ref(const TextureData *tex, uint32_t texCoord)
|
|
@ -7,12 +7,11 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_MATERIALDATA_H
|
#pragma once
|
||||||
#define FBX2GLTF_MATERIALDATA_H
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
|
|
||||||
struct Tex
|
struct Tex
|
||||||
{
|
{
|
||||||
|
@ -67,5 +66,3 @@ struct MaterialData : Holdable
|
||||||
void to_json(json &j, const Tex &data);
|
void to_json(json &j, const Tex &data);
|
||||||
void to_json(json &j, const KHRCmnUnlitMaterial &d);
|
void to_json(json &j, const KHRCmnUnlitMaterial &d);
|
||||||
void to_json(json &j, const PBRMetallicRoughness &d);
|
void to_json(json &j, const PBRMetallicRoughness &d);
|
||||||
|
|
||||||
#endif //FBX2GLTF_MATERIALDATA_H
|
|
|
@ -7,8 +7,8 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "MeshData.h"
|
#include "MeshData.hpp"
|
||||||
#include "PrimitiveData.h"
|
#include "PrimitiveData.hpp"
|
||||||
|
|
||||||
MeshData::MeshData(const std::string &name, const std::vector<float> &weights)
|
MeshData::MeshData(const std::string &name, const std::vector<float> &weights)
|
||||||
: Holdable(),
|
: Holdable(),
|
|
@ -7,16 +7,15 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_MESHDATA_H
|
#pragma once
|
||||||
#define FBX2GLTF_MESHDATA_H
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <draco/compression/encode.h>
|
#include <draco/compression/encode.h>
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
|
|
||||||
#include "PrimitiveData.h"
|
#include "PrimitiveData.hpp"
|
||||||
|
|
||||||
struct MeshData : Holdable
|
struct MeshData : Holdable
|
||||||
{
|
{
|
||||||
|
@ -33,5 +32,3 @@ struct MeshData : Holdable
|
||||||
const std::vector<float> weights;
|
const std::vector<float> weights;
|
||||||
std::vector<std::shared_ptr<PrimitiveData>> primitives;
|
std::vector<std::shared_ptr<PrimitiveData>> primitives;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FBX2GLTF_MESHDATA_H
|
|
|
@ -7,7 +7,7 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "NodeData.h"
|
#include "NodeData.hpp"
|
||||||
|
|
||||||
NodeData::NodeData(
|
NodeData::NodeData(
|
||||||
std::string name, const Vec3f &translation,
|
std::string name, const Vec3f &translation,
|
||||||
|
@ -55,7 +55,7 @@ json NodeData::serialize() const
|
||||||
json result = { { "name", name } };
|
json result = { { "name", name } };
|
||||||
|
|
||||||
// if any of the T/R/S have NaN components, just leave them out of the glTF
|
// if any of the T/R/S have NaN components, just leave them out of the glTF
|
||||||
auto maybeAdd = [&](std::string key, std::vector<float> vec) {
|
auto maybeAdd = [&](std::string key, std::vector<float> vec) -> void {
|
||||||
if (std::none_of(vec.begin(), vec.end(), [&](float n) { return isnan(n); })) {
|
if (std::none_of(vec.begin(), vec.end(), [&](float n) { return isnan(n); })) {
|
||||||
result[key] = vec;
|
result[key] = vec;
|
||||||
}
|
}
|
|
@ -7,10 +7,9 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_NODEDATA_H
|
#pragma once
|
||||||
#define FBX2GLTF_NODEDATA_H
|
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
|
|
||||||
struct NodeData : Holdable
|
struct NodeData : Holdable
|
||||||
{
|
{
|
||||||
|
@ -34,5 +33,3 @@ struct NodeData : Holdable
|
||||||
int32_t skin;
|
int32_t skin;
|
||||||
std::vector<std::string> skeletons;
|
std::vector<std::string> skeletons;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FBX2GLTF_NODEDATA_H
|
|
|
@ -7,11 +7,11 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PrimitiveData.h"
|
#include "PrimitiveData.hpp"
|
||||||
|
|
||||||
#include "MaterialData.h"
|
#include "MaterialData.hpp"
|
||||||
#include "AccessorData.h"
|
#include "AccessorData.hpp"
|
||||||
#include "BufferViewData.h"
|
#include "BufferViewData.hpp"
|
||||||
|
|
||||||
PrimitiveData::PrimitiveData(const AccessorData &indices, const MaterialData &material, std::shared_ptr<draco::Mesh> dracoMesh)
|
PrimitiveData::PrimitiveData(const AccessorData &indices, const MaterialData &material, std::shared_ptr<draco::Mesh> dracoMesh)
|
||||||
: indices(indices.ix),
|
: indices(indices.ix),
|
||||||
|
@ -43,8 +43,8 @@ void PrimitiveData::AddTarget(const AccessorData *positions, const AccessorData
|
||||||
{
|
{
|
||||||
targetAccessors.push_back(std::make_tuple(
|
targetAccessors.push_back(std::make_tuple(
|
||||||
positions->ix,
|
positions->ix,
|
||||||
normals ? normals->ix : -1,
|
normals != nullptr ? normals->ix : -1,
|
||||||
tangents ? tangents ->ix : -1
|
tangents != nullptr ? tangents ->ix : -1
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,9 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_PRIMITIVEDATA_H
|
#pragma once
|
||||||
#define FBX2GLTF_PRIMITIVEDATA_H
|
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
|
|
||||||
struct PrimitiveData
|
struct PrimitiveData
|
||||||
{
|
{
|
||||||
|
@ -71,5 +70,3 @@ struct PrimitiveData
|
||||||
};
|
};
|
||||||
|
|
||||||
void to_json(json &j, const PrimitiveData &d);
|
void to_json(json &j, const PrimitiveData &d);
|
||||||
|
|
||||||
#endif //FBX2GLTF_PRIMITIVEDATA_H
|
|
|
@ -7,10 +7,9 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_SAMPLERDATA_H
|
#pragma once
|
||||||
#define FBX2GLTF_SAMPLERDATA_H
|
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
|
|
||||||
struct SamplerData : Holdable
|
struct SamplerData : Holdable
|
||||||
{
|
{
|
||||||
|
@ -20,9 +19,7 @@ struct SamplerData : Holdable
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
json serialize() const {
|
json serialize() const override {
|
||||||
return json::object();
|
return json::object();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FBX2GLTF_SAMPLERDATA_H
|
|
|
@ -7,9 +7,9 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* 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)
|
SceneData::SceneData(std::string name, const NodeData &rootNode)
|
||||||
: Holdable(),
|
: Holdable(),
|
|
@ -7,10 +7,7 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_SCENEDATA_H
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
#define FBX2GLTF_SCENEDATA_H
|
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
|
||||||
|
|
||||||
struct SceneData : Holdable
|
struct SceneData : Holdable
|
||||||
{
|
{
|
||||||
|
@ -21,5 +18,3 @@ struct SceneData : Holdable
|
||||||
const std::string name;
|
const std::string name;
|
||||||
std::vector<uint32_t> nodes;
|
std::vector<uint32_t> nodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FBX2GLTF_SCENEDATA_H
|
|
|
@ -7,10 +7,10 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "SkinData.h"
|
#include "SkinData.hpp"
|
||||||
|
|
||||||
#include "AccessorData.h"
|
#include "AccessorData.hpp"
|
||||||
#include "NodeData.h"
|
#include "NodeData.hpp"
|
||||||
|
|
||||||
SkinData::SkinData(
|
SkinData::SkinData(
|
||||||
const std::vector<uint32_t> joints, const AccessorData &inverseBindMatricesAccessor,
|
const std::vector<uint32_t> joints, const AccessorData &inverseBindMatricesAccessor,
|
|
@ -7,10 +7,7 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_SKINDATA_H
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
#define FBX2GLTF_SKINDATA_H
|
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
|
||||||
|
|
||||||
struct SkinData : Holdable
|
struct SkinData : Holdable
|
||||||
{
|
{
|
||||||
|
@ -24,5 +21,3 @@ struct SkinData : Holdable
|
||||||
const uint32_t skeletonRootNode;
|
const uint32_t skeletonRootNode;
|
||||||
const uint32_t inverseBindMatrices;
|
const uint32_t inverseBindMatrices;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FBX2GLTF_SKINDATA_H
|
|
|
@ -7,10 +7,10 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "TextureData.h"
|
#include "TextureData.hpp"
|
||||||
|
|
||||||
#include "ImageData.h"
|
#include "ImageData.hpp"
|
||||||
#include "SamplerData.h"
|
#include "SamplerData.hpp"
|
||||||
|
|
||||||
TextureData::TextureData(std::string name, const SamplerData &sampler, const ImageData &source)
|
TextureData::TextureData(std::string name, const SamplerData &sampler, const ImageData &source)
|
||||||
: Holdable(),
|
: Holdable(),
|
|
@ -7,10 +7,7 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FBX2GLTF_TEXTUREDATA_H
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
#define FBX2GLTF_TEXTUREDATA_H
|
|
||||||
|
|
||||||
#include "Raw2Gltf.h"
|
|
||||||
|
|
||||||
struct TextureData : Holdable
|
struct TextureData : Holdable
|
||||||
{
|
{
|
||||||
|
@ -22,5 +19,3 @@ struct TextureData : Holdable
|
||||||
const uint32_t sampler;
|
const uint32_t sampler;
|
||||||
const uint32_t source;
|
const uint32_t source;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FBX2GLTF_TEXTUREDATA_H
|
|
|
@ -6,8 +6,8 @@
|
||||||
* 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.
|
* 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 <fbxsdk.h>
|
#include <fbxsdk.h>
|
||||||
|
|
||||||
|
@ -58,12 +58,12 @@ typedef mathfu::Matrix<float, 4> Mat4f;
|
||||||
typedef mathfu::Quaternion<float> Quatf;
|
typedef mathfu::Quaternion<float> Quatf;
|
||||||
typedef Bounds<float, 3> Boundsf;
|
typedef Bounds<float, 3> Boundsf;
|
||||||
|
|
||||||
const Vec3f VEC3F_ONE = Vec3f {1.0f};
|
#define VEC3F_ONE (Vec3f {1.0f})
|
||||||
const Vec3f VEC3F_ZERO = Vec3f {0.0f};
|
#define VEC3F_ZERO (Vec3f {0.0f})
|
||||||
const Vec4f VEC4F_ONE = Vec4f {1.0f};
|
#define VEC4F_ONE (Vec4f {1.0f})
|
||||||
const Vec4f VEC4F_ZERO = Vec4f {0.0f};
|
#define VEC4F_ZERO (Vec4f {0.0f})
|
||||||
|
|
||||||
template<class T, int d> static inline std::vector<T> toStdVec(const mathfu::Vector <T, d> &vec)
|
template<class T, int d> inline std::vector<T> toStdVec(const mathfu::Vector <T, d> &vec)
|
||||||
{
|
{
|
||||||
std::vector<T> result(d);
|
std::vector<T> result(d);
|
||||||
for (int ii = 0; ii < d; ii ++) {
|
for (int ii = 0; ii < d; ii ++) {
|
||||||
|
@ -76,15 +76,15 @@ template<class T> std::vector<T> toStdVec(const mathfu::Quaternion<T> &quat) {
|
||||||
return std::vector<T> { quat.vector()[0], quat.vector()[1], quat.vector()[2], quat.scalar() };
|
return std::vector<T> { 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]);
|
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]);
|
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();
|
auto result = Mat4f();
|
||||||
for (int row = 0; row < 4; row ++) {
|
for (int row = 0; row < 4; row ++) {
|
||||||
for (int col = 0; col < 4; col ++) {
|
for (int col = 0; col < 4; col ++) {
|
||||||
|
@ -94,8 +94,6 @@ static inline Mat4f toMat4f(const FbxAMatrix &m) {
|
||||||
return result;
|
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]);
|
return Quatf((float) q[3], (float) q[0], (float) q[1], (float) q[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //FBX2GLTF_MATHFU_H
|
|
|
@ -7,6 +7,8 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "RawModel.hpp"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
@ -18,12 +20,8 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "FBX2glTF.h"
|
#include "utils/String_Utils.hpp"
|
||||||
#include "utils/String_Utils.h"
|
#include "utils/Image_Utils.hpp"
|
||||||
#include "utils/Image_Utils.h"
|
|
||||||
#include "RawModel.h"
|
|
||||||
|
|
||||||
extern bool verboseOutput;
|
|
||||||
|
|
||||||
bool RawVertex::operator==(const RawVertex &other) const
|
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;
|
RawTexture texture;
|
||||||
texture.name = name;
|
texture.name = name;
|
||||||
|
@ -104,7 +103,7 @@ int RawModel::AddTexture(const std::string &name, const std::string &fileName, c
|
||||||
texture.height = properties.height;
|
texture.height = properties.height;
|
||||||
texture.mipLevels = (int) ceilf(log2f(std::max((float) properties.width, (float) properties.height)));
|
texture.mipLevels = (int) ceilf(log2f(std::max((float) properties.width, (float) properties.height)));
|
||||||
texture.usage = usage;
|
texture.usage = usage;
|
||||||
texture.occlusion = (properties.occlusion == IMAGE_TRANSPARENT) ?
|
texture.occlusion = (properties.occlusion == ImageUtils::IMAGE_TRANSPARENT) ?
|
||||||
RAW_TEXTURE_OCCLUSION_TRANSPARENT : RAW_TEXTURE_OCCLUSION_OPAQUE;
|
RAW_TEXTURE_OCCLUSION_TRANSPARENT : RAW_TEXTURE_OCCLUSION_OPAQUE;
|
||||||
texture.fileName = fileName;
|
texture.fileName = fileName;
|
||||||
texture.fileLocation = fileLocation;
|
texture.fileLocation = fileLocation;
|
||||||
|
@ -461,7 +460,7 @@ void RawModel::CreateMaterialModels(
|
||||||
// Overestimate the number of models that will be created to avoid massive reallocation.
|
// Overestimate the number of models that will be created to avoid massive reallocation.
|
||||||
int discreteCount = 0;
|
int discreteCount = 0;
|
||||||
for (const auto &surface : surfaces) {
|
for (const auto &surface : surfaces) {
|
||||||
discreteCount += (surface.discrete != false);
|
discreteCount += surface.discrete ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
materialModels.clear();
|
materialModels.clear();
|
|
@ -7,13 +7,14 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __RAWMODEL_H__
|
#pragma once
|
||||||
#define __RAWMODEL_H__
|
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
#include "FBX2glTF.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The variuos situations in which the user may wish for us to (re-)compute normals for our vertices.
|
* 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
|
RAW_SHADING_MODEL_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline std::string Describe(RawShadingModel model) {
|
inline std::string Describe(RawShadingModel model) {
|
||||||
switch(model) {
|
switch(model) {
|
||||||
case RAW_SHADING_MODEL_UNKNOWN: return "<unknown>";
|
case RAW_SHADING_MODEL_UNKNOWN: return "<unknown>";
|
||||||
case RAW_SHADING_MODEL_CONSTANT: return "Constant";
|
case RAW_SHADING_MODEL_CONSTANT: return "Constant";
|
||||||
|
@ -194,7 +195,7 @@ enum RawTextureUsage
|
||||||
RAW_TEXTURE_USAGE_MAX
|
RAW_TEXTURE_USAGE_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline std::string Describe(RawTextureUsage usage)
|
inline std::string Describe(RawTextureUsage usage)
|
||||||
{
|
{
|
||||||
switch (usage) {
|
switch (usage) {
|
||||||
case RAW_TEXTURE_USAGE_NONE: return "<none>";
|
case RAW_TEXTURE_USAGE_NONE: return "<none>";
|
||||||
|
@ -515,5 +516,3 @@ void RawModel::GetAttributeArray(std::vector<_attrib_type_> &out, const _attrib_
|
||||||
out[i] = vertices[i].*ptr;
|
out[i] = vertices[i].*ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !__RAWMODEL_H__
|
|
|
@ -7,6 +7,8 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "File_Utils.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -35,7 +37,7 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "FBX2glTF.h"
|
#include "FBX2glTF.h"
|
||||||
#include "String_Utils.h"
|
#include "String_Utils.hpp"
|
||||||
|
|
||||||
namespace FileUtils {
|
namespace FileUtils {
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,13 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __FILE_UTILS_H__
|
#pragma once
|
||||||
#define __FILE_UTILS_H__
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace FileUtils {
|
namespace FileUtils {
|
||||||
|
|
||||||
std::string GetCurrentFolder();
|
std::string GetCurrentFolder();
|
||||||
|
|
||||||
bool FileExists(const std::string &folderPath);
|
bool FileExists(const std::string &folderPath);
|
||||||
|
@ -22,6 +25,5 @@ namespace FileUtils {
|
||||||
bool CreatePath(const char *path);
|
bool CreatePath(const char *path);
|
||||||
|
|
||||||
bool CopyFile(const std::string &srcFilename, const std::string &dstFilename);
|
bool CopyFile(const std::string &srcFilename, const std::string &dstFilename);
|
||||||
}
|
|
||||||
|
|
||||||
#endif // !__FILE_UTILS_H__
|
}
|
|
@ -7,33 +7,40 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Image_Utils.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
|
||||||
#include <stb_image.h>
|
#include <stb_image.h>
|
||||||
|
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
|
||||||
#include <stb_image_write.h>
|
#include <stb_image_write.h>
|
||||||
|
|
||||||
#include "Image_Utils.h"
|
namespace ImageUtils {
|
||||||
|
|
||||||
static bool imageHasTransparentPixels(FILE *f) {
|
static bool imageHasTransparentPixels(FILE *f)
|
||||||
|
{
|
||||||
int width, height, channels;
|
int width, height, channels;
|
||||||
// RGBA: we have to load the pixels to figure out if the image is fully opaque
|
// 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);
|
uint8_t *pixels = stbi_load_from_file(f, &width, &height, &channels, 0);
|
||||||
if (pixels != nullptr) {
|
if (pixels != nullptr) {
|
||||||
int pixelCount = width * height;
|
int pixelCount = width * height;
|
||||||
for (int ix = 0; ix < pixelCount; ix ++) {
|
for (int ix = 0; ix < pixelCount; ix++) {
|
||||||
// test fourth byte (alpha); 255 is 1.0
|
// test fourth byte (alpha); 255 is 1.0
|
||||||
if (pixels[4*ix + 3] != 255) {
|
if (pixels[4 * ix + 3] != 255) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageProperties GetImageProperties(char const *filePath)
|
ImageProperties GetImageProperties(char const *filePath)
|
||||||
{
|
{
|
||||||
ImageProperties result = {
|
ImageProperties result = {
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
@ -52,5 +59,19 @@ ImageProperties GetImageProperties(char const *filePath)
|
||||||
result.occlusion = IMAGE_TRANSPARENT;
|
result.occlusion = IMAGE_TRANSPARENT;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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 <algorithm>
|
|
||||||
|
|
||||||
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__
|
|
|
@ -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 <string>
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
|
@ -7,7 +7,7 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "String_Utils.h"
|
#include "String_Utils.hpp"
|
||||||
|
|
||||||
namespace StringUtils {
|
namespace StringUtils {
|
||||||
|
|
||||||
|
@ -16,4 +16,48 @@ namespace StringUtils {
|
||||||
return (s == PATH_WIN) ? PATH_UNIX : PATH_WIN;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 <string>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cstdarg>
|
|
||||||
|
|
||||||
#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<size_t size>
|
|
||||||
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__
|
|
||||||
|
|
|
@ -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 <string>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdarg>
|
||||||
|
|
||||||
|
#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<size_t size>
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue