Merge d96da9434e
into ed43cacb33
This commit is contained in:
commit
662b55d328
|
@ -1,4 +1,4 @@
|
||||||
# FBX2glTF
|
# FBX2glTF
|
||||||
|
|
||||||
[](https://opensource.org/licenses/BSD-3-Clause)
|
[](https://opensource.org/licenses/BSD-3-Clause)
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@ Options:
|
||||||
--flip-v Flip all V texture coordinates.
|
--flip-v Flip all V texture coordinates.
|
||||||
--no-flip-v Don't flip V texture coordinates.
|
--no-flip-v Don't flip V texture coordinates.
|
||||||
--no-khr-lights-punctual Don't use KHR_lights_punctual extension to export FBX lights.
|
--no-khr-lights-punctual Don't use KHR_lights_punctual extension to export FBX lights.
|
||||||
|
--animation-files Read multiple fbx animation files.
|
||||||
--user-properties Transcribe FBX User Properties into glTF node and material 'extras'.
|
--user-properties Transcribe FBX User Properties into glTF node and material 'extras'.
|
||||||
--blend-shape-normals Include blend shape normals, if reported present by the FBX SDK.
|
--blend-shape-normals Include blend shape normals, if reported present by the FBX SDK.
|
||||||
--blend-shape-tangents Include blend shape tangents, if reported present by the FBX SDK.
|
--blend-shape-tangents Include blend shape tangents, if reported present by the FBX SDK.
|
||||||
|
@ -97,6 +98,10 @@ Some of these switches are not obvious:
|
||||||
Your FBX is likely constructed with the assumption that `(0, 0)` is bottom
|
Your FBX is likely constructed with the assumption that `(0, 0)` is bottom
|
||||||
left, whereas glTF has `(0, 0)` as top left. To produce spec-compliant glTF,
|
left, whereas glTF has `(0, 0)` as top left. To produce spec-compliant glTF,
|
||||||
we must flip the texcoords. To request unflipped coordinates:
|
we must flip the texcoords. To request unflipped coordinates:
|
||||||
|
- '--animation-files' will try to read in additional animations from multiple
|
||||||
|
fbx files with numbers added to the base name (eg. test1.fbx, test2.fbx, ...).
|
||||||
|
It will apply animations to geometry in the base file by matching the node
|
||||||
|
names, so make sure all nodes have a unique name.
|
||||||
- `--long-indices` lets you force the use of either 16-bit or 32-bit indices.
|
- `--long-indices` lets you force the use of either 16-bit or 32-bit indices.
|
||||||
The default option is auto, which make the choice on a per-mesh-size basis.
|
The default option is auto, which make the choice on a per-mesh-size basis.
|
||||||
- `--compute-normals` controls when automatic vertex normals should be computed
|
- `--compute-normals` controls when automatic vertex normals should be computed
|
||||||
|
|
|
@ -137,6 +137,11 @@ int main(int argc, char* argv[]) {
|
||||||
[&](size_t count) { gltfOptions.useKHRLightsPunctual = (count == 0); },
|
[&](size_t count) { gltfOptions.useKHRLightsPunctual = (count == 0); },
|
||||||
"Don't use KHR_lights_punctual extension to export FBX lights.");
|
"Don't use KHR_lights_punctual extension to export FBX lights.");
|
||||||
|
|
||||||
|
app.add_flag(
|
||||||
|
"--animation-files",
|
||||||
|
gltfOptions.readAnimationFiles,
|
||||||
|
"Read multiple fbx animation files.");
|
||||||
|
|
||||||
app.add_flag(
|
app.add_flag(
|
||||||
"--user-properties",
|
"--user-properties",
|
||||||
gltfOptions.enableUserProperties,
|
gltfOptions.enableUserProperties,
|
||||||
|
@ -314,6 +319,7 @@ int main(int argc, char* argv[]) {
|
||||||
if (verboseOutput) {
|
if (verboseOutput) {
|
||||||
fmt::printf("Loading FBX File: %s\n", inputPath);
|
fmt::printf("Loading FBX File: %s\n", inputPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LoadFBXFile(raw, inputPath, {"png", "jpg", "jpeg"}, gltfOptions)) {
|
if (!LoadFBXFile(raw, inputPath, {"png", "jpg", "jpeg"}, gltfOptions)) {
|
||||||
fmt::fprintf(stderr, "ERROR:: Failed to parse FBX: %s\n", inputPath);
|
fmt::fprintf(stderr, "ERROR:: Failed to parse FBX: %s\n", inputPath);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -112,6 +112,9 @@ struct GltfOptions {
|
||||||
/** Whether to include lights through the KHR_punctual_lights extension. */
|
/** Whether to include lights through the KHR_punctual_lights extension. */
|
||||||
bool useKHRLightsPunctual{true};
|
bool useKHRLightsPunctual{true};
|
||||||
|
|
||||||
|
/** Whether to read multiple fbx animation files. */
|
||||||
|
bool readAnimationFiles{false};
|
||||||
|
|
||||||
/** 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. */
|
||||||
|
|
|
@ -720,7 +720,7 @@ static void ReadNodeHierarchy(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReadAnimations(RawModel& raw, FbxScene* pScene, const GltfOptions& options) {
|
static void ReadAnimations(RawModel& raw, FbxScene* pScene, const GltfOptions& options, bool nodeNameLookup) {
|
||||||
FbxTime::EMode eMode = FbxTime::eFrames24;
|
FbxTime::EMode eMode = FbxTime::eFrames24;
|
||||||
switch (options.animationFramerate) {
|
switch (options.animationFramerate) {
|
||||||
case AnimationFramerateOptions::BAKE24:
|
case AnimationFramerateOptions::BAKE24:
|
||||||
|
@ -816,7 +816,14 @@ static void ReadAnimations(RawModel& raw, FbxScene* pScene, const GltfOptions& o
|
||||||
bool hasMorphs = false;
|
bool hasMorphs = false;
|
||||||
|
|
||||||
RawChannel channel;
|
RawChannel channel;
|
||||||
channel.nodeIndex = raw.GetNodeById(pNode->GetUniqueID());
|
if (nodeNameLookup) {
|
||||||
|
channel.nodeIndex = raw.GetNodeByName(pNode->GetName());
|
||||||
|
} else {
|
||||||
|
channel.nodeIndex = raw.GetNodeById(pNode->GetUniqueID());
|
||||||
|
}
|
||||||
|
if (channel.nodeIndex == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (FbxLongLong frameIndex = firstFrameIndex; frameIndex <= lastFrameIndex; frameIndex++) {
|
for (FbxLongLong frameIndex = firstFrameIndex; frameIndex <= lastFrameIndex; frameIndex++) {
|
||||||
FbxTime pTime;
|
FbxTime pTime;
|
||||||
|
@ -1068,15 +1075,7 @@ static void FindFbxTextures(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LoadFBXFile(
|
static FbxScene* LoadScene(FbxManager* pManager, const std::string fbxFileName) {
|
||||||
RawModel& raw,
|
|
||||||
const std::string fbxFileName,
|
|
||||||
const std::set<std::string>& textureExtensions,
|
|
||||||
const GltfOptions& options) {
|
|
||||||
FbxManager* pManager = FbxManager::Create();
|
|
||||||
FbxIOSettings* pIoSettings = FbxIOSettings::Create(pManager, IOSROOT);
|
|
||||||
pManager->SetIOSettings(pIoSettings);
|
|
||||||
|
|
||||||
FbxImporter* pImporter = FbxImporter::Create(pManager, "");
|
FbxImporter* pImporter = FbxImporter::Create(pManager, "");
|
||||||
|
|
||||||
if (!pImporter->Initialize(fbxFileName.c_str(), -1, pManager->GetIOSettings())) {
|
if (!pImporter->Initialize(fbxFileName.c_str(), -1, pManager->GetIOSettings())) {
|
||||||
|
@ -1084,8 +1083,7 @@ bool LoadFBXFile(
|
||||||
fmt::printf("%s\n", pImporter->GetStatus().GetErrorString());
|
fmt::printf("%s\n", pImporter->GetStatus().GetErrorString());
|
||||||
}
|
}
|
||||||
pImporter->Destroy();
|
pImporter->Destroy();
|
||||||
pManager->Destroy();
|
return nullptr;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FbxScene* pScene = FbxScene::Create(pManager, "fbxScene");
|
FbxScene* pScene = FbxScene::Create(pManager, "fbxScene");
|
||||||
|
@ -1093,14 +1091,9 @@ bool LoadFBXFile(
|
||||||
pImporter->Destroy();
|
pImporter->Destroy();
|
||||||
|
|
||||||
if (pScene == nullptr) {
|
if (pScene == nullptr) {
|
||||||
pImporter->Destroy();
|
return nullptr;
|
||||||
pManager->Destroy();
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<const FbxTexture*, FbxString> textureLocations;
|
|
||||||
FindFbxTextures(pScene, fbxFileName, textureExtensions, textureLocations);
|
|
||||||
|
|
||||||
// Use Y up for glTF
|
// Use Y up for glTF
|
||||||
FbxAxisSystem::MayaYUp.ConvertScene(pScene);
|
FbxAxisSystem::MayaYUp.ConvertScene(pScene);
|
||||||
|
|
||||||
|
@ -1113,14 +1106,54 @@ bool LoadFBXFile(
|
||||||
if (sceneSystemUnit != FbxSystemUnit::cm) {
|
if (sceneSystemUnit != FbxSystemUnit::cm) {
|
||||||
FbxSystemUnit::cm.ConvertScene(pScene);
|
FbxSystemUnit::cm.ConvertScene(pScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return pScene;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoadFBXFile(
|
||||||
|
RawModel& raw,
|
||||||
|
const std::string fbxFileName,
|
||||||
|
const std::set<std::string>& textureExtensions,
|
||||||
|
const GltfOptions& options) {
|
||||||
|
FbxManager* pManager = FbxManager::Create();
|
||||||
|
FbxIOSettings* pIoSettings = FbxIOSettings::Create(pManager, IOSROOT);
|
||||||
|
pManager->SetIOSettings(pIoSettings);
|
||||||
|
|
||||||
|
FbxScene* pScene = LoadScene(pManager, fbxFileName);
|
||||||
|
if (pScene == nullptr) {
|
||||||
|
pManager->Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// this is always 0.01, but let's opt for clarity.
|
// this is always 0.01, but let's opt for clarity.
|
||||||
scaleFactor = FbxSystemUnit::m.GetConversionFactorFrom(FbxSystemUnit::cm);
|
scaleFactor = FbxSystemUnit::m.GetConversionFactorFrom(FbxSystemUnit::cm);
|
||||||
|
|
||||||
|
std::map<const FbxTexture*, FbxString> textureLocations;
|
||||||
|
FindFbxTextures(pScene, fbxFileName, textureExtensions, textureLocations);
|
||||||
|
|
||||||
ReadNodeHierarchy(raw, pScene, pScene->GetRootNode(), 0, "");
|
ReadNodeHierarchy(raw, pScene, pScene->GetRootNode(), 0, "");
|
||||||
ReadNodeAttributes(raw, pScene, pScene->GetRootNode(), textureLocations);
|
ReadNodeAttributes(raw, pScene, pScene->GetRootNode(), textureLocations);
|
||||||
ReadAnimations(raw, pScene, options);
|
ReadAnimations(raw, pScene, options, false);
|
||||||
|
|
||||||
pScene->Destroy();
|
pScene->Destroy();
|
||||||
|
|
||||||
|
// read additional animation files
|
||||||
|
bool readAnimationFiles = options.readAnimationFiles;
|
||||||
|
int animationFileNumber = 1;
|
||||||
|
while (readAnimationFiles) {
|
||||||
|
std::string animationFileName = StringUtils::GetFolderString(fbxFileName) +
|
||||||
|
StringUtils::GetFileBaseString(fbxFileName) +
|
||||||
|
std::to_string(animationFileNumber) + ".fbx";
|
||||||
|
FbxScene* pAnimationScene = LoadScene(pManager, animationFileName.c_str());
|
||||||
|
if (pAnimationScene != nullptr) {
|
||||||
|
ReadAnimations(raw, pAnimationScene, options, true);
|
||||||
|
pAnimationScene->Destroy();
|
||||||
|
animationFileNumber++;
|
||||||
|
} else {
|
||||||
|
readAnimationFiles = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pManager->Destroy();
|
pManager->Destroy();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -640,6 +640,15 @@ int RawModel::GetNodeById(const long nodeId) const {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int RawModel::GetNodeByName(const char* name) const {
|
||||||
|
for (size_t i = 0; i < nodes.size(); i++) {
|
||||||
|
if (nodes[i].name == name) {
|
||||||
|
return (int)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int RawModel::GetSurfaceById(const long surfaceId) const {
|
int RawModel::GetSurfaceById(const long surfaceId) const {
|
||||||
for (size_t i = 0; i < surfaces.size(); i++) {
|
for (size_t i = 0; i < surfaces.size(); i++) {
|
||||||
if (surfaces[i].id == surfaceId) {
|
if (surfaces[i].id == surfaceId) {
|
||||||
|
|
|
@ -508,6 +508,7 @@ class RawModel {
|
||||||
return nodes[index];
|
return nodes[index];
|
||||||
}
|
}
|
||||||
int GetNodeById(const long nodeId) const;
|
int GetNodeById(const long nodeId) const;
|
||||||
|
int GetNodeByName(const char* name) const;
|
||||||
|
|
||||||
// Create individual attribute arrays.
|
// Create individual attribute arrays.
|
||||||
// Returns true if the vertices store the particular attribute.
|
// Returns true if the vertices store the particular attribute.
|
||||||
|
|
Loading…
Reference in New Issue