import multiple animation files

This commit is contained in:
Simon 2019-04-13 15:30:55 +01:00
parent 13f463d336
commit 47001c9ce3
7 changed files with 82 additions and 18 deletions

View File

@ -47,6 +47,7 @@ Usage:
from the FBX. from the FBX.
--khr-materials-unlit Use KHR_materials_unlit extension to specify --khr-materials-unlit Use KHR_materials_unlit extension to specify
Unlit shader. Unlit shader.
--animation-files Read multiple fbx animation files.
--blend-shape-normals Include blend shape normals, if reported --blend-shape-normals Include blend shape normals, if reported
present by the FBX SDK. present by the FBX SDK.
--blend-shape-tangents Include blend shape tangents, if reported --blend-shape-tangents Include blend shape tangents, if reported
@ -75,6 +76,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

@ -125,6 +125,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,
@ -302,7 +307,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.c_str(), "png;jpg;jpeg")) { if (!LoadFBXFile(raw, inputPath.c_str(), "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

@ -95,6 +95,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

@ -721,7 +721,7 @@ static void ReadNodeHierarchy(
} }
} }
static void ReadAnimations(RawModel& raw, FbxScene* pScene) { static void ReadAnimations(RawModel& raw, FbxScene* pScene, bool nodeNameLookup) {
FbxTime::EMode eMode = FbxTime::eFrames24; FbxTime::EMode eMode = FbxTime::eFrames24;
const double epsilon = 1e-5f; const double epsilon = 1e-5f;
@ -772,7 +772,14 @@ static void ReadAnimations(RawModel& raw, FbxScene* pScene) {
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;
@ -996,11 +1003,7 @@ static void FindFbxTextures(
} }
} }
bool LoadFBXFile(RawModel& raw, const char* fbxFileName, const char* textureExtensions) { static FbxScene* LoadScene(FbxManager* pManager, const char* fbxFileName) {
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, -1, pManager->GetIOSettings())) { if (!pImporter->Initialize(fbxFileName, -1, pManager->GetIOSettings())) {
@ -1008,8 +1011,7 @@ bool LoadFBXFile(RawModel& raw, const char* fbxFileName, const char* textureExte
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");
@ -1017,14 +1019,9 @@ bool LoadFBXFile(RawModel& raw, const char* fbxFileName, const char* textureExte
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);
@ -1037,14 +1034,54 @@ bool LoadFBXFile(RawModel& raw, const char* fbxFileName, const char* textureExte
if (sceneSystemUnit != FbxSystemUnit::cm) { if (sceneSystemUnit != FbxSystemUnit::cm) {
FbxSystemUnit::cm.ConvertScene(pScene); FbxSystemUnit::cm.ConvertScene(pScene);
} }
return pScene;
}
bool LoadFBXFile(
RawModel& raw,
const char* fbxFileName,
const char* 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); ReadAnimations(raw, pScene, 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, true);
pAnimationScene->Destroy();
animationFileNumber++;
} else {
readAnimationFiles = false;
}
}
pManager->Destroy(); pManager->Destroy();
return true; return true;

View File

@ -11,6 +11,10 @@
#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,
const GltfOptions& options);
json TranscribeProperty(FbxProperty& prop); json TranscribeProperty(FbxProperty& prop);

View File

@ -641,6 +641,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

@ -509,6 +509,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.