Merge remote-tracking branch 'upstream/master' into feat/mocha-tests
This commit is contained in:
commit
8874a38751
|
@ -0,0 +1,87 @@
|
||||||
|
---
|
||||||
|
AccessModifierOffset: -1
|
||||||
|
AlignAfterOpenBracket: AlwaysBreak
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignEscapedNewlinesLeft: true
|
||||||
|
AlignOperands: false
|
||||||
|
AlignTrailingComments: false
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: true
|
||||||
|
AlwaysBreakTemplateDeclarations: true
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BraceWrapping:
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
BreakAfterJavaFieldAnnotations: false
|
||||||
|
BreakStringLiterals: false
|
||||||
|
ColumnLimit: 80
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
ForEachMacros: [ FOR_EACH, FOR_EACH_ENUMERATE, FOR_EACH_KV, FOR_EACH_R, FOR_EACH_RANGE, FOR_EACH_RANGE_R, ]
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^<.*\.h(pp)?>'
|
||||||
|
Priority: 1
|
||||||
|
- Regex: '^<.*'
|
||||||
|
Priority: 2
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 3
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentWidth: 2
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBlockIndentWidth: 2
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: false
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 1
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 200
|
||||||
|
PointerAlignment: Left
|
||||||
|
ReflowComments: true
|
||||||
|
SortIncludes: true
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
Standard: Cpp11
|
||||||
|
TabWidth: 8
|
||||||
|
UseTab: Never
|
||||||
|
...
|
|
@ -32,6 +32,9 @@ set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
find_package(Iconv QUIET)
|
find_package(Iconv QUIET)
|
||||||
|
|
||||||
|
# create a compilation database for e.g. clang-tidy
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
# this will suffice for now; don't really care about 32-bit
|
# this will suffice for now; don't really care about 32-bit
|
||||||
set(LIBXML2_INCLUDE_DIRS ${FBXSDK_INCLUDE_DIR})
|
set(LIBXML2_INCLUDE_DIRS ${FBXSDK_INCLUDE_DIR})
|
||||||
|
@ -127,17 +130,6 @@ ExternalProject_Add(CPPCodec
|
||||||
)
|
)
|
||||||
set(CPPCODEC_INCLUDE_DIR "${CMAKE_BINARY_DIR}/cppcodec/src/CPPCodec")
|
set(CPPCODEC_INCLUDE_DIR "${CMAKE_BINARY_DIR}/cppcodec/src/CPPCodec")
|
||||||
|
|
||||||
# CXXOPTS
|
|
||||||
ExternalProject_Add(CxxOpts
|
|
||||||
GIT_REPOSITORY https://github.com/jarro2783/cxxopts
|
|
||||||
GIT_TAG v1.4.4
|
|
||||||
PREFIX cxxopts
|
|
||||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "Skipping cxxopts configure step."
|
|
||||||
BUILD_COMMAND ${CMAKE_COMMAND} -E echo "Skipping cxxopts build step."
|
|
||||||
INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Skipping cxxopts install step."
|
|
||||||
)
|
|
||||||
set(CXXOPTS_INCLUDE_DIR "${CMAKE_BINARY_DIR}/cxxopts/src/CxxOpts/include")
|
|
||||||
|
|
||||||
# FMT
|
# FMT
|
||||||
ExternalProject_Add(Fmt
|
ExternalProject_Add(Fmt
|
||||||
PREFIX fmt
|
PREFIX fmt
|
||||||
|
@ -194,6 +186,8 @@ set(LIB_SOURCE_FILES
|
||||||
src/gltf/properties/CameraData.hpp
|
src/gltf/properties/CameraData.hpp
|
||||||
src/gltf/properties/ImageData.cpp
|
src/gltf/properties/ImageData.cpp
|
||||||
src/gltf/properties/ImageData.hpp
|
src/gltf/properties/ImageData.hpp
|
||||||
|
src/gltf/properties/LightData.cpp
|
||||||
|
src/gltf/properties/LightData.hpp
|
||||||
src/gltf/properties/MaterialData.cpp
|
src/gltf/properties/MaterialData.cpp
|
||||||
src/gltf/properties/MaterialData.hpp
|
src/gltf/properties/MaterialData.hpp
|
||||||
src/gltf/properties/MeshData.cpp
|
src/gltf/properties/MeshData.cpp
|
||||||
|
@ -218,6 +212,7 @@ set(LIB_SOURCE_FILES
|
||||||
src/utils/Image_Utils.hpp
|
src/utils/Image_Utils.hpp
|
||||||
src/utils/String_Utils.cpp
|
src/utils/String_Utils.cpp
|
||||||
src/utils/String_Utils.hpp
|
src/utils/String_Utils.hpp
|
||||||
|
third_party/CLI11/CLI11.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(libFBX2glTF STATIC ${LIB_SOURCE_FILES})
|
add_library(libFBX2glTF STATIC ${LIB_SOURCE_FILES})
|
||||||
|
@ -231,7 +226,6 @@ add_dependencies(libFBX2glTF
|
||||||
FiFoMap
|
FiFoMap
|
||||||
Json
|
Json
|
||||||
STB
|
STB
|
||||||
CxxOpts
|
|
||||||
CPPCodec
|
CPPCodec
|
||||||
Fmt
|
Fmt
|
||||||
)
|
)
|
||||||
|
@ -295,7 +289,7 @@ if (Iconv_FOUND)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(appFBX2glTF PUBLIC
|
target_include_directories(appFBX2glTF PUBLIC
|
||||||
${CXXOPTS_INCLUDE_DIR}
|
"third_party/CLI11"
|
||||||
)
|
)
|
||||||
target_link_libraries(appFBX2glTF libFBX2glTF)
|
target_link_libraries(appFBX2glTF libFBX2glTF)
|
||||||
|
|
||||||
|
|
637
src/FBX2glTF.cpp
637
src/FBX2glTF.cpp
|
@ -7,307 +7,392 @@
|
||||||
* 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 <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <map>
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#if defined( __unix__ ) || defined( __APPLE__ )
|
#if defined(__unix__) || defined(__APPLE__)
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#define _stricmp strcasecmp
|
#define _stricmp strcasecmp
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cxxopts.hpp>
|
#include <CLI11.hpp>
|
||||||
|
|
||||||
#include "FBX2glTF.h"
|
#include "FBX2glTF.h"
|
||||||
#include "utils/String_Utils.hpp"
|
|
||||||
#include "utils/File_Utils.hpp"
|
|
||||||
#include "fbx/Fbx2Raw.hpp"
|
#include "fbx/Fbx2Raw.hpp"
|
||||||
#include "gltf/Raw2Gltf.hpp"
|
#include "gltf/Raw2Gltf.hpp"
|
||||||
|
#include "utils/File_Utils.hpp"
|
||||||
|
#include "utils/String_Utils.hpp"
|
||||||
|
|
||||||
bool verboseOutput = false;
|
bool verboseOutput = false;
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char* argv[]) {
|
||||||
{
|
GltfOptions gltfOptions;
|
||||||
cxxopts::Options options(
|
|
||||||
"FBX2glTF",
|
|
||||||
fmt::sprintf("FBX2glTF %s: Generate a glTF 2.0 representation of an FBX model.", FBX2GLTF_VERSION)
|
|
||||||
);
|
|
||||||
|
|
||||||
std::string inputPath;
|
CLI::App app{
|
||||||
std::string outputPath;
|
fmt::sprintf(
|
||||||
|
"FBX2glTF %s: Generate a glTF 2.0 representation of an FBX model.",
|
||||||
|
FBX2GLTF_VERSION),
|
||||||
|
"FBX2glTF"};
|
||||||
|
|
||||||
std::vector<std::function<Vec2f(Vec2f)>> texturesTransforms;
|
app.add_flag(
|
||||||
|
"-v,--verbose",
|
||||||
GltfOptions gltfOptions;
|
verboseOutput,
|
||||||
|
"Include blend shape tangents, if reported present by the FBX SDK.");
|
||||||
options.positional_help("[<FBX File>]");
|
|
||||||
options.add_options()
|
|
||||||
(
|
|
||||||
"i,input", "The FBX model to convert.",
|
|
||||||
cxxopts::value<std::string>(inputPath))
|
|
||||||
(
|
|
||||||
"o,output", "Where to generate the output, without suffix.",
|
|
||||||
cxxopts::value<std::string>(outputPath))
|
|
||||||
(
|
|
||||||
"e,embed", "Inline buffers as data:// URIs within generated non-binary glTF.",
|
|
||||||
cxxopts::value<bool>(gltfOptions.embedResources))
|
|
||||||
(
|
|
||||||
"b,binary", "Output a single binary format .glb file.",
|
|
||||||
cxxopts::value<bool>(gltfOptions.outputBinary))
|
|
||||||
(
|
|
||||||
"long-indices", "Whether to use 32-bit indices (never|auto|always).",
|
|
||||||
cxxopts::value<std::vector<std::string>>())
|
|
||||||
(
|
|
||||||
"d,draco", "Apply Draco mesh compression to geometries.",
|
|
||||||
cxxopts::value<bool>(gltfOptions.draco.enabled))
|
|
||||||
(
|
|
||||||
"draco-compression-level", "The compression level to tune Draco to, from 0 to 10. (default: 7)",
|
|
||||||
cxxopts::value<int>(gltfOptions.draco.compressionLevel))
|
|
||||||
(
|
|
||||||
"draco-bits-for-positions", "How many bits to quantize position to. (default: 14)",
|
|
||||||
cxxopts::value<int>(gltfOptions.draco.quantBitsPosition))
|
|
||||||
(
|
|
||||||
"draco-bits-for-uv", "How many bits to quantize UV coordinates to. (default: 10)",
|
|
||||||
cxxopts::value<int>(gltfOptions.draco.quantBitsTexCoord))
|
|
||||||
(
|
|
||||||
"draco-bits-for-normals", "How many bits to quantize normals to. (default: 10)",
|
|
||||||
cxxopts::value<int>(gltfOptions.draco.quantBitsNormal))
|
|
||||||
(
|
|
||||||
"draco-bits-for-colors", "How many bits to quantize color to. (default: 8)",
|
|
||||||
cxxopts::value<int>(gltfOptions.draco.quantBitsColor))
|
|
||||||
(
|
|
||||||
"draco-bits-for-other", "How many bits to quantize other vertex attributes to to. (default: 8)",
|
|
||||||
cxxopts::value<int>(gltfOptions.draco.quantBitsGeneric))
|
|
||||||
(
|
|
||||||
"compute-normals", "When to compute normals for vertices (never|broken|missing|always).",
|
|
||||||
cxxopts::value<std::vector<std::string>>())
|
|
||||||
("flip-u", "Flip all U texture coordinates.")
|
|
||||||
("flip-v", "Flip all V texture coordinates (default behaviour!)")
|
|
||||||
("no-flip-v", "Suppress the default flipping of V texture coordinates")
|
|
||||||
(
|
|
||||||
"pbr-metallic-roughness", "Try to glean glTF 2.0 native PBR attributes from the FBX.",
|
|
||||||
cxxopts::value<bool>(gltfOptions.usePBRMetRough))
|
|
||||||
(
|
|
||||||
"khr-materials-unlit", "Use KHR_materials_unlit extension to specify Unlit shader.",
|
|
||||||
cxxopts::value<bool>(gltfOptions.useKHRMatUnlit))
|
|
||||||
(
|
|
||||||
"user-properties", "Transcribe FBX User Properties into glTF node 'extras'.",
|
|
||||||
cxxopts::value<bool>(gltfOptions.enableUserProperties))
|
|
||||||
(
|
|
||||||
"blend-shape-normals", "Include blend shape normals, if reported present by the FBX SDK.",
|
|
||||||
cxxopts::value<bool>(gltfOptions.useBlendShapeNormals))
|
|
||||||
(
|
|
||||||
"blend-shape-tangents", "Include blend shape tangents, if reported present by the FBX SDK.",
|
|
||||||
cxxopts::value<bool>(gltfOptions.useBlendShapeTangents))
|
|
||||||
(
|
|
||||||
"k,keep-attribute", "Used repeatedly to build a limiting set of vertex attributes to keep.",
|
|
||||||
cxxopts::value<std::vector<std::string>>())
|
|
||||||
("v,verbose", "Enable verbose output.")
|
|
||||||
("h,help", "Show this help.")
|
|
||||||
("V,version", "Display the current program version.");
|
|
||||||
|
|
||||||
try {
|
|
||||||
options.parse_positional("input");
|
|
||||||
options.parse(argc, argv);
|
|
||||||
|
|
||||||
} catch (const cxxopts::OptionException &e) {
|
|
||||||
fmt::printf(options.help());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.count("version")) {
|
|
||||||
fmt::printf("FBX2glTF version %s\nCopyright (c) 2016-2018 Oculus VR, LLC.\n", FBX2GLTF_VERSION);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.count("help")) {
|
|
||||||
fmt::printf(options.help());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.count("input")) {
|
|
||||||
fmt::printf("You must supply a FBX file to convert.\n");
|
|
||||||
fmt::printf(options.help());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.count("verbose")) {
|
|
||||||
verboseOutput = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gltfOptions.useKHRMatUnlit && !gltfOptions.usePBRMetRough) {
|
|
||||||
if (verboseOutput) {
|
|
||||||
fmt::printf("Defaulting to --pbr-metallic-roughness material support.\n");
|
|
||||||
}
|
|
||||||
gltfOptions.usePBRMetRough = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gltfOptions.draco.compressionLevel != -1 &&
|
|
||||||
(gltfOptions.draco.compressionLevel < 1 || gltfOptions.draco.compressionLevel > 10)) {
|
|
||||||
fmt::printf("Draco compression level must lie in [1, 10].\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.count("flip-u") > 0) {
|
|
||||||
texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(1.0f - uv[0], uv[1]); });
|
|
||||||
}
|
|
||||||
if (options.count("flip-v") > 0) {
|
|
||||||
fmt::printf("Note: The --flip-v command switch is now default behaviour.\n");
|
|
||||||
}
|
|
||||||
if (options.count("no-flip-v") == 0) {
|
|
||||||
texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(uv[0], 1.0f - uv[1]); });
|
|
||||||
} else if (verboseOutput) {
|
|
||||||
fmt::printf("Suppressing --flip-v transformation of texture coordinates.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::string &choice : options["long-indices"].as<std::vector<std::string>>()) {
|
|
||||||
if (choice == "never") {
|
|
||||||
gltfOptions.useLongIndices = UseLongIndicesOptions::NEVER;
|
|
||||||
} else if (choice == "auto") {
|
|
||||||
gltfOptions.useLongIndices = UseLongIndicesOptions::AUTO;
|
|
||||||
} else if (choice == "always") {
|
|
||||||
gltfOptions.useLongIndices = UseLongIndicesOptions::ALWAYS;
|
|
||||||
} else {
|
|
||||||
fmt::printf("Unknown --long-indices: %s\n", choice);
|
|
||||||
fmt::printf(options.help());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.count("compute-normals") > 0) {
|
|
||||||
for (const std::string &choice : options["compute-normals"].as<std::vector<std::string>>()) {
|
|
||||||
if (choice == "never") {
|
|
||||||
gltfOptions.computeNormals = ComputeNormalsOption::NEVER;
|
|
||||||
} else if (choice == "broken") {
|
|
||||||
gltfOptions.computeNormals = ComputeNormalsOption::BROKEN;
|
|
||||||
} else if (choice == "missing") {
|
|
||||||
gltfOptions.computeNormals = ComputeNormalsOption::MISSING;
|
|
||||||
} else if (choice == "always") {
|
|
||||||
gltfOptions.computeNormals = ComputeNormalsOption::ALWAYS;
|
|
||||||
} else {
|
|
||||||
fmt::printf("Unknown --compute-normals: %s\n", choice);
|
|
||||||
fmt::printf(options.help());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.count("keep-attribute") > 0) {
|
|
||||||
gltfOptions.keepAttribs = RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS;
|
|
||||||
for (std::string attribute : options["keep-attribute"].as<std::vector<std::string>>()) {
|
|
||||||
if (attribute == "position") { gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_POSITION; }
|
|
||||||
else if (attribute == "normal") { gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_NORMAL; }
|
|
||||||
else if (attribute == "tangent") { gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_TANGENT; }
|
|
||||||
else if (attribute == "binormal") { gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_BINORMAL; }
|
|
||||||
else if (attribute == "color") { gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_COLOR; }
|
|
||||||
else if (attribute == "uv0") { gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_UV0; }
|
|
||||||
else if (attribute == "uv1") { gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_UV1; }
|
|
||||||
else if (attribute == "auto") { gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_AUTO; }
|
|
||||||
else {
|
|
||||||
fmt::printf("Unknown --keep-attribute: %s\n", attribute);
|
|
||||||
fmt::printf("Valid choices are: position, normal, tangent, binormial, color, uv0, uv1, auto,\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gltfOptions.embedResources && gltfOptions.outputBinary) {
|
|
||||||
fmt::printf("Note: Ignoring --embed; it's meaningless with --binary.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.count("output") == 0) {
|
|
||||||
// if -o is not given, default to the basename of the .fbx
|
|
||||||
outputPath = fmt::format(".{}{}", (const char)StringUtils::GetPathSeparator(), StringUtils::GetFileBaseString(inputPath));
|
|
||||||
|
|
||||||
fmt::printf("outputPath = %s\n", outputPath);
|
|
||||||
}
|
|
||||||
std::string outputFolder; // the output folder in .gltf mode, not used for .glb
|
|
||||||
std::string modelPath; // the path of the actual .glb or .gltf file
|
|
||||||
if (gltfOptions.outputBinary) {
|
|
||||||
// in binary mode, we write precisely where we're asked
|
|
||||||
modelPath = outputPath + ".glb";
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// in gltf mode, we create a folder and write into that
|
|
||||||
outputFolder = fmt::format("{}_out{}", outputFolder.c_str(), (const char)StringUtils::GetPathSeparator());
|
|
||||||
modelPath = outputFolder + StringUtils::GetFileNameString(outputPath) + ".gltf";
|
|
||||||
}
|
|
||||||
if (!FileUtils::CreatePath(modelPath.c_str())) {
|
|
||||||
fmt::fprintf(stderr, "ERROR: Failed to create folder: %s'\n", outputFolder.c_str());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModelData *data_render_model = nullptr;
|
|
||||||
RawModel raw;
|
|
||||||
|
|
||||||
if (verboseOutput) {
|
|
||||||
fmt::printf("Loading FBX File: %s\n", inputPath);
|
|
||||||
}
|
|
||||||
if (!LoadFBXFile(raw, inputPath.c_str(), "png;jpg;jpeg")) {
|
|
||||||
fmt::fprintf(stderr, "ERROR:: Failed to parse FBX: %s\n", inputPath);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!texturesTransforms.empty()) {
|
|
||||||
raw.TransformTextures(texturesTransforms);
|
|
||||||
}
|
|
||||||
raw.Condense();
|
|
||||||
raw.TransformGeometry(gltfOptions.computeNormals);
|
|
||||||
|
|
||||||
std::ofstream outStream; // note: auto-flushes in destructor
|
|
||||||
const auto streamStart = outStream.tellp();
|
|
||||||
|
|
||||||
outStream.open(modelPath, std::ios::trunc | std::ios::ate | std::ios::out | std::ios::binary);
|
|
||||||
if (outStream.fail()) {
|
|
||||||
fmt::fprintf(stderr, "ERROR:: Couldn't open file for writing: %s\n", modelPath.c_str());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
data_render_model = Raw2Gltf(outStream, outputFolder, raw, gltfOptions);
|
|
||||||
|
|
||||||
if (gltfOptions.outputBinary) {
|
|
||||||
fmt::printf(
|
|
||||||
"Wrote %lu bytes of binary glTF to %s.\n",
|
|
||||||
(unsigned long) (outStream.tellp() - streamStart), modelPath);
|
|
||||||
delete data_render_model;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
app.add_flag_function("-V,--version", [&](size_t count) {
|
||||||
fmt::printf(
|
fmt::printf(
|
||||||
"Wrote %lu bytes of glTF to %s.\n",
|
"FBX2glTF version %s\nCopyright (c) 2016-2018 Oculus VR, LLC.\n",
|
||||||
(unsigned long) (outStream.tellp() - streamStart), modelPath);
|
FBX2GLTF_VERSION);
|
||||||
|
exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
if (gltfOptions.embedResources) {
|
std::string inputPath;
|
||||||
// we're done: everything was inlined into the glTF JSON
|
app.add_option("FBX Model", inputPath, "The FBX model to convert.")
|
||||||
delete data_render_model;
|
->check(CLI::ExistingFile);
|
||||||
return 0;
|
app.add_option("-i,--input", inputPath, "The FBX model to convert.")
|
||||||
}
|
->check(CLI::ExistingFile);
|
||||||
|
|
||||||
assert(!outputFolder.empty());
|
std::string outputPath;
|
||||||
|
app.add_option(
|
||||||
|
"-o,--output",
|
||||||
|
outputPath,
|
||||||
|
"Where to generate the output, without suffix.");
|
||||||
|
|
||||||
const std::string binaryPath = outputFolder + extBufferFilename;
|
app.add_flag(
|
||||||
FILE *fp = fopen(binaryPath.c_str(), "wb");
|
"-e,--embed",
|
||||||
if (fp == nullptr) {
|
gltfOptions.embedResources,
|
||||||
fmt::fprintf(stderr, "ERROR:: Couldn't open file '%s' for writing.\n", binaryPath);
|
"Inline buffers as data:// URIs within generated non-binary glTF.");
|
||||||
return 1;
|
app.add_flag(
|
||||||
}
|
"-b,--binary",
|
||||||
|
gltfOptions.outputBinary,
|
||||||
|
"Output a single binary format .glb file.");
|
||||||
|
|
||||||
if (data_render_model->binary->empty() == false)
|
app.add_option(
|
||||||
{
|
"--long-indices",
|
||||||
const unsigned char *binaryData = &(*data_render_model->binary)[0];
|
[&](std::vector<std::string> choices) -> bool {
|
||||||
unsigned long binarySize = data_render_model->binary->size();
|
for (const std::string choice : choices) {
|
||||||
if (fwrite(binaryData, binarySize, 1, fp) != 1) {
|
if (choice == "never") {
|
||||||
fmt::fprintf(stderr, "ERROR: Failed to write %lu bytes to file '%s'.\n", binarySize, binaryPath);
|
gltfOptions.useLongIndices = UseLongIndicesOptions::NEVER;
|
||||||
fclose(fp);
|
} else if (choice == "auto") {
|
||||||
return 1;
|
gltfOptions.useLongIndices = UseLongIndicesOptions::AUTO;
|
||||||
|
} else if (choice == "always") {
|
||||||
|
gltfOptions.useLongIndices = UseLongIndicesOptions::ALWAYS;
|
||||||
|
} else {
|
||||||
|
fmt::printf("Unknown --long-indices: %s\n", choice);
|
||||||
|
throw CLI::RuntimeError(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
"Whether to use 32-bit indices.")
|
||||||
|
->type_name("(never|auto|always)");
|
||||||
|
|
||||||
|
app.add_option(
|
||||||
|
"--compute-normals",
|
||||||
|
[&](std::vector<std::string> choices) -> bool {
|
||||||
|
for (const std::string choice : choices) {
|
||||||
|
if (choice == "never") {
|
||||||
|
gltfOptions.computeNormals = ComputeNormalsOption::NEVER;
|
||||||
|
} else if (choice == "broken") {
|
||||||
|
gltfOptions.computeNormals = ComputeNormalsOption::BROKEN;
|
||||||
|
} else if (choice == "missing") {
|
||||||
|
gltfOptions.computeNormals = ComputeNormalsOption::MISSING;
|
||||||
|
} else if (choice == "always") {
|
||||||
|
gltfOptions.computeNormals = ComputeNormalsOption::ALWAYS;
|
||||||
|
} else {
|
||||||
|
fmt::printf("Unknown --compute-normals option: %s\n", choice);
|
||||||
|
throw CLI::RuntimeError(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
"When to compute vertex normals from mesh geometry.")
|
||||||
|
->type_name("(never|broken|missing|always)");
|
||||||
|
|
||||||
|
std::vector<std::function<Vec2f(Vec2f)>> texturesTransforms;
|
||||||
|
app.add_flag_function(
|
||||||
|
"--flip-u",
|
||||||
|
[&](size_t count) {
|
||||||
|
if (count > 0) {
|
||||||
|
texturesTransforms.emplace_back(
|
||||||
|
[](Vec2f uv) { return Vec2f(1.0f - uv[0], uv[1]); });
|
||||||
|
if (verboseOutput) {
|
||||||
|
fmt::printf("Flipping texture coordinates in the 'U' dimension.\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fclose(fp);
|
},
|
||||||
fmt::printf("Wrote %lu bytes of binary data to %s.\n", binarySize, binaryPath);
|
"Flip all U texture coordinates.");
|
||||||
}
|
|
||||||
|
|
||||||
|
app.add_flag("--no-flip-u", "Don't flip U texture coordinates.")
|
||||||
|
->excludes("--flip-u");
|
||||||
|
|
||||||
|
app.add_flag_function(
|
||||||
|
"--no-flip-v",
|
||||||
|
[&](size_t count) {
|
||||||
|
if (count > 0) {
|
||||||
|
texturesTransforms.emplace_back(
|
||||||
|
[](Vec2f uv) { return Vec2f(uv[0], 1.0f - uv[1]); });
|
||||||
|
if (verboseOutput) {
|
||||||
|
fmt::printf("NOT flipping texture coordinates in the 'V' dimension.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Flip all V texture coordinates.");
|
||||||
|
app.add_flag("--flip-v", "Don't flip U texture coordinates.")
|
||||||
|
->excludes("--no-flip-v");
|
||||||
|
|
||||||
|
app.add_flag(
|
||||||
|
"--pbr-metallic-rougnness",
|
||||||
|
gltfOptions.usePBRMetRough,
|
||||||
|
"Try to glean glTF 2.0 native PBR attributes from the FBX.")
|
||||||
|
->group("Materials");
|
||||||
|
|
||||||
|
app.add_flag(
|
||||||
|
"--khr-materials-unlit",
|
||||||
|
gltfOptions.useKHRMatUnlit,
|
||||||
|
"Use KHR_materials_unlit extension to request an unlit shader.")
|
||||||
|
->group("Materials");
|
||||||
|
|
||||||
|
app.add_flag(
|
||||||
|
"--khr-lights-punctual",
|
||||||
|
gltfOptions.useKHRLightsPunctual,
|
||||||
|
"Use KHR_lights_punctual extension to request an unlit shader.");
|
||||||
|
|
||||||
|
app.add_flag(
|
||||||
|
"--user-properties",
|
||||||
|
gltfOptions.enableUserProperties,
|
||||||
|
"Transcribe FBX User Properties into glTF node and material 'extras'.");
|
||||||
|
|
||||||
|
app.add_flag(
|
||||||
|
"--blend-shape-normals",
|
||||||
|
gltfOptions.useBlendShapeNormals,
|
||||||
|
"Include blend shape normals, if reported present by the FBX SDK.");
|
||||||
|
|
||||||
|
app.add_flag(
|
||||||
|
"--blend-shape-tangents",
|
||||||
|
gltfOptions.useBlendShapeTangents,
|
||||||
|
"Include blend shape tangents, if reported present by the FBX SDK.");
|
||||||
|
|
||||||
|
app.add_option(
|
||||||
|
"-k,--keep-attribute",
|
||||||
|
[&](std::vector<std::string> attributes) -> bool {
|
||||||
|
gltfOptions.keepAttribs = RAW_VERTEX_ATTRIBUTE_JOINT_INDICES |
|
||||||
|
RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS;
|
||||||
|
for (std::string attribute : attributes) {
|
||||||
|
if (attribute == "position") {
|
||||||
|
gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_POSITION;
|
||||||
|
} else if (attribute == "normal") {
|
||||||
|
gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_NORMAL;
|
||||||
|
} else if (attribute == "tangent") {
|
||||||
|
gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_TANGENT;
|
||||||
|
} else if (attribute == "binormal") {
|
||||||
|
gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_BINORMAL;
|
||||||
|
} else if (attribute == "color") {
|
||||||
|
gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_COLOR;
|
||||||
|
} else if (attribute == "uv0") {
|
||||||
|
gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_UV0;
|
||||||
|
} else if (attribute == "uv1") {
|
||||||
|
gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_UV1;
|
||||||
|
} else if (attribute == "auto") {
|
||||||
|
gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_AUTO;
|
||||||
|
} else {
|
||||||
|
fmt::printf("Unknown --keep-attribute option: %s\n", attribute);
|
||||||
|
throw CLI::RuntimeError(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
"Used repeatedly to build a limiting set of vertex attributes to keep.")
|
||||||
|
->type_size(-1)
|
||||||
|
->type_name("(position|normal|tangent|binormial|color|uv0|uv1|auto)");
|
||||||
|
|
||||||
|
app.add_flag(
|
||||||
|
"-d,--draco",
|
||||||
|
gltfOptions.draco.enabled,
|
||||||
|
"Apply Draco mesh compression to geometries.")
|
||||||
|
->group("Draco");
|
||||||
|
|
||||||
|
app.add_option(
|
||||||
|
"--draco-compression-level",
|
||||||
|
gltfOptions.draco.compressionLevel,
|
||||||
|
"The compression level to tune Draco to.",
|
||||||
|
true)
|
||||||
|
->check(CLI::Range(0, 10))
|
||||||
|
->group("Draco");
|
||||||
|
|
||||||
|
app.add_option(
|
||||||
|
"--draco-bits-for-position",
|
||||||
|
gltfOptions.draco.quantBitsPosition,
|
||||||
|
"How many bits to quantize position to.",
|
||||||
|
true)
|
||||||
|
->check(CLI::Range(1, 32))
|
||||||
|
->group("Draco");
|
||||||
|
|
||||||
|
app.add_option(
|
||||||
|
"--draco-bits-for-uv",
|
||||||
|
gltfOptions.draco.quantBitsTexCoord,
|
||||||
|
"How many bits to quantize UV coordinates to.",
|
||||||
|
true)
|
||||||
|
->check(CLI::Range(1, 32))
|
||||||
|
->group("Draco");
|
||||||
|
|
||||||
|
app.add_option(
|
||||||
|
"--draco-bits-for-normals",
|
||||||
|
gltfOptions.draco.quantBitsNormal,
|
||||||
|
"How many bits to quantize nornals to.",
|
||||||
|
true)
|
||||||
|
->check(CLI::Range(1, 32))
|
||||||
|
->group("Draco");
|
||||||
|
|
||||||
|
app.add_option(
|
||||||
|
"--draco-bits-for-colors",
|
||||||
|
gltfOptions.draco.quantBitsColor,
|
||||||
|
"How many bits to quantize colors to.",
|
||||||
|
true)
|
||||||
|
->check(CLI::Range(1, 32))
|
||||||
|
->group("Draco");
|
||||||
|
|
||||||
|
app.add_option(
|
||||||
|
"--draco-bits-for-other",
|
||||||
|
gltfOptions.draco.quantBitsGeneric,
|
||||||
|
"How many bits to quantize all other vertex attributes to.",
|
||||||
|
true)
|
||||||
|
->check(CLI::Range(1, 32))
|
||||||
|
->group("Draco");
|
||||||
|
|
||||||
|
CLI11_PARSE(app, argc, argv);
|
||||||
|
|
||||||
|
if (inputPath.empty()) {
|
||||||
|
fmt::printf("You must supply a FBX file to convert.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gltfOptions.useKHRMatUnlit && !gltfOptions.usePBRMetRough) {
|
||||||
|
if (verboseOutput) {
|
||||||
|
fmt::printf("Defaulting to --pbr-metallic-roughness material support.\n");
|
||||||
|
}
|
||||||
|
gltfOptions.usePBRMetRough = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gltfOptions.embedResources && gltfOptions.outputBinary) {
|
||||||
|
fmt::printf("Note: Ignoring --embed; it's meaningless with --binary.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputPath.empty()) {
|
||||||
|
// if -o is not given, default to the basename of the .fbx
|
||||||
|
outputPath = fmt::format(
|
||||||
|
".{}{}",
|
||||||
|
(const char)StringUtils::GetPathSeparator(),
|
||||||
|
StringUtils::GetFileBaseString(inputPath));
|
||||||
|
}
|
||||||
|
// the output folder in .gltf mode, not used for .glb
|
||||||
|
std::string outputFolder;
|
||||||
|
|
||||||
|
// the path of the actual .glb or .gltf file
|
||||||
|
std::string modelPath;
|
||||||
|
if (gltfOptions.outputBinary) {
|
||||||
|
// in binary mode, we write precisely where we're asked
|
||||||
|
modelPath = outputPath + ".glb";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// in gltf mode, we create a folder and write into that
|
||||||
|
outputFolder = fmt::format(
|
||||||
|
"{}_out{}",
|
||||||
|
outputPath.c_str(),
|
||||||
|
(const char)StringUtils::GetPathSeparator());
|
||||||
|
modelPath =
|
||||||
|
outputFolder + StringUtils::GetFileNameString(outputPath) + ".gltf";
|
||||||
|
}
|
||||||
|
if (!FileUtils::CreatePath(modelPath.c_str())) {
|
||||||
|
fmt::fprintf(
|
||||||
|
stderr, "ERROR: Failed to create folder: %s'\n", outputFolder.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelData* data_render_model = nullptr;
|
||||||
|
RawModel raw;
|
||||||
|
|
||||||
|
if (verboseOutput) {
|
||||||
|
fmt::printf("Loading FBX File: %s\n", inputPath);
|
||||||
|
}
|
||||||
|
if (!LoadFBXFile(raw, inputPath.c_str(), "png;jpg;jpeg")) {
|
||||||
|
fmt::fprintf(stderr, "ERROR:: Failed to parse FBX: %s\n", inputPath);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!texturesTransforms.empty()) {
|
||||||
|
raw.TransformTextures(texturesTransforms);
|
||||||
|
}
|
||||||
|
raw.Condense();
|
||||||
|
raw.TransformGeometry(gltfOptions.computeNormals);
|
||||||
|
|
||||||
|
std::ofstream outStream; // note: auto-flushes in destructor
|
||||||
|
const auto streamStart = outStream.tellp();
|
||||||
|
|
||||||
|
outStream.open(
|
||||||
|
modelPath,
|
||||||
|
std::ios::trunc | std::ios::ate | std::ios::out | std::ios::binary);
|
||||||
|
if (outStream.fail()) {
|
||||||
|
fmt::fprintf(
|
||||||
|
stderr,
|
||||||
|
"ERROR:: Couldn't open file for writing: %s\n",
|
||||||
|
modelPath.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
data_render_model = Raw2Gltf(outStream, outputFolder, raw, gltfOptions);
|
||||||
|
|
||||||
|
if (gltfOptions.outputBinary) {
|
||||||
|
fmt::printf(
|
||||||
|
"Wrote %lu bytes of binary glTF to %s.\n",
|
||||||
|
(unsigned long)(outStream.tellp() - streamStart),
|
||||||
|
modelPath);
|
||||||
delete data_render_model;
|
delete data_render_model;
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::printf(
|
||||||
|
"Wrote %lu bytes of glTF to %s.\n",
|
||||||
|
(unsigned long)(outStream.tellp() - streamStart),
|
||||||
|
modelPath);
|
||||||
|
|
||||||
|
if (gltfOptions.embedResources) {
|
||||||
|
// we're done: everything was inlined into the glTF JSON
|
||||||
|
delete data_render_model;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!outputFolder.empty());
|
||||||
|
|
||||||
|
const std::string binaryPath = outputFolder + extBufferFilename;
|
||||||
|
FILE* fp = fopen(binaryPath.c_str(), "wb");
|
||||||
|
if (fp == nullptr) {
|
||||||
|
fmt::fprintf(
|
||||||
|
stderr, "ERROR:: Couldn't open file '%s' for writing.\n", binaryPath);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_render_model->binary->empty() == false) {
|
||||||
|
const unsigned char* binaryData = &(*data_render_model->binary)[0];
|
||||||
|
unsigned long binarySize = data_render_model->binary->size();
|
||||||
|
if (fwrite(binaryData, binarySize, 1, fp) != 1) {
|
||||||
|
fmt::fprintf(
|
||||||
|
stderr,
|
||||||
|
"ERROR: Failed to write %lu bytes to file '%s'.\n",
|
||||||
|
binarySize,
|
||||||
|
binaryPath);
|
||||||
|
fclose(fp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
fmt::printf(
|
||||||
|
"Wrote %lu bytes of binary data to %s.\n", binarySize, binaryPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete data_render_model;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,12 +76,12 @@ struct GltfOptions
|
||||||
/** Whether and how to use KHR_draco_mesh_compression to minimize static geometry size. */
|
/** Whether and how to use KHR_draco_mesh_compression to minimize static geometry size. */
|
||||||
struct {
|
struct {
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
int compressionLevel = -1;
|
int compressionLevel = 7;
|
||||||
int quantBitsPosition = -1;
|
int quantBitsPosition = 14;
|
||||||
int quantBitsTexCoord = -1;
|
int quantBitsTexCoord = 10;
|
||||||
int quantBitsNormal = -1;
|
int quantBitsNormal = 10;
|
||||||
int quantBitsColor = -1;
|
int quantBitsColor = 8;
|
||||||
int quantBitsGeneric = -1;
|
int quantBitsGeneric = 8;
|
||||||
} draco;
|
} draco;
|
||||||
|
|
||||||
/** Whether to include FBX User Properties as 'extras' metadata in glTF nodes. */
|
/** Whether to include FBX User Properties as 'extras' metadata in glTF nodes. */
|
||||||
|
@ -91,6 +91,10 @@ struct GltfOptions
|
||||||
bool useKHRMatUnlit { false };
|
bool useKHRMatUnlit { false };
|
||||||
/** Whether to populate the pbrMetallicRoughness substruct in materials. */
|
/** Whether to populate the pbrMetallicRoughness substruct in materials. */
|
||||||
bool usePBRMetRough { false };
|
bool usePBRMetRough { false };
|
||||||
|
|
||||||
|
/** Whether to include lights through the KHR_punctual_lights extension. */
|
||||||
|
bool useKHRLightsPunctual { true };
|
||||||
|
|
||||||
/** Whether to include blend shape normals, if present according to the SDK. */
|
/** Whether to include blend shape normals, if present according to the SDK. */
|
||||||
bool useBlendShapeNormals { false };
|
bool useBlendShapeNormals { false };
|
||||||
/** Whether to include blend shape tangents, if present according to the SDK. */
|
/** Whether to include blend shape tangents, if present according to the SDK. */
|
||||||
|
|
|
@ -57,10 +57,10 @@ GetMaterialType(const RawModel &raw, const int textures[RAW_TEXTURE_USAGE_MAX],
|
||||||
: (skinned ? RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT : RAW_MATERIAL_TYPE_TRANSPARENT);
|
: (skinned ? RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT : RAW_MATERIAL_TYPE_TRANSPARENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// else if there is any vertex transparency, treat whole mesh as transparent
|
// else if there is any vertex transparency, treat whole mesh as transparent
|
||||||
if (vertexTransparency) {
|
if (vertexTransparency) {
|
||||||
return skinned ? RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT : RAW_MATERIAL_TYPE_TRANSPARENT;
|
return skinned ? RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT : RAW_MATERIAL_TYPE_TRANSPARENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Default to simply opaque.
|
// Default to simply opaque.
|
||||||
|
@ -173,6 +173,7 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
|
||||||
for (int polygonIndex = 0; polygonIndex < pMesh->GetPolygonCount(); polygonIndex++) {
|
for (int polygonIndex = 0; polygonIndex < pMesh->GetPolygonCount(); polygonIndex++) {
|
||||||
FBX_ASSERT(pMesh->GetPolygonSize(polygonIndex) == 3);
|
FBX_ASSERT(pMesh->GetPolygonSize(polygonIndex) == 3);
|
||||||
const std::shared_ptr<FbxMaterialInfo> fbxMaterial = materials.GetMaterial(polygonIndex);
|
const std::shared_ptr<FbxMaterialInfo> fbxMaterial = materials.GetMaterial(polygonIndex);
|
||||||
|
const std::vector<std::string> userProperties = materials.GetUserProperties(polygonIndex);
|
||||||
|
|
||||||
int textures[RAW_TEXTURE_USAGE_MAX];
|
int textures[RAW_TEXTURE_USAGE_MAX];
|
||||||
std::fill_n(textures, (int) RAW_TEXTURE_USAGE_MAX, -1);
|
std::fill_n(textures, (int) RAW_TEXTURE_USAGE_MAX, -1);
|
||||||
|
@ -361,7 +362,7 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
|
||||||
}
|
}
|
||||||
|
|
||||||
const RawMaterialType materialType = GetMaterialType(raw, textures, vertexTransparency, skinning.IsSkinned());
|
const RawMaterialType materialType = GetMaterialType(raw, textures, vertexTransparency, skinning.IsSkinned());
|
||||||
const int rawMaterialIndex = raw.AddMaterial(materialName, materialType, textures, rawMatProps);
|
const int rawMaterialIndex = raw.AddMaterial(materialName, materialType, textures, rawMatProps, userProperties);
|
||||||
|
|
||||||
raw.AddTriangle(rawVertexIndices[0], rawVertexIndices[1], rawVertexIndices[2], rawMaterialIndex, rawSurfaceIndex);
|
raw.AddTriangle(rawVertexIndices[0], rawVertexIndices[1], rawVertexIndices[2], rawMaterialIndex, rawSurfaceIndex);
|
||||||
}
|
}
|
||||||
|
@ -379,6 +380,40 @@ double VFOV2HFOV(double v, double ar)
|
||||||
return 2.0 * std::atan((ar) * std::tan((v * FBXSDK_PI_DIV_180) * 0.5)) * FBXSDK_180_DIV_PI;
|
return 2.0 * std::atan((ar) * std::tan((v * FBXSDK_PI_DIV_180) * 0.5)) * FBXSDK_180_DIV_PI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ReadLight(RawModel &raw, FbxScene *pScene, FbxNode *pNode) {
|
||||||
|
const FbxLight *pLight = pNode->GetLight();
|
||||||
|
|
||||||
|
int lightIx;
|
||||||
|
float intensity = (float)pLight->Intensity.Get();
|
||||||
|
Vec3f color = toVec3f(pLight->Color.Get());
|
||||||
|
switch (pLight->LightType.Get()) {
|
||||||
|
case FbxLight::eDirectional: {
|
||||||
|
lightIx = raw.AddLight(pLight->GetName(), RAW_LIGHT_TYPE_DIRECTIONAL,
|
||||||
|
color, intensity, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FbxLight::ePoint: {
|
||||||
|
lightIx = raw.AddLight(pLight->GetName(), RAW_LIGHT_TYPE_POINT, color,
|
||||||
|
intensity, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FbxLight::eSpot: {
|
||||||
|
lightIx = raw.AddLight(pLight->GetName(), RAW_LIGHT_TYPE_SPOT, color,
|
||||||
|
intensity, (float)pLight->InnerAngle.Get(),
|
||||||
|
(float)pLight->OuterAngle.Get());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
fmt::printf("Warning:: Ignoring unsupported light type.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int nodeId = raw.GetNodeById(pNode->GetUniqueID());
|
||||||
|
RawNode &node = raw.GetNode(nodeId);
|
||||||
|
node.lightIx = lightIx;
|
||||||
|
}
|
||||||
|
|
||||||
// Largely adopted from fbx example
|
// Largely adopted from fbx example
|
||||||
static void ReadCamera(RawModel &raw, FbxScene *pScene, FbxNode *pNode)
|
static void ReadCamera(RawModel &raw, FbxScene *pScene, FbxNode *pNode)
|
||||||
{
|
{
|
||||||
|
@ -444,91 +479,11 @@ static void ReadCamera(RawModel &raw, FbxScene *pScene, FbxNode *pNode)
|
||||||
|
|
||||||
static void ReadNodeProperty(RawModel &raw, FbxNode *pNode, FbxProperty &prop)
|
static void ReadNodeProperty(RawModel &raw, FbxNode *pNode, FbxProperty &prop)
|
||||||
{
|
{
|
||||||
using fbxsdk::EFbxType;
|
|
||||||
int nodeId = raw.GetNodeById(pNode->GetUniqueID());
|
int nodeId = raw.GetNodeById(pNode->GetUniqueID());
|
||||||
if (nodeId < 0)
|
if (nodeId >= 0) {
|
||||||
return;
|
RawNode &node = raw.GetNode(nodeId);
|
||||||
|
node.userProperties.push_back(TranscribeProperty(prop).dump());
|
||||||
std::string ename;
|
|
||||||
// Convert property type
|
|
||||||
switch (prop.GetPropertyDataType().GetType()) {
|
|
||||||
case eFbxBool: ename = "eFbxBool"; break;
|
|
||||||
case eFbxChar: ename = "eFbxChar"; break;
|
|
||||||
case eFbxUChar: ename = "eFbxUChar"; break;
|
|
||||||
case eFbxShort: ename = "eFbxShort"; break;
|
|
||||||
case eFbxUShort: ename = "eFbxUShort"; break;
|
|
||||||
case eFbxInt: ename = "eFbxInt"; break;
|
|
||||||
case eFbxUInt: ename = "eFbxUint"; break;
|
|
||||||
case eFbxLongLong: ename = "eFbxLongLong"; break;
|
|
||||||
case eFbxULongLong: ename = "eFbxULongLong"; break;
|
|
||||||
case eFbxFloat: ename = "eFbxFloat"; break;
|
|
||||||
case eFbxHalfFloat: ename = "eFbxHalfFloat"; break;
|
|
||||||
case eFbxDouble: ename = "eFbxDouble"; break;
|
|
||||||
case eFbxDouble2: ename = "eFbxDouble2"; break;
|
|
||||||
case eFbxDouble3: ename = "eFbxDouble3"; break;
|
|
||||||
case eFbxDouble4: ename = "eFbxDouble4"; break;
|
|
||||||
case eFbxString: ename = "eFbxString"; break;
|
|
||||||
|
|
||||||
// Use this as fallback because it does not give very descriptive names
|
|
||||||
default: ename = prop.GetPropertyDataType().GetName(); break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
json p;
|
|
||||||
p["type"] = ename;
|
|
||||||
|
|
||||||
// Convert property value
|
|
||||||
switch (prop.GetPropertyDataType().GetType()) {
|
|
||||||
case eFbxBool:
|
|
||||||
case eFbxChar:
|
|
||||||
case eFbxUChar:
|
|
||||||
case eFbxShort:
|
|
||||||
case eFbxUShort:
|
|
||||||
case eFbxInt:
|
|
||||||
case eFbxUInt:
|
|
||||||
case eFbxLongLong: {
|
|
||||||
p["value"] = prop.EvaluateValue<long long>(FBXSDK_TIME_INFINITE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case eFbxULongLong: {
|
|
||||||
p["value"] = prop.EvaluateValue<unsigned long long>(FBXSDK_TIME_INFINITE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case eFbxFloat:
|
|
||||||
case eFbxHalfFloat:
|
|
||||||
case eFbxDouble: {
|
|
||||||
p["value"] = prop.EvaluateValue<double>(FBXSDK_TIME_INFINITE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case eFbxDouble2: {
|
|
||||||
auto v = prop.EvaluateValue<FbxDouble2>(FBXSDK_TIME_INFINITE);
|
|
||||||
p["value"] = {v[0], v[1]};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case eFbxDouble3: {
|
|
||||||
auto v = prop.EvaluateValue<FbxDouble3>(FBXSDK_TIME_INFINITE);
|
|
||||||
p["value"] = {v[0], v[1], v[2]};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case eFbxDouble4: {
|
|
||||||
auto v = prop.EvaluateValue<FbxDouble4>(FBXSDK_TIME_INFINITE);
|
|
||||||
p["value"] = {v[0], v[1], v[2], v[3]};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case eFbxString: {
|
|
||||||
p["value"] = std::string{prop.Get<FbxString>()};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
p["value"] = "UNSUPPORTED_VALUE_TYPE";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
json n;
|
|
||||||
n[prop.GetNameAsCStr()] = p;
|
|
||||||
|
|
||||||
RawNode &node = raw.GetNode(nodeId);
|
|
||||||
node.userProperties.push_back(n.dump());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReadNodeAttributes(
|
static void ReadNodeAttributes(
|
||||||
|
@ -565,13 +520,15 @@ static void ReadNodeAttributes(
|
||||||
ReadCamera(raw, pScene, pNode);
|
ReadCamera(raw, pScene, pNode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FbxNodeAttribute::eLight:
|
||||||
|
ReadLight(raw, pScene, pNode);
|
||||||
|
break;
|
||||||
case FbxNodeAttribute::eUnknown:
|
case FbxNodeAttribute::eUnknown:
|
||||||
case FbxNodeAttribute::eNull:
|
case FbxNodeAttribute::eNull:
|
||||||
case FbxNodeAttribute::eMarker:
|
case FbxNodeAttribute::eMarker:
|
||||||
case FbxNodeAttribute::eSkeleton:
|
case FbxNodeAttribute::eSkeleton:
|
||||||
case FbxNodeAttribute::eCameraStereo:
|
case FbxNodeAttribute::eCameraStereo:
|
||||||
case FbxNodeAttribute::eCameraSwitcher:
|
case FbxNodeAttribute::eCameraSwitcher:
|
||||||
case FbxNodeAttribute::eLight:
|
|
||||||
case FbxNodeAttribute::eOpticalReference:
|
case FbxNodeAttribute::eOpticalReference:
|
||||||
case FbxNodeAttribute::eOpticalMarker:
|
case FbxNodeAttribute::eOpticalMarker:
|
||||||
case FbxNodeAttribute::eNurbsCurve:
|
case FbxNodeAttribute::eNurbsCurve:
|
||||||
|
@ -985,3 +942,90 @@ bool LoadFBXFile(RawModel &raw, const char *fbxFileName, const char *textureExte
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convenience method for describing a property in JSON
|
||||||
|
json TranscribeProperty(FbxProperty &prop)
|
||||||
|
{
|
||||||
|
using fbxsdk::EFbxType;
|
||||||
|
std::string ename;
|
||||||
|
|
||||||
|
// Convert property type
|
||||||
|
switch (prop.GetPropertyDataType().GetType()) {
|
||||||
|
case eFbxBool: ename = "eFbxBool"; break;
|
||||||
|
case eFbxChar: ename = "eFbxChar"; break;
|
||||||
|
case eFbxUChar: ename = "eFbxUChar"; break;
|
||||||
|
case eFbxShort: ename = "eFbxShort"; break;
|
||||||
|
case eFbxUShort: ename = "eFbxUShort"; break;
|
||||||
|
case eFbxInt: ename = "eFbxInt"; break;
|
||||||
|
case eFbxUInt: ename = "eFbxUint"; break;
|
||||||
|
case eFbxLongLong: ename = "eFbxLongLong"; break;
|
||||||
|
case eFbxULongLong: ename = "eFbxULongLong"; break;
|
||||||
|
case eFbxFloat: ename = "eFbxFloat"; break;
|
||||||
|
case eFbxHalfFloat: ename = "eFbxHalfFloat"; break;
|
||||||
|
case eFbxDouble: ename = "eFbxDouble"; break;
|
||||||
|
case eFbxDouble2: ename = "eFbxDouble2"; break;
|
||||||
|
case eFbxDouble3: ename = "eFbxDouble3"; break;
|
||||||
|
case eFbxDouble4: ename = "eFbxDouble4"; break;
|
||||||
|
case eFbxString: ename = "eFbxString"; break;
|
||||||
|
|
||||||
|
// Use this as fallback because it does not give very descriptive names
|
||||||
|
default: ename = prop.GetPropertyDataType().GetName(); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
json p = {
|
||||||
|
{"type", ename}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert property value
|
||||||
|
switch (prop.GetPropertyDataType().GetType()) {
|
||||||
|
case eFbxBool:
|
||||||
|
case eFbxChar:
|
||||||
|
case eFbxUChar:
|
||||||
|
case eFbxShort:
|
||||||
|
case eFbxUShort:
|
||||||
|
case eFbxInt:
|
||||||
|
case eFbxUInt:
|
||||||
|
case eFbxLongLong: {
|
||||||
|
p["value"] = prop.EvaluateValue<long long>(FBXSDK_TIME_INFINITE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case eFbxULongLong: {
|
||||||
|
p["value"] = prop.EvaluateValue<unsigned long long>(FBXSDK_TIME_INFINITE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case eFbxFloat:
|
||||||
|
case eFbxHalfFloat:
|
||||||
|
case eFbxDouble: {
|
||||||
|
p["value"] = prop.EvaluateValue<double>(FBXSDK_TIME_INFINITE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case eFbxDouble2: {
|
||||||
|
auto v = prop.EvaluateValue<FbxDouble2>(FBXSDK_TIME_INFINITE);
|
||||||
|
p["value"] = {v[0], v[1]};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case eFbxDouble3: {
|
||||||
|
auto v = prop.EvaluateValue<FbxDouble3>(FBXSDK_TIME_INFINITE);
|
||||||
|
p["value"] = {v[0], v[1], v[2]};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case eFbxDouble4: {
|
||||||
|
auto v = prop.EvaluateValue<FbxDouble4>(FBXSDK_TIME_INFINITE);
|
||||||
|
p["value"] = {v[0], v[1], v[2], v[3]};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case eFbxString: {
|
||||||
|
p["value"] = std::string{prop.Get<FbxString>()};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
p["value"] = "UNSUPPORTED_VALUE_TYPE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
{prop.GetNameAsCStr(), p}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,3 +12,5 @@
|
||||||
#include "raw/RawModel.hpp"
|
#include "raw/RawModel.hpp"
|
||||||
|
|
||||||
bool LoadFBXFile(RawModel &raw, const char *fbxFileName, const char *textureExtensions);
|
bool LoadFBXFile(RawModel &raw, const char *fbxFileName, const char *textureExtensions);
|
||||||
|
|
||||||
|
json TranscribeProperty(FbxProperty &prop);
|
|
@ -8,6 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "FbxMaterialsAccess.hpp"
|
#include "FbxMaterialsAccess.hpp"
|
||||||
|
#include "Fbx2Raw.hpp"
|
||||||
|
|
||||||
FbxMaterialsAccess::FbxMaterialsAccess(const FbxMesh *pMesh, const std::map<const FbxTexture *, FbxString> &textureLocations) :
|
FbxMaterialsAccess::FbxMaterialsAccess(const FbxMesh *pMesh, const std::map<const FbxTexture *, FbxString> &textureLocations) :
|
||||||
mappingMode(FbxGeometryElement::eNone),
|
mappingMode(FbxGeometryElement::eNone),
|
||||||
|
@ -37,15 +38,32 @@ FbxMaterialsAccess::FbxMaterialsAccess(const FbxMesh *pMesh, const std::map<cons
|
||||||
if (materialNum < 0) {
|
if (materialNum < 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FbxSurfaceMaterial* surfaceMaterial = mesh->GetNode()->GetSrcObject<FbxSurfaceMaterial>(materialNum);
|
||||||
|
|
||||||
if (materialNum >= summaries.size()) {
|
if (materialNum >= summaries.size()) {
|
||||||
summaries.resize(materialNum + 1);
|
summaries.resize(materialNum + 1);
|
||||||
}
|
}
|
||||||
auto summary = summaries[materialNum];
|
auto summary = summaries[materialNum];
|
||||||
if (summary == nullptr) {
|
if (summary == nullptr) {
|
||||||
summary = summaries[materialNum] = GetMaterialInfo(
|
summary = summaries[materialNum] = GetMaterialInfo(
|
||||||
mesh->GetNode()->GetSrcObject<FbxSurfaceMaterial>(materialNum),
|
surfaceMaterial,
|
||||||
textureLocations);
|
textureLocations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (materialNum >= userProperties.size()) {
|
||||||
|
userProperties.resize(materialNum + 1);
|
||||||
|
}
|
||||||
|
if (userProperties[materialNum].empty()) {
|
||||||
|
FbxProperty objectProperty = surfaceMaterial->GetFirstProperty();
|
||||||
|
while (objectProperty.IsValid())
|
||||||
|
{
|
||||||
|
if (objectProperty.GetFlag(FbxPropertyFlags::eUserDefined)) {
|
||||||
|
userProperties[materialNum].push_back(TranscribeProperty(objectProperty).dump());
|
||||||
|
}
|
||||||
|
objectProperty = surfaceMaterial->GetNextProperty(objectProperty);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +79,18 @@ const std::shared_ptr<FbxMaterialInfo> FbxMaterialsAccess::GetMaterial(const int
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> FbxMaterialsAccess::GetUserProperties(const int polygonIndex) const
|
||||||
|
{
|
||||||
|
if (mappingMode != FbxGeometryElement::eNone) {
|
||||||
|
const int materialNum = indices->GetAt((mappingMode == FbxGeometryElement::eByPolygon) ? polygonIndex : 0);
|
||||||
|
if (materialNum < 0) {
|
||||||
|
return std::vector<std::string>();
|
||||||
|
}
|
||||||
|
return userProperties.at((unsigned long)materialNum);
|
||||||
|
}
|
||||||
|
return std::vector<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<FbxMaterialInfo>
|
std::unique_ptr<FbxMaterialInfo>
|
||||||
FbxMaterialsAccess::GetMaterialInfo(FbxSurfaceMaterial *material, const std::map<const FbxTexture *, FbxString> &textureLocations)
|
FbxMaterialsAccess::GetMaterialInfo(FbxSurfaceMaterial *material, const std::map<const FbxTexture *, FbxString> &textureLocations)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Fbx2Raw.hpp"
|
||||||
#include "FbxMaterialInfo.hpp"
|
#include "FbxMaterialInfo.hpp"
|
||||||
#include "FbxTraditionalMaterialInfo.hpp"
|
#include "FbxTraditionalMaterialInfo.hpp"
|
||||||
#include "FbxRoughMetMaterialInfo.hpp"
|
#include "FbxRoughMetMaterialInfo.hpp"
|
||||||
|
@ -21,12 +22,15 @@ public:
|
||||||
|
|
||||||
const std::shared_ptr<FbxMaterialInfo> GetMaterial(const int polygonIndex) const;
|
const std::shared_ptr<FbxMaterialInfo> GetMaterial(const int polygonIndex) const;
|
||||||
|
|
||||||
|
const std::vector<std::string> GetUserProperties(const int polygonIndex) const;
|
||||||
|
|
||||||
std::unique_ptr<FbxMaterialInfo>
|
std::unique_ptr<FbxMaterialInfo>
|
||||||
GetMaterialInfo(FbxSurfaceMaterial *material, const std::map<const FbxTexture *, FbxString> &textureLocations);
|
GetMaterialInfo(FbxSurfaceMaterial *material, const std::map<const FbxTexture *, FbxString> &textureLocations);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FbxGeometryElement::EMappingMode mappingMode;
|
FbxGeometryElement::EMappingMode mappingMode;
|
||||||
std::vector<std::shared_ptr<FbxMaterialInfo>> summaries {};
|
std::vector<std::shared_ptr<FbxMaterialInfo>> summaries {};
|
||||||
|
std::vector<std::vector<std::string>> userProperties;
|
||||||
const FbxMesh *mesh;
|
const FbxMesh *mesh;
|
||||||
const FbxLayerElementArrayTemplate<int> *indices;
|
const FbxLayerElementArrayTemplate<int> *indices;
|
||||||
};
|
};
|
||||||
|
|
|
@ -77,4 +77,9 @@ void GltfModel::serializeHolders(json &glTFJson)
|
||||||
serializeHolder(glTFJson, "animations", animations);
|
serializeHolder(glTFJson, "animations", animations);
|
||||||
serializeHolder(glTFJson, "cameras", cameras);
|
serializeHolder(glTFJson, "cameras", cameras);
|
||||||
serializeHolder(glTFJson, "nodes", nodes);
|
serializeHolder(glTFJson, "nodes", nodes);
|
||||||
|
if (!lights.ptrs.empty()) {
|
||||||
|
json lightsJson = json::object();
|
||||||
|
serializeHolder(lightsJson, "lights", lights);
|
||||||
|
glTFJson["extensions"][KHR_LIGHTS_PUNCTUAL] = lightsJson;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "gltf/properties/BufferViewData.hpp"
|
#include "gltf/properties/BufferViewData.hpp"
|
||||||
#include "gltf/properties/CameraData.hpp"
|
#include "gltf/properties/CameraData.hpp"
|
||||||
#include "gltf/properties/ImageData.hpp"
|
#include "gltf/properties/ImageData.hpp"
|
||||||
|
#include "gltf/properties/LightData.hpp"
|
||||||
#include "gltf/properties/MaterialData.hpp"
|
#include "gltf/properties/MaterialData.hpp"
|
||||||
#include "gltf/properties/MeshData.hpp"
|
#include "gltf/properties/MeshData.hpp"
|
||||||
#include "gltf/properties/NodeData.hpp"
|
#include "gltf/properties/NodeData.hpp"
|
||||||
|
@ -110,7 +111,7 @@ public:
|
||||||
accessor->count = attribArr.size();
|
accessor->count = attribArr.size();
|
||||||
} else {
|
} else {
|
||||||
auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER);
|
auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER);
|
||||||
accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string(""));
|
accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string(""));
|
||||||
}
|
}
|
||||||
primitive.AddAttrib(attrDef.gltfName, *accessor);
|
primitive.AddAttrib(attrDef.gltfName, *accessor);
|
||||||
return accessor;
|
return accessor;
|
||||||
|
@ -150,6 +151,7 @@ public:
|
||||||
Holder<CameraData> cameras;
|
Holder<CameraData> cameras;
|
||||||
Holder<NodeData> nodes;
|
Holder<NodeData> nodes;
|
||||||
Holder<SceneData> scenes;
|
Holder<SceneData> scenes;
|
||||||
|
Holder<LightData> lights;
|
||||||
|
|
||||||
std::shared_ptr<SamplerData> defaultSampler;
|
std::shared_ptr<SamplerData> defaultSampler;
|
||||||
std::shared_ptr<BufferData> defaultBuffer;
|
std::shared_ptr<BufferData> defaultBuffer;
|
||||||
|
|
|
@ -119,6 +119,8 @@ ModelData *Raw2Gltf(
|
||||||
fmt::printf("%7d nodes\n", raw.GetNodeCount());
|
fmt::printf("%7d nodes\n", raw.GetNodeCount());
|
||||||
fmt::printf("%7d surfaces\n", (int) materialModels.size());
|
fmt::printf("%7d surfaces\n", (int) materialModels.size());
|
||||||
fmt::printf("%7d animations\n", raw.GetAnimationCount());
|
fmt::printf("%7d animations\n", raw.GetAnimationCount());
|
||||||
|
fmt::printf("%7d cameras\n", raw.GetCameraCount());
|
||||||
|
fmt::printf("%7d lights\n", raw.GetLightCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GltfModel> gltf(new GltfModel(options));
|
std::unique_ptr<GltfModel> gltf(new GltfModel(options));
|
||||||
|
@ -362,6 +364,10 @@ ModelData *Raw2Gltf(
|
||||||
normalTexture, occlusionTexture, emissiveTexture,
|
normalTexture, occlusionTexture, emissiveTexture,
|
||||||
emissiveFactor * emissiveIntensity, khrCmnUnlitMat, pbrMetRough));
|
emissiveFactor * emissiveIntensity, khrCmnUnlitMat, pbrMetRough));
|
||||||
materialsByName[materialHash(material)] = mData;
|
materialsByName[materialHash(material)] = mData;
|
||||||
|
|
||||||
|
if (options.enableUserProperties) {
|
||||||
|
mData->userProperties = material.userProperties;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &surfaceModel : materialModels) {
|
for (const auto &surfaceModel : materialModels) {
|
||||||
|
@ -620,6 +626,47 @@ ModelData *Raw2Gltf(
|
||||||
}
|
}
|
||||||
iter->second->SetCamera(camera.ix);
|
iter->second->SetCamera(camera.ix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// lights
|
||||||
|
//
|
||||||
|
std::vector<json> khrPunctualLights;
|
||||||
|
if (options.useKHRLightsPunctual) {
|
||||||
|
for (int i = 0; i < raw.GetLightCount(); i ++) {
|
||||||
|
const RawLight &light = raw.GetLight(i);
|
||||||
|
LightData::Type type;
|
||||||
|
switch(light.type) {
|
||||||
|
case RAW_LIGHT_TYPE_DIRECTIONAL:
|
||||||
|
type = LightData::Type::Directional;
|
||||||
|
break;
|
||||||
|
case RAW_LIGHT_TYPE_POINT:
|
||||||
|
type = LightData::Type::Point;
|
||||||
|
break;
|
||||||
|
case RAW_LIGHT_TYPE_SPOT:
|
||||||
|
type = LightData::Type::Spot;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
gltf->lights.hold(new LightData(
|
||||||
|
light.name,
|
||||||
|
type,
|
||||||
|
light.color,
|
||||||
|
// FBX intensity defaults to 100, so let's call that 1.0;
|
||||||
|
// but caveat: I find nothing in the documentation to suggest
|
||||||
|
// what unit the FBX value is meant to be measured in...
|
||||||
|
light.intensity / 100,
|
||||||
|
light.innerConeAngle,
|
||||||
|
light.outerConeAngle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < raw.GetNodeCount(); i++) {
|
||||||
|
const RawNode &node = raw.GetNode(i);
|
||||||
|
const auto nodeData = gltf->nodes.ptrs[i];
|
||||||
|
|
||||||
|
if (node.lightIx >= 0) {
|
||||||
|
// we lean on the fact that in this simple case, raw and gltf indexing are aligned
|
||||||
|
nodeData->SetLight(node.lightIx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeData &rootNode = require(nodesById, raw.GetRootNode());
|
NodeData &rootNode = require(nodesById, raw.GetRootNode());
|
||||||
|
@ -647,6 +694,9 @@ ModelData *Raw2Gltf(
|
||||||
if (options.useKHRMatUnlit) {
|
if (options.useKHRMatUnlit) {
|
||||||
extensionsUsed.push_back(KHR_MATERIALS_CMN_UNLIT);
|
extensionsUsed.push_back(KHR_MATERIALS_CMN_UNLIT);
|
||||||
}
|
}
|
||||||
|
if (!gltf->lights.ptrs.empty()) {
|
||||||
|
extensionsUsed.push_back(KHR_LIGHTS_PUNCTUAL);
|
||||||
|
}
|
||||||
if (options.draco.enabled) {
|
if (options.draco.enabled) {
|
||||||
extensionsUsed.push_back(KHR_DRACO_MESH_COMPRESSION);
|
extensionsUsed.push_back(KHR_DRACO_MESH_COMPRESSION);
|
||||||
extensionsRequired.push_back(KHR_DRACO_MESH_COMPRESSION);
|
extensionsRequired.push_back(KHR_DRACO_MESH_COMPRESSION);
|
||||||
|
@ -666,6 +716,7 @@ ModelData *Raw2Gltf(
|
||||||
}
|
}
|
||||||
|
|
||||||
gltf->serializeHolders(glTFJson);
|
gltf->serializeHolders(glTFJson);
|
||||||
|
|
||||||
gltfOutStream << glTFJson.dump(options.outputBinary ? 0 : 4);
|
gltfOutStream << glTFJson.dump(options.outputBinary ? 0 : 4);
|
||||||
}
|
}
|
||||||
if (options.outputBinary) {
|
if (options.outputBinary) {
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
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";
|
||||||
|
const std::string KHR_LIGHTS_PUNCTUAL = "KHR_lights_punctual";
|
||||||
|
|
||||||
const std::string extBufferFilename = "buffer.bin";
|
const std::string extBufferFilename = "buffer.bin";
|
||||||
|
|
||||||
|
|
|
@ -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 "LightData.hpp"
|
||||||
|
|
||||||
|
LightData::LightData(
|
||||||
|
std::string name, Type type, Vec3f color, float intensity,
|
||||||
|
float innerConeAngle, float outerConeAngle)
|
||||||
|
: Holdable(),
|
||||||
|
type(type),
|
||||||
|
color(color),
|
||||||
|
intensity(intensity),
|
||||||
|
innerConeAngle(innerConeAngle),
|
||||||
|
outerConeAngle(outerConeAngle)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
json LightData::serialize() const
|
||||||
|
{
|
||||||
|
json result {
|
||||||
|
{ "name", name },
|
||||||
|
{ "color", toStdVec(color) },
|
||||||
|
{ "intensity", intensity }
|
||||||
|
};
|
||||||
|
switch(type) {
|
||||||
|
case Directional:
|
||||||
|
result["type"] = "directional";
|
||||||
|
break;
|
||||||
|
case Point:
|
||||||
|
result["type"] = "point";
|
||||||
|
break;
|
||||||
|
case Spot:
|
||||||
|
result["type"] = "spot";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* 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 "gltf/Raw2Gltf.hpp"
|
||||||
|
|
||||||
|
struct LightData : Holdable
|
||||||
|
{
|
||||||
|
enum Type {
|
||||||
|
Directional,
|
||||||
|
Point,
|
||||||
|
Spot,
|
||||||
|
};
|
||||||
|
|
||||||
|
LightData(std::string name, Type type, Vec3f color, float intensity,
|
||||||
|
float innerConeAngle, float outerConeAngle);
|
||||||
|
|
||||||
|
json serialize() const override;
|
||||||
|
|
||||||
|
const std::string name;
|
||||||
|
const Type type;
|
||||||
|
const Vec3f color;
|
||||||
|
const float intensity;
|
||||||
|
const float innerConeAngle;
|
||||||
|
const float outerConeAngle;
|
||||||
|
};
|
|
@ -33,7 +33,7 @@ KHRCmnUnlitMaterial::KHRCmnUnlitMaterial()
|
||||||
|
|
||||||
void to_json(json &j, const KHRCmnUnlitMaterial &d)
|
void to_json(json &j, const KHRCmnUnlitMaterial &d)
|
||||||
{
|
{
|
||||||
j = json({});
|
j = json({});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline float clamp(float d, float bottom = 0, float top = 1) {
|
inline float clamp(float d, float bottom = 0, float top = 1) {
|
||||||
|
@ -128,5 +128,17 @@ json MaterialData::serialize() const
|
||||||
extensions[KHR_MATERIALS_CMN_UNLIT] = *khrCmnConstantMaterial;
|
extensions[KHR_MATERIALS_CMN_UNLIT] = *khrCmnConstantMaterial;
|
||||||
result["extensions"] = extensions;
|
result["extensions"] = extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& i : userProperties)
|
||||||
|
{
|
||||||
|
auto& prop_map = result["extras"]["fromFBX"]["userProperties"];
|
||||||
|
|
||||||
|
json j = json::parse(i);
|
||||||
|
for (const auto& k : json::iterator_wrapper(j))
|
||||||
|
{
|
||||||
|
prop_map[k.key()] = k.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,8 @@ struct MaterialData : Holdable
|
||||||
|
|
||||||
const std::shared_ptr<const KHRCmnUnlitMaterial> khrCmnConstantMaterial;
|
const std::shared_ptr<const KHRCmnUnlitMaterial> khrCmnConstantMaterial;
|
||||||
const std::shared_ptr<const PBRMetallicRoughness> pbrMetallicRoughness;
|
const std::shared_ptr<const PBRMetallicRoughness> pbrMetallicRoughness;
|
||||||
|
|
||||||
|
std::vector<std::string> userProperties;
|
||||||
};
|
};
|
||||||
|
|
||||||
void to_json(json &j, const Tex &data);
|
void to_json(json &j, const Tex &data);
|
||||||
|
|
|
@ -21,6 +21,7 @@ NodeData::NodeData(
|
||||||
children(),
|
children(),
|
||||||
mesh(-1),
|
mesh(-1),
|
||||||
camera(-1),
|
camera(-1),
|
||||||
|
light(-1),
|
||||||
skin(-1)
|
skin(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -50,6 +51,12 @@ void NodeData::SetCamera(uint32_t cameraIndex)
|
||||||
camera = cameraIndex;
|
camera = cameraIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NodeData::SetLight(uint32_t lightIndex)
|
||||||
|
{
|
||||||
|
assert(!isJoint);
|
||||||
|
light = lightIndex;
|
||||||
|
}
|
||||||
|
|
||||||
json NodeData::serialize() const
|
json NodeData::serialize() const
|
||||||
{
|
{
|
||||||
json result = { { "name", name } };
|
json result = { { "name", name } };
|
||||||
|
@ -84,6 +91,9 @@ json NodeData::serialize() const
|
||||||
if (camera >= 0) {
|
if (camera >= 0) {
|
||||||
result["camera"] = camera;
|
result["camera"] = camera;
|
||||||
}
|
}
|
||||||
|
if (light >= 0) {
|
||||||
|
result["extensions"][KHR_LIGHTS_PUNCTUAL]["light"] = light;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& i : userProperties)
|
for (const auto& i : userProperties)
|
||||||
|
|
|
@ -19,6 +19,7 @@ struct NodeData : Holdable
|
||||||
void SetMesh(uint32_t meshIx);
|
void SetMesh(uint32_t meshIx);
|
||||||
void SetSkin(uint32_t skinIx);
|
void SetSkin(uint32_t skinIx);
|
||||||
void SetCamera(uint32_t camera);
|
void SetCamera(uint32_t camera);
|
||||||
|
void SetLight(uint32_t light);
|
||||||
|
|
||||||
json serialize() const override;
|
json serialize() const override;
|
||||||
|
|
||||||
|
@ -30,6 +31,7 @@ struct NodeData : Holdable
|
||||||
std::vector<uint32_t> children;
|
std::vector<uint32_t> children;
|
||||||
int32_t mesh;
|
int32_t mesh;
|
||||||
int32_t camera;
|
int32_t camera;
|
||||||
|
int32_t light;
|
||||||
int32_t skin;
|
int32_t skin;
|
||||||
std::vector<std::string> skeletons;
|
std::vector<std::string> skeletons;
|
||||||
std::vector<std::string> userProperties;
|
std::vector<std::string> userProperties;
|
||||||
|
|
|
@ -76,6 +76,10 @@ 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() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Vec3f toVec3f(const FbxDouble3 &v) {
|
||||||
|
return Vec3f((float) v[0], (float) v[1], (float) v[2]);
|
||||||
|
}
|
||||||
|
|
||||||
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]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,14 +113,15 @@ int RawModel::AddTexture(const std::string &name, const std::string &fileName, c
|
||||||
|
|
||||||
int RawModel::AddMaterial(const RawMaterial &material)
|
int RawModel::AddMaterial(const RawMaterial &material)
|
||||||
{
|
{
|
||||||
return AddMaterial(material.name.c_str(), material.type, material.textures, material.info);
|
return AddMaterial(material.name.c_str(), material.type, material.textures, material.info, material.userProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
int RawModel::AddMaterial(
|
int RawModel::AddMaterial(
|
||||||
const char *name,
|
const char *name,
|
||||||
const RawMaterialType materialType,
|
const RawMaterialType materialType,
|
||||||
const int textures[RAW_TEXTURE_USAGE_MAX],
|
const int textures[RAW_TEXTURE_USAGE_MAX],
|
||||||
std::shared_ptr<RawMatProps> materialInfo)
|
std::shared_ptr<RawMatProps> materialInfo,
|
||||||
|
const std::vector<std::string>& userProperties)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < materials.size(); i++) {
|
for (size_t i = 0; i < materials.size(); i++) {
|
||||||
if (materials[i].name != name) {
|
if (materials[i].name != name) {
|
||||||
|
@ -136,6 +137,14 @@ int RawModel::AddMaterial(
|
||||||
for (int j = 0; match && j < RAW_TEXTURE_USAGE_MAX; j++) {
|
for (int j = 0; match && j < RAW_TEXTURE_USAGE_MAX; j++) {
|
||||||
match = match && (materials[i].textures[j] == textures[j]);
|
match = match && (materials[i].textures[j] == textures[j]);
|
||||||
}
|
}
|
||||||
|
if (materials[i].userProperties.size() != userProperties.size()) {
|
||||||
|
match = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int j = 0; match && j < userProperties.size(); j++) {
|
||||||
|
match = match && (materials[i].userProperties[j] == userProperties[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (match) {
|
if (match) {
|
||||||
return (int) i;
|
return (int) i;
|
||||||
}
|
}
|
||||||
|
@ -145,6 +154,7 @@ int RawModel::AddMaterial(
|
||||||
material.name = name;
|
material.name = name;
|
||||||
material.type = materialType;
|
material.type = materialType;
|
||||||
material.info = materialInfo;
|
material.info = materialInfo;
|
||||||
|
material.userProperties = userProperties;
|
||||||
|
|
||||||
for (int i = 0; i < RAW_TEXTURE_USAGE_MAX; i++) {
|
for (int i = 0; i < RAW_TEXTURE_USAGE_MAX; i++) {
|
||||||
material.textures[i] = textures[i];
|
material.textures[i] = textures[i];
|
||||||
|
@ -155,6 +165,40 @@ int RawModel::AddMaterial(
|
||||||
return (int) materials.size() - 1;
|
return (int) materials.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int RawModel::AddLight(
|
||||||
|
const char *name,
|
||||||
|
const RawLightType lightType,
|
||||||
|
const Vec3f color,
|
||||||
|
const float intensity,
|
||||||
|
const float innerConeAngle,
|
||||||
|
const float outerConeAngle)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < lights.size(); i ++) {
|
||||||
|
if (lights[i].name != name || lights[i].type != lightType) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// only care about cone angles for spot
|
||||||
|
if (lights[i].type == RAW_LIGHT_TYPE_SPOT) {
|
||||||
|
if (lights[i].innerConeAngle != innerConeAngle ||
|
||||||
|
lights[i].outerConeAngle != outerConeAngle) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (int) i;
|
||||||
|
}
|
||||||
|
RawLight light {
|
||||||
|
name,
|
||||||
|
lightType,
|
||||||
|
color,
|
||||||
|
intensity,
|
||||||
|
innerConeAngle,
|
||||||
|
outerConeAngle,
|
||||||
|
};
|
||||||
|
lights.push_back(light);
|
||||||
|
return (int) lights.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int RawModel::AddSurface(const RawSurface &surface)
|
int RawModel::AddSurface(const RawSurface &surface)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < surfaces.size(); i++) {
|
for (size_t i = 0; i < surfaces.size(); i++) {
|
||||||
|
@ -252,6 +296,7 @@ int RawModel::AddNode(const long id, const char *name, const long parentId)
|
||||||
joint.name = name;
|
joint.name = name;
|
||||||
joint.parentId = parentId;
|
joint.parentId = parentId;
|
||||||
joint.surfaceId = 0;
|
joint.surfaceId = 0;
|
||||||
|
joint.lightIx = -1;
|
||||||
joint.translation = Vec3f(0, 0, 0);
|
joint.translation = Vec3f(0, 0, 0);
|
||||||
joint.rotation = Quatf(0, 0, 0, 1);
|
joint.rotation = Quatf(0, 0, 0, 1);
|
||||||
joint.scale = Vec3f(1, 1, 1);
|
joint.scale = Vec3f(1, 1, 1);
|
||||||
|
@ -613,11 +658,11 @@ size_t RawModel::CalculateNormals(bool onlyBroken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &triangle : triangles) {
|
for (auto &triangle : triangles) {
|
||||||
bool relevant = false;
|
bool relevant = false;
|
||||||
for (int vertIx : triangle.verts) {
|
for (int vertIx : triangle.verts) {
|
||||||
relevant |= (brokenVerts.count(vertIx) > 0);
|
relevant |= (brokenVerts.count(vertIx) > 0);
|
||||||
}
|
}
|
||||||
if (!relevant) {
|
if (!relevant) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -626,8 +671,8 @@ size_t RawModel::CalculateNormals(bool onlyBroken)
|
||||||
if (!onlyBroken || brokenVerts.count(vertIx) > 0) {
|
if (!onlyBroken || brokenVerts.count(vertIx) > 0) {
|
||||||
vertices[vertIx].normal += faceNormal;
|
vertices[vertIx].normal += faceNormal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int vertIx = 0; vertIx < vertices.size(); vertIx ++) {
|
for (int vertIx = 0; vertIx < vertices.size(); vertIx ++) {
|
||||||
if (onlyBroken && brokenVerts.count(vertIx) == 0) {
|
if (onlyBroken && brokenVerts.count(vertIx) == 0) {
|
||||||
|
@ -642,6 +687,6 @@ size_t RawModel::CalculateNormals(bool onlyBroken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vertex.normal.Normalize();
|
vertex.normal.Normalize();
|
||||||
}
|
}
|
||||||
return onlyBroken ? brokenVerts.size() : vertices.size();
|
return onlyBroken ? brokenVerts.size() : vertices.size();
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,12 +261,31 @@ struct RawMetRoughMatProps : RawMatProps {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct RawMaterial
|
struct RawMaterial
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
RawMaterialType type;
|
RawMaterialType type;
|
||||||
std::shared_ptr<RawMatProps> info;
|
std::shared_ptr<RawMatProps> info;
|
||||||
int textures[RAW_TEXTURE_USAGE_MAX];
|
int textures[RAW_TEXTURE_USAGE_MAX];
|
||||||
|
std::vector<std::string> userProperties;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RawLightType
|
||||||
|
{
|
||||||
|
RAW_LIGHT_TYPE_DIRECTIONAL,
|
||||||
|
RAW_LIGHT_TYPE_POINT,
|
||||||
|
RAW_LIGHT_TYPE_SPOT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RawLight
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
RawLightType type;
|
||||||
|
Vec3f color;
|
||||||
|
float intensity;
|
||||||
|
float innerConeAngle; // only meaningful for spot
|
||||||
|
float outerConeAngle; // only meaningful for spot
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RawBlendChannel
|
struct RawBlendChannel
|
||||||
|
@ -347,6 +366,7 @@ struct RawNode
|
||||||
Quatf rotation;
|
Quatf rotation;
|
||||||
Vec3f scale;
|
Vec3f scale;
|
||||||
long surfaceId;
|
long surfaceId;
|
||||||
|
long lightIx;
|
||||||
std::vector<std::string> userProperties;
|
std::vector<std::string> userProperties;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -363,7 +383,9 @@ public:
|
||||||
int AddMaterial(const RawMaterial &material);
|
int AddMaterial(const RawMaterial &material);
|
||||||
int AddMaterial(
|
int AddMaterial(
|
||||||
const char *name, const RawMaterialType materialType, const int textures[RAW_TEXTURE_USAGE_MAX],
|
const char *name, const RawMaterialType materialType, const int textures[RAW_TEXTURE_USAGE_MAX],
|
||||||
std::shared_ptr<RawMatProps> materialInfo);
|
std::shared_ptr<RawMatProps> materialInfo, const std::vector<std::string>& userProperties);
|
||||||
|
int AddLight(const char *name, RawLightType lightType, Vec3f color, float intensity,
|
||||||
|
float innerConeAngle, float outerConeAngle);
|
||||||
int AddSurface(const RawSurface &suface);
|
int AddSurface(const RawSurface &suface);
|
||||||
int AddSurface(const char *name, long surfaceId);
|
int AddSurface(const char *name, long surfaceId);
|
||||||
int AddAnimation(const RawAnimation &animation);
|
int AddAnimation(const RawAnimation &animation);
|
||||||
|
@ -419,6 +441,10 @@ public:
|
||||||
int GetCameraCount() const { return (int) cameras.size(); }
|
int GetCameraCount() const { return (int) cameras.size(); }
|
||||||
const RawCamera &GetCamera(const int index) const { return cameras[index]; }
|
const RawCamera &GetCamera(const int index) const { return cameras[index]; }
|
||||||
|
|
||||||
|
// Iterate over the lights.
|
||||||
|
int GetLightCount() const { return (int) lights.size(); }
|
||||||
|
const RawLight &GetLight(const int index) const { return lights[index]; }
|
||||||
|
|
||||||
// Iterate over the nodes.
|
// Iterate over the nodes.
|
||||||
int GetNodeCount() const { return (int) nodes.size(); }
|
int GetNodeCount() const { return (int) nodes.size(); }
|
||||||
const RawNode &GetNode(const int index) const { return nodes[index]; }
|
const RawNode &GetNode(const int index) const { return nodes[index]; }
|
||||||
|
@ -446,6 +472,7 @@ private:
|
||||||
std::vector<RawTriangle> triangles;
|
std::vector<RawTriangle> triangles;
|
||||||
std::vector<RawTexture> textures;
|
std::vector<RawTexture> textures;
|
||||||
std::vector<RawMaterial> materials;
|
std::vector<RawMaterial> materials;
|
||||||
|
std::vector<RawLight> lights;
|
||||||
std::vector<RawSurface> surfaces;
|
std::vector<RawSurface> surfaces;
|
||||||
std::vector<RawAnimation> animations;
|
std::vector<RawAnimation> animations;
|
||||||
std::vector<RawCamera> cameras;
|
std::vector<RawCamera> cameras;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue