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)
@ -56,6 +56,7 @@ Options:
--flip-v Flip all 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.
--animation-files Read multiple fbx animation files.
--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-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
left, whereas glTF has `(0, 0)` as top left. To produce spec-compliant glTF,
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.
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

View File

@ -137,6 +137,11 @@ int main(int argc, char* argv[]) {
[&](size_t count) { gltfOptions.useKHRLightsPunctual = (count == 0); },
"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(
"--user-properties",
gltfOptions.enableUserProperties,
@ -314,6 +319,7 @@ int main(int argc, char* argv[]) {
if (verboseOutput) {
fmt::printf("Loading FBX File: %s\n", inputPath);
}
if (!LoadFBXFile(raw, inputPath, {"png", "jpg", "jpeg"}, gltfOptions)) {
fmt::fprintf(stderr, "ERROR:: Failed to parse FBX: %s\n", inputPath);
return 1;

View File

@ -112,6 +112,9 @@ struct GltfOptions {
/** Whether to include lights through the KHR_punctual_lights extension. */
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. */
bool useBlendShapeNormals{false};
/** 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;
switch (options.animationFramerate) {
case AnimationFramerateOptions::BAKE24:
@ -816,7 +816,14 @@ static void ReadAnimations(RawModel& raw, FbxScene* pScene, const GltfOptions& o
bool hasMorphs = false;
RawChannel channel;
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++) {
FbxTime pTime;
@ -1068,15 +1075,7 @@ static void FindFbxTextures(
}
}
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);
static FbxScene* LoadScene(FbxManager* pManager, const std::string fbxFileName) {
FbxImporter* pImporter = FbxImporter::Create(pManager, "");
if (!pImporter->Initialize(fbxFileName.c_str(), -1, pManager->GetIOSettings())) {
@ -1084,8 +1083,7 @@ bool LoadFBXFile(
fmt::printf("%s\n", pImporter->GetStatus().GetErrorString());
}
pImporter->Destroy();
pManager->Destroy();
return false;
return nullptr;
}
FbxScene* pScene = FbxScene::Create(pManager, "fbxScene");
@ -1093,14 +1091,9 @@ bool LoadFBXFile(
pImporter->Destroy();
if (pScene == nullptr) {
pImporter->Destroy();
pManager->Destroy();
return false;
return nullptr;
}
std::map<const FbxTexture*, FbxString> textureLocations;
FindFbxTextures(pScene, fbxFileName, textureExtensions, textureLocations);
// Use Y up for glTF
FbxAxisSystem::MayaYUp.ConvertScene(pScene);
@ -1113,14 +1106,54 @@ bool LoadFBXFile(
if (sceneSystemUnit != FbxSystemUnit::cm) {
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.
scaleFactor = FbxSystemUnit::m.GetConversionFactorFrom(FbxSystemUnit::cm);
std::map<const FbxTexture*, FbxString> textureLocations;
FindFbxTextures(pScene, fbxFileName, textureExtensions, textureLocations);
ReadNodeHierarchy(raw, pScene, pScene->GetRootNode(), 0, "");
ReadNodeAttributes(raw, pScene, pScene->GetRootNode(), textureLocations);
ReadAnimations(raw, pScene, options);
ReadAnimations(raw, pScene, options, false);
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();
return true;

View File

@ -640,6 +640,15 @@ int RawModel::GetNodeById(const long nodeId) const {
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 {
for (size_t i = 0; i < surfaces.size(); i++) {
if (surfaces[i].id == surfaceId) {

View File

@ -508,6 +508,7 @@ class RawModel {
return nodes[index];
}
int GetNodeById(const long nodeId) const;
int GetNodeByName(const char* name) const;
// Create individual attribute arrays.
// Returns true if the vertices store the particular attribute.