Merge branch 'master' of https://github.com/facebookincubator/FBX2glTF into sync-upstream
This commit is contained in:
commit
cd7ecd8f82
|
@ -27,7 +27,7 @@ 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.2.5
|
GIT_TAG 1.3.1
|
||||||
PREFIX draco
|
PREFIX draco
|
||||||
INSTALL_DIR
|
INSTALL_DIR
|
||||||
CMAKE_ARGS
|
CMAKE_ARGS
|
||||||
|
@ -130,28 +130,48 @@ if (APPLE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
src/utils/File_Utils.cpp
|
src/FBX2glTF.h
|
||||||
src/utils/Image_Utils.cpp
|
|
||||||
src/utils/String_Utils.cpp
|
|
||||||
src/main.cpp
|
|
||||||
src/Fbx2Raw.cpp
|
src/Fbx2Raw.cpp
|
||||||
|
src/Fbx2Raw.h
|
||||||
src/Raw2Gltf.cpp
|
src/Raw2Gltf.cpp
|
||||||
|
src/Raw2Gltf.h
|
||||||
src/RawModel.cpp
|
src/RawModel.cpp
|
||||||
src/glTF/BufferData.cpp
|
src/RawModel.h
|
||||||
src/glTF/MaterialData.cpp
|
|
||||||
src/glTF/MeshData.cpp
|
|
||||||
src/glTF/NodeData.cpp
|
|
||||||
src/glTF/PrimitiveData.cpp
|
|
||||||
src/glTF/BufferViewData.cpp
|
|
||||||
src/glTF/BufferViewData.h
|
|
||||||
src/glTF/AccessorData.cpp
|
src/glTF/AccessorData.cpp
|
||||||
src/glTF/AccessorData.h
|
src/glTF/AccessorData.h
|
||||||
src/glTF/ImageData.cpp
|
|
||||||
src/glTF/TextureData.cpp
|
|
||||||
src/glTF/SkinData.cpp
|
|
||||||
src/glTF/AnimationData.cpp
|
src/glTF/AnimationData.cpp
|
||||||
|
src/glTF/AnimationData.h
|
||||||
|
src/glTF/BufferData.cpp
|
||||||
|
src/glTF/BufferData.h
|
||||||
|
src/glTF/BufferViewData.cpp
|
||||||
|
src/glTF/BufferViewData.h
|
||||||
src/glTF/CameraData.cpp
|
src/glTF/CameraData.cpp
|
||||||
|
src/glTF/CameraData.h
|
||||||
|
src/glTF/ImageData.cpp
|
||||||
|
src/glTF/ImageData.h
|
||||||
|
src/glTF/MaterialData.cpp
|
||||||
|
src/glTF/MaterialData.h
|
||||||
|
src/glTF/MeshData.cpp
|
||||||
|
src/glTF/MeshData.h
|
||||||
|
src/glTF/NodeData.cpp
|
||||||
|
src/glTF/NodeData.h
|
||||||
|
src/glTF/PrimitiveData.cpp
|
||||||
|
src/glTF/PrimitiveData.h
|
||||||
|
src/glTF/SamplerData.h
|
||||||
src/glTF/SceneData.cpp
|
src/glTF/SceneData.cpp
|
||||||
|
src/glTF/SceneData.h
|
||||||
|
src/glTF/SkinData.cpp
|
||||||
|
src/glTF/SkinData.h
|
||||||
|
src/glTF/TextureData.cpp
|
||||||
|
src/glTF/TextureData.h
|
||||||
|
src/main.cpp
|
||||||
|
src/mathfu.h
|
||||||
|
src/utils/File_Utils.cpp
|
||||||
|
src/utils/File_Utils.h
|
||||||
|
src/utils/Image_Utils.cpp
|
||||||
|
src/utils/Image_Utils.h
|
||||||
|
src/utils/String_Utils.cpp
|
||||||
|
src/utils/String_Utils.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(FBX2glTF ${SOURCE_FILES})
|
add_executable(FBX2glTF ${SOURCE_FILES})
|
||||||
|
|
|
@ -236,12 +236,12 @@ ratification process**
|
||||||
Given the command line flag --pbr-metallic-roughness, we throw ourselves into
|
Given the command line flag --pbr-metallic-roughness, we throw ourselves into
|
||||||
the warm embrace of glTF 2.0's PBR preference.
|
the warm embrace of glTF 2.0's PBR preference.
|
||||||
|
|
||||||
As mentioned above, there is lilttle consensus in the world on how PBR should be
|
As mentioned above, there is little consensus in the world on how PBR should be
|
||||||
represented in FBX. At present, we support only one format: Stingray PBS. This
|
represented in FBX. At present, we support only one format: Stingray PBS. This
|
||||||
is a featue that comes bundled with Maya, and any PBR model exported through
|
is a feature that comes bundled with Maya, and any PBR model exported through
|
||||||
that route should be digested propertly by FBX2glTF.
|
that route should be digested propertly by FBX2glTF.
|
||||||
|
|
||||||
(A happy note: Allegorithmic's Susbstance Painter also exports Stingray PBS,
|
(A happy note: Allegorithmic's Substance Painter also exports Stingray PBS,
|
||||||
when hooked up to Maya.)
|
when hooked up to Maya.)
|
||||||
|
|
||||||
## Draco Compression
|
## Draco Compression
|
||||||
|
|
|
@ -11,16 +11,21 @@
|
||||||
#define __FBX2GLTF_H__
|
#define __FBX2GLTF_H__
|
||||||
|
|
||||||
#if defined ( _WIN32 )
|
#if defined ( _WIN32 )
|
||||||
// This can be a macro under Windows, confusing FMT
|
|
||||||
#undef isnan
|
|
||||||
// Tell Windows not to define min() and max() macros
|
// Tell Windows not to define min() and max() macros
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const std::string FBX2GLTF_VERSION = "0.9.5";
|
||||||
|
|
||||||
#include <fmt/printf.h>
|
#include <fmt/printf.h>
|
||||||
#include <fbxsdk.h>
|
#include <fbxsdk.h>
|
||||||
|
|
||||||
|
#if defined ( _WIN32 )
|
||||||
|
// this is defined in fbxmath.h
|
||||||
|
#undef isnan
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "mathfu.h"
|
#include "mathfu.h"
|
||||||
|
|
||||||
#endif // !__FBX2GLTF_H__
|
#endif // !__FBX2GLTF_H__
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "FBX2glTF.h"
|
#include "FBX2glTF.h"
|
||||||
#include "utils/File_Utils.h"
|
#include "utils/File_Utils.h"
|
||||||
|
@ -401,12 +402,14 @@ public:
|
||||||
for (int deformerIndex = 0; deformerIndex < pMesh->GetDeformerCount(); deformerIndex++) {
|
for (int deformerIndex = 0; deformerIndex < pMesh->GetDeformerCount(); deformerIndex++) {
|
||||||
FbxSkin *skin = reinterpret_cast< FbxSkin * >( pMesh->GetDeformer(deformerIndex, FbxDeformer::eSkin));
|
FbxSkin *skin = reinterpret_cast< FbxSkin * >( pMesh->GetDeformer(deformerIndex, FbxDeformer::eSkin));
|
||||||
if (skin != nullptr) {
|
if (skin != nullptr) {
|
||||||
|
const int clusterCount = skin->GetClusterCount();
|
||||||
|
if (clusterCount == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
int controlPointCount = pMesh->GetControlPointsCount();
|
int controlPointCount = pMesh->GetControlPointsCount();
|
||||||
|
|
||||||
vertexJointIndices.resize(controlPointCount, Vec4i(0, 0, 0, 0));
|
vertexJointIndices.resize(controlPointCount, Vec4i(0, 0, 0, 0));
|
||||||
vertexJointWeights.resize(controlPointCount, Vec4f(0.0f, 0.0f, 0.0f, 0.0f));
|
vertexJointWeights.resize(controlPointCount, Vec4f(0.0f, 0.0f, 0.0f, 0.0f));
|
||||||
|
|
||||||
const int clusterCount = skin->GetClusterCount();
|
|
||||||
for (int clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++) {
|
for (int clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++) {
|
||||||
FbxCluster *cluster = skin->GetCluster(clusterIndex);
|
FbxCluster *cluster = skin->GetCluster(clusterIndex);
|
||||||
const int indexCount = cluster->GetControlPointIndicesCount();
|
const int indexCount = cluster->GetControlPointIndicesCount();
|
||||||
|
@ -1262,7 +1265,7 @@ static void ReadAnimations(RawModel &raw, FbxScene *pScene)
|
||||||
for (int targetIx = 0; targetIx < targetCount; targetIx++) {
|
for (int targetIx = 0; targetIx < targetCount; targetIx++) {
|
||||||
if (curve) {
|
if (curve) {
|
||||||
float result = findInInterval(influence, targetIx-1);
|
float result = findInInterval(influence, targetIx-1);
|
||||||
if (!isnan(result)) {
|
if (!std::isnan(result)) {
|
||||||
// we're transitioning into targetIx
|
// we're transitioning into targetIx
|
||||||
channel.weights.push_back(result);
|
channel.weights.push_back(result);
|
||||||
hasMorphs = true;
|
hasMorphs = true;
|
||||||
|
@ -1270,7 +1273,7 @@ static void ReadAnimations(RawModel &raw, FbxScene *pScene)
|
||||||
}
|
}
|
||||||
if (targetIx != targetCount-1) {
|
if (targetIx != targetCount-1) {
|
||||||
result = findInInterval(influence, targetIx);
|
result = findInInterval(influence, targetIx);
|
||||||
if (!isnan(result)) {
|
if (!std::isnan(result)) {
|
||||||
// we're transitioning AWAY from targetIx
|
// we're transitioning AWAY from targetIx
|
||||||
channel.weights.push_back(1.0f - result);
|
channel.weights.push_back(1.0f - result);
|
||||||
hasMorphs = true;
|
hasMorphs = true;
|
||||||
|
|
|
@ -827,7 +827,7 @@ ModelData *Raw2Gltf(
|
||||||
&& surfaceModel.GetVertexCount() > 65535);
|
&& surfaceModel.GetVertexCount() > 65535);
|
||||||
|
|
||||||
std::shared_ptr<PrimitiveData> primitive;
|
std::shared_ptr<PrimitiveData> primitive;
|
||||||
if (options.useDraco) {
|
if (options.draco.enabled) {
|
||||||
int triangleCount = surfaceModel.GetTriangleCount();
|
int triangleCount = surfaceModel.GetTriangleCount();
|
||||||
|
|
||||||
// initialize Draco mesh with vertex index information
|
// initialize Draco mesh with vertex index information
|
||||||
|
@ -943,17 +943,29 @@ ModelData *Raw2Gltf(
|
||||||
primitive->AddTarget(pAcc.get(), nAcc.get(), tAcc.get(), channel.name);
|
primitive->AddTarget(pAcc.get(), nAcc.get(), tAcc.get(), channel.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (options.useDraco) {
|
if (options.draco.enabled) {
|
||||||
// Set up the encoder.
|
// Set up the encoder.
|
||||||
draco::Encoder encoder;
|
draco::Encoder encoder;
|
||||||
|
|
||||||
// TODO: generalize / allow configuration
|
if (options.draco.compressionLevel != -1) {
|
||||||
encoder.SetSpeedOptions(5, 5);
|
int dracoSpeed = 10 - options.draco.compressionLevel;
|
||||||
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 14);
|
encoder.SetSpeedOptions(dracoSpeed, dracoSpeed);
|
||||||
encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, 10);
|
}
|
||||||
encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, 10);
|
if (options.draco.quantBitsPosition != -1) {
|
||||||
encoder.SetAttributeQuantization(draco::GeometryAttribute::COLOR, 8);
|
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, options.draco.quantBitsPosition);
|
||||||
encoder.SetAttributeQuantization(draco::GeometryAttribute::GENERIC, 8);
|
}
|
||||||
|
if (options.draco.quantBitsTexCoord != -1) {
|
||||||
|
encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, options.draco.quantBitsTexCoord);
|
||||||
|
}
|
||||||
|
if (options.draco.quantBitsNormal != -1) {
|
||||||
|
encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, options.draco.quantBitsNormal);
|
||||||
|
}
|
||||||
|
if (options.draco.quantBitsColor != -1) {
|
||||||
|
encoder.SetAttributeQuantization(draco::GeometryAttribute::COLOR, options.draco.quantBitsColor);
|
||||||
|
}
|
||||||
|
if (options.draco.quantBitsGeneric != -1) {
|
||||||
|
encoder.SetAttributeQuantization(draco::GeometryAttribute::GENERIC, options.draco.quantBitsGeneric);
|
||||||
|
}
|
||||||
|
|
||||||
draco::EncoderBuffer dracoBuffer;
|
draco::EncoderBuffer dracoBuffer;
|
||||||
draco::Status status = encoder.EncodeMeshToBuffer(*primitive->dracoMesh, &dracoBuffer);
|
draco::Status status = encoder.EncodeMeshToBuffer(*primitive->dracoMesh, &dracoBuffer);
|
||||||
|
@ -1070,14 +1082,14 @@ ModelData *Raw2Gltf(
|
||||||
if (options.useKHRMatUnlit) {
|
if (options.useKHRMatUnlit) {
|
||||||
extensionsUsed.push_back(KHR_MATERIALS_CMN_UNLIT);
|
extensionsUsed.push_back(KHR_MATERIALS_CMN_UNLIT);
|
||||||
}
|
}
|
||||||
if (options.useDraco) {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
json glTFJson {
|
json glTFJson {
|
||||||
{ "asset", {
|
{ "asset", {
|
||||||
{ "generator", "FBX2glTF" },
|
{ "generator", "FBX2glTF v" + FBX2GLTF_VERSION },
|
||||||
{ "version", "2.0" }}},
|
{ "version", "2.0" }}},
|
||||||
{ "scene", rootScene.ix }
|
{ "scene", rootScene.ix }
|
||||||
};
|
};
|
||||||
|
|
|
@ -269,11 +269,20 @@ void RawModel::Condense()
|
||||||
|
|
||||||
surfaces.clear();
|
surfaces.clear();
|
||||||
|
|
||||||
|
std::set<int> survivingSurfaceIds;
|
||||||
for (auto &triangle : triangles) {
|
for (auto &triangle : triangles) {
|
||||||
|
int oldSurfaceIndex = triangle.surfaceIndex;
|
||||||
const RawSurface &surface = oldSurfaces[triangle.surfaceIndex];
|
const RawSurface &surface = oldSurfaces[triangle.surfaceIndex];
|
||||||
const int surfaceIndex = AddSurface(surface.name.c_str(), surface.id);
|
const int surfaceIndex = AddSurface(surface.name.c_str(), surface.id);
|
||||||
surfaces[surfaceIndex] = surface;
|
surfaces[surfaceIndex] = surface;
|
||||||
triangle.surfaceIndex = surfaceIndex;
|
triangle.surfaceIndex = surfaceIndex;
|
||||||
|
survivingSurfaceIds.emplace(surface.id);
|
||||||
|
}
|
||||||
|
// clear out references to meshes that no longer exist
|
||||||
|
for (auto &node : nodes) {
|
||||||
|
if (node.surfaceId != 0 && survivingSurfaceIds.find(node.surfaceId) == survivingSurfaceIds.end()) {
|
||||||
|
node.surfaceId = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,7 +350,9 @@ void RawModel::TransformGeometry(ComputeNormalsOption normals)
|
||||||
|
|
||||||
if (verboseOutput) {
|
if (verboseOutput) {
|
||||||
if (normals == ComputeNormalsOption::BROKEN) {
|
if (normals == ComputeNormalsOption::BROKEN) {
|
||||||
fmt::printf("Repaired %lu empty normals.\n", computedNormalsCount);
|
if (computedNormalsCount > 0) {
|
||||||
|
fmt::printf("Repaired %lu empty normals.\n", computedNormalsCount);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt::printf("Computed %lu normals.\n", computedNormalsCount);
|
fmt::printf("Computed %lu normals.\n", computedNormalsCount);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,8 +46,18 @@ struct GltfOptions
|
||||||
bool outputBinary { false };
|
bool outputBinary { false };
|
||||||
/** If non-binary, whether to inline all resources, for a single (large) .glTF file. */
|
/** If non-binary, whether to inline all resources, for a single (large) .glTF file. */
|
||||||
bool embedResources { false };
|
bool embedResources { false };
|
||||||
/** Whether to use KHR_draco_mesh_compression to minimize static geometry size. */
|
|
||||||
bool useDraco { false };
|
/** Whether and how to use KHR_draco_mesh_compression to minimize static geometry size. */
|
||||||
|
struct {
|
||||||
|
bool enabled = false;
|
||||||
|
int compressionLevel = -1;
|
||||||
|
int quantBitsPosition = -1;
|
||||||
|
int quantBitsTexCoord = -1;
|
||||||
|
int quantBitsNormal = -1;
|
||||||
|
int quantBitsColor = -1;
|
||||||
|
int quantBitsGeneric = -1;
|
||||||
|
} draco;
|
||||||
|
|
||||||
/** Whether to use KHR_materials_unlit to extend materials definitions. */
|
/** Whether to use KHR_materials_unlit to extend materials definitions. */
|
||||||
bool useKHRMatUnlit { false };
|
bool useKHRMatUnlit { false };
|
||||||
/** Whether to populate the pbrMetallicRoughness substruct in materials. */
|
/** Whether to populate the pbrMetallicRoughness substruct in materials. */
|
||||||
|
|
35
src/main.cpp
35
src/main.cpp
|
@ -34,7 +34,8 @@ int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
cxxopts::Options options(
|
cxxopts::Options options(
|
||||||
"FBX2glTF",
|
"FBX2glTF",
|
||||||
"FBX2glTF 2.0: Generate a glTF 2.0 representation of an FBX model.");
|
fmt::sprintf("FBX2glTF %s: Generate a glTF 2.0 representation of an FBX model.", FBX2GLTF_VERSION)
|
||||||
|
);
|
||||||
|
|
||||||
std::string inputPath;
|
std::string inputPath;
|
||||||
std::string outputPath;
|
std::string outputPath;
|
||||||
|
@ -62,7 +63,25 @@ int main(int argc, char *argv[])
|
||||||
cxxopts::value<std::vector<std::string>>())
|
cxxopts::value<std::vector<std::string>>())
|
||||||
(
|
(
|
||||||
"d,draco", "Apply Draco mesh compression to geometries.",
|
"d,draco", "Apply Draco mesh compression to geometries.",
|
||||||
cxxopts::value<bool>(gltfOptions.useDraco))
|
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).",
|
"compute-normals", "When to compute normals for vertices (never|broken|missing|always).",
|
||||||
cxxopts::value<std::vector<std::string>>())
|
cxxopts::value<std::vector<std::string>>())
|
||||||
|
@ -98,11 +117,7 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.count("version")) {
|
if (options.count("version")) {
|
||||||
fmt::printf(
|
fmt::printf("FBX2glTF version %s\nCopyright (c) 2016-2017 Oculus VR, LLC.\n", FBX2GLTF_VERSION);
|
||||||
R"(
|
|
||||||
FBX2glTF version 2.0
|
|
||||||
Copyright (c) 2016-2017 Oculus VR, LLC.
|
|
||||||
)");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +143,12 @@ Copyright (c) 2016-2017 Oculus VR, LLC.
|
||||||
gltfOptions.usePBRMetRough = true;
|
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) {
|
if (options.count("flip-u") > 0) {
|
||||||
texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(1.0f - uv[0], uv[1]); });
|
texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(1.0f - uv[0], uv[1]); });
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue