This commit is contained in:
Simon 2019-05-07 20:16:47 +00:00 committed by GitHub
commit 662b55d328
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 78 additions and 21 deletions

View File

@ -1,4 +1,4 @@
# FBX2glTF # FBX2glTF
[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](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

View File

@ -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;

View File

@ -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. */

View File

@ -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;

View File

@ -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) {

View File

@ -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.