From 26d4cc73e0c9ec3667aec35b5a0f196c0ea2b47a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 30 Mar 2017 15:16:23 -0700 Subject: [PATCH] add stubbed FBXBaker leveraging FBX SDK for read/write --- libraries/model-baking/CMakeLists.txt | 7 ++ libraries/model-baking/src/FBXBaker.cpp | 156 ++++++++++++++++++++++++ libraries/model-baking/src/FBXBaker.h | 38 ++++++ tools/oven/CMakeLists.txt | 2 +- 4 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 libraries/model-baking/CMakeLists.txt create mode 100644 libraries/model-baking/src/FBXBaker.cpp create mode 100644 libraries/model-baking/src/FBXBaker.h diff --git a/libraries/model-baking/CMakeLists.txt b/libraries/model-baking/CMakeLists.txt new file mode 100644 index 0000000000..45c0350bbe --- /dev/null +++ b/libraries/model-baking/CMakeLists.txt @@ -0,0 +1,7 @@ +set(TARGET_NAME model-baking) + +setup_hifi_library() + +find_package(FBXSDK REQUIRED) +target_link_libraries(${TARGET_NAME} ${FBX_LIBRARIES}) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${FBX_INCLUDE_DIR}) diff --git a/libraries/model-baking/src/FBXBaker.cpp b/libraries/model-baking/src/FBXBaker.cpp new file mode 100644 index 0000000000..af981b471e --- /dev/null +++ b/libraries/model-baking/src/FBXBaker.cpp @@ -0,0 +1,156 @@ +// +// FBXBaker.cpp +// libraries/model-baking/src +// +// Created by Stephen Birarda on 3/30/17. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include + +#include + +#include "FBXBaker.h" + +Q_LOGGING_CATEGORY(model_baking, "hifi.model-baking"); + +FBXBaker::FBXBaker(std::string fbxPath) : + _fbxPath(fbxPath) +{ + // create an FBX SDK manager + _sdkManager = FbxManager::Create(); +} + +bool FBXBaker::bakeFBX() { + + // load the scene from the FBX file + if (importScene()) { + // enumerate the textures found in the scene and bake them + rewriteAndCollectSceneTextures(); + } else { + return false; + } + + return true; +} + +bool FBXBaker::importScene() { + // create an FBX SDK importer + FbxImporter* importer = FbxImporter::Create(_sdkManager, ""); + + // import the FBX file at the given path + bool importStatus = importer->Initialize(_fbxPath.c_str()); + + if (!importStatus) { + // failed to import the FBX file, print an error and return + qCDebug(model_baking) << "Failed to import FBX file at" << _fbxPath.c_str() << "- error:" << importer->GetStatus().GetErrorString(); + + return false; + } + + // setup a new scene to hold the imported file + _scene = FbxScene::Create(_sdkManager, "bakeScene"); + + // import the file to the created scene + importer->Import(_scene); + + // destroy the importer that is no longer needed + importer->Destroy(); + + return true; +} + +bool FBXBaker::rewriteAndCollectSceneTextures() { + // grab the root node from the scene + FbxNode* rootNode = _scene->GetRootNode(); + + if (rootNode) { + // enumerate the children of the root node + for (int i = 0; i < rootNode->GetChildCount(); ++i) { + FbxNode* node = rootNode->GetChild(i); + + // check if this child is a mesh node + if (node->GetNodeAttribute()->GetAttributeType() == FbxNodeAttribute::eMesh) { + FbxMesh* mesh = (FbxMesh*) node->GetNodeAttribute(); + + // make sure this mesh is valid + if (mesh->GetNode() != nullptr) { + // figure out the number of materials in this mesh + int numMaterials = mesh->GetNode()->GetSrcObjectCount(); + + // enumerate the materials in this mesh + for (int materialIndex = 0; materialIndex < numMaterials; materialIndex++) { + // grab this material + FbxSurfaceMaterial* material = mesh->GetNode()->GetSrcObject(materialIndex); + + if (material) { + // enumerate the textures in this valid material + int textureIndex; + FBXSDK_FOR_EACH_TEXTURE(textureIndex) { + // collect this texture so we know later to bake it + FbxProperty property = material->FindProperty(FbxLayerElement::sTextureChannelNames[textureIndex]); + if (property.IsValid()) { + rewriteAndCollectChannelTextures(property); + } + } + + } + } + + } + } + } + } + + return true; +} + +bool FBXBaker::rewriteAndCollectChannelTextures(FbxProperty& property) { + if (property.IsValid()) { + int textureCount = property.GetSrcObjectCount(); + + // enumerate the textures for this channel + for (int i = 0; i < textureCount; ++i) { + // check if this texture is layered + FbxLayeredTexture* layeredTexture = property.GetSrcObject(i); + if (layeredTexture) { + // enumerate the layers of the layered texture + int numberOfLayers = layeredTexture->GetSrcObjectCount(); + + for (int j = 0; j < numberOfLayers; ++j) { + FbxTexture* texture = layeredTexture->GetSrcObject(j); + rewriteAndCollectTexture(texture); + } + } else { + FbxTexture* texture = property.GetSrcObject(i); + rewriteAndCollectTexture(texture); + } + } + } + + return true; +} + +static const QString BAKED_TEXTURE_DIRECTORY = "textures"; + +bool FBXBaker::rewriteAndCollectTexture(fbxsdk::FbxTexture* texture) { + FbxFileTexture* fileTexture = FbxCast(texture); + if (fileTexture) { + qCDebug(model_baking) << "Flagging" << fileTexture->GetRelativeFileName() << "for bake and re-mapping to .xtk in FBX"; + + // use QFileInfo to easily split up the existing texture filename into its components + QFileInfo textureFileInfo { fileTexture->GetRelativeFileName() }; + + // construct the new baked texture file name + QString bakedTextureFileName { BAKED_TEXTURE_DIRECTORY + "/" + textureFileInfo.baseName() + ".xtk" }; + + // write the new filename into the FBX scene + fileTexture->SetRelativeFileName(bakedTextureFileName.toLocal8Bit()); + } + + return true; +} diff --git a/libraries/model-baking/src/FBXBaker.h b/libraries/model-baking/src/FBXBaker.h new file mode 100644 index 0000000000..4ebea6f2fa --- /dev/null +++ b/libraries/model-baking/src/FBXBaker.h @@ -0,0 +1,38 @@ +// +// FBXBaker.h +// libraries/model-baking/src +// +// Created by Stephen Birarda on 3/30/17. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_FBXBaker_h +#define hifi_FBXBaker_h + +#include + +#include + +Q_DECLARE_LOGGING_CATEGORY(model_baking) + +class FBXBaker { + +public: + FBXBaker(std::string fbxPath); + bool bakeFBX(); + +private: + bool importScene(); + bool rewriteAndCollectSceneTextures(); + bool rewriteAndCollectChannelTextures(FbxProperty& property); + bool rewriteAndCollectTexture(FbxTexture* texture); + + std::string _fbxPath; + FbxManager* _sdkManager; + FbxScene* _scene { nullptr }; +}; + +#endif // hifi_FBXBaker_h diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 5244b68217..473fa707f1 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -2,4 +2,4 @@ set(TARGET_NAME oven) setup_hifi_project() -find_package(FBXSDK REQUIRED) +link_hifi_libraries(model-baking)