From 711938fb3d6702a95796be2e90867c07c5d9d9d3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 5 Apr 2017 11:20:05 -0700 Subject: [PATCH] lay async foundation for FBXBaker --- libraries/model-baking/src/FBXBaker.cpp | 197 +++++++++++++----------- libraries/model-baking/src/FBXBaker.h | 39 +++-- tools/oven/src/main.cpp | 6 + 3 files changed, 138 insertions(+), 104 deletions(-) diff --git a/libraries/model-baking/src/FBXBaker.cpp b/libraries/model-baking/src/FBXBaker.cpp index e4de73b991..41fa0e1a12 100644 --- a/libraries/model-baking/src/FBXBaker.cpp +++ b/libraries/model-baking/src/FBXBaker.cpp @@ -13,29 +13,44 @@ #include #include +#include #include "FBXBaker.h" Q_LOGGING_CATEGORY(model_baking, "hifi.model-baking"); -FBXBaker::FBXBaker(std::string fbxPath) : +FBXBaker::FBXBaker(QUrl fbxPath) : _fbxPath(fbxPath) { // create an FBX SDK manager _sdkManager = FbxManager::Create(); } -bool FBXBaker::bakeFBX() { +FBXBaker::~FBXBaker() { + _sdkManager->Destroy(); +} - // load the scene from the FBX file - if (importScene()) { - // enumerate the textures found in the scene and bake them - rewriteAndCollectSceneTextures(); +void FBXBaker::start() { + // check if the FBX is local or first needs to be downloaded + if (_fbxPath.isLocalFile()) { + // local file, bake now + bake(); } else { - return false; + // remote file, kick off a download } +} - return true; +void FBXBaker::bake() { + // (1) load the scene from the FBX file + // (2) enumerate the textures found in the scene and bake them + // (3) export the FBX with re-written texture references + // (4) enumerate the collected texture paths and bake the textures + + // a failure at any step along the way stops the chain + importScene() && rewriteAndCollectSceneTextures() && exportScene() && bakeTextures(); + + // emit a signal saying that we are done, with whatever errors were produced + emit finished(_errorList); } bool FBXBaker::importScene() { @@ -43,11 +58,11 @@ bool FBXBaker::importScene() { FbxImporter* importer = FbxImporter::Create(_sdkManager, ""); // import the FBX file at the given path - bool importStatus = importer->Initialize(_fbxPath.c_str()); + bool importStatus = importer->Initialize(_fbxPath.toLocalFile().toLocal8Bit().data()); 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(); + // failed to initialize importer, print an error and return + qCDebug(model_baking) << "Failed to import FBX file at" << _fbxPath << "- error:" << importer->GetStatus().GetErrorString(); return false; } @@ -64,93 +79,89 @@ bool FBXBaker::importScene() { 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"; -static const QString BAKED_TEXTURE_EXT = ".xtk"; +static const QString BAKED_TEXTURE_EXT = ".ktx"; -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"; +static const QString EXPORT_PATH { "/Users/birarda/code/hifi/lod/test-oven/export/DiscGolfBasket.ktx.fbx" }; - // use QFileInfo to easily split up the existing texture filename into its components - QFileInfo textureFileInfo { fileTexture->GetRelativeFileName() }; +bool FBXBaker::rewriteAndCollectSceneTextures() { + // get a count of the textures used in the scene + int numTextures = _scene->GetTextureCount(); - // construct the new baked texture file name - QString bakedTextureFileName { BAKED_TEXTURE_DIRECTORY + "/" + textureFileInfo.baseName() + BAKED_TEXTURE_EXT }; + // enumerate the textures in the scene + for (int i = 0; i < numTextures; i++) { + // grab each file texture + FbxFileTexture* fileTexture = FbxCast(_scene->GetTexture(i)); - // write the new filename into the FBX scene - fileTexture->SetRelativeFileName(bakedTextureFileName.toLocal8Bit()); + if (fileTexture) { + // use QFileInfo to easily split up the existing texture filename into its components + QFileInfo textureFileInfo { fileTexture->GetFileName() }; + + // make sure this texture points to something + if (!textureFileInfo.filePath().isEmpty()) { + + // construct the new baked texture file name and file path + QString bakedTextureFileName { textureFileInfo.baseName() + BAKED_TEXTURE_EXT }; + QString bakedTextureFilePath { QFileInfo(EXPORT_PATH).absolutePath() + "/textures/" + bakedTextureFileName }; + + qCDebug(model_baking).noquote() << "Re-mapping" << fileTexture->GetFileName() << "to" << bakedTextureFilePath; + + // write the new filename into the FBX scene + fileTexture->SetFileName(bakedTextureFilePath.toLocal8Bit()); + + // add the texture to the list of textures needing to be baked + if (textureFileInfo.exists() && textureFileInfo.isFile()) { + // append the URL to the local texture that we have confirmed exists + _unbakedTextures.append(QUrl::fromLocalFile(textureFileInfo.absoluteFilePath())); + } else { + + } + } + } + } + + return true; +} + +bool FBXBaker::exportScene() { + // setup the exporter + FbxExporter* exporter = FbxExporter::Create(_sdkManager, ""); + + bool exportStatus = exporter->Initialize(EXPORT_PATH.toLocal8Bit().data()); + + if (!exportStatus) { + // failed to initialize exporter, print an error and return + qCDebug(model_baking) << "Failed to export FBX file at" << _fbxPath + << "to" << EXPORT_PATH << "- error:" << exporter->GetStatus().GetErrorString(); + + return false; + } + + // export the scene + exporter->Export(_scene); + + return true; +} + +bool FBXBaker::bakeTextures() { + // enumerate the list of unbaked textures + foreach(const QUrl& textureUrl, _unbakedTextures) { + qCDebug(model_baking) << "Baking texture at" << textureUrl; + + if (textureUrl.isLocalFile()) { + // this is a local file that we've already determined is available on the filesystem + + // load the file + QFile localTexture { textureUrl.toLocalFile() }; + + if (!localTexture.open(QIODevice::ReadOnly)) { + // add an error to the list stating that this texture couldn't be baked because it could not be loaded + } + + // call the image library to produce a compressed KTX for this image + } else { + // this is a remote texture that we'll need to download first + } } return true; diff --git a/libraries/model-baking/src/FBXBaker.h b/libraries/model-baking/src/FBXBaker.h index 4ebea6f2fa..0ae8311522 100644 --- a/libraries/model-baking/src/FBXBaker.h +++ b/libraries/model-baking/src/FBXBaker.h @@ -12,27 +12,44 @@ #ifndef hifi_FBXBaker_h #define hifi_FBXBaker_h -#include - #include +#include Q_DECLARE_LOGGING_CATEGORY(model_baking) -class FBXBaker { +namespace fbxsdk { + class FbxManager; + class FbxProperty; + class FbxScene; + class FbxTexture; +} +class FBXBaker : public QObject { + Q_OBJECT public: - FBXBaker(std::string fbxPath); - bool bakeFBX(); + FBXBaker(QUrl fbxPath); + ~FBXBaker(); + + void start(); + +signals: + void finished(QStringList errorList); private: + void bake(); bool importScene(); bool rewriteAndCollectSceneTextures(); - bool rewriteAndCollectChannelTextures(FbxProperty& property); - bool rewriteAndCollectTexture(FbxTexture* texture); - - std::string _fbxPath; - FbxManager* _sdkManager; - FbxScene* _scene { nullptr }; + bool exportScene(); + bool bakeTextures(); + bool bakeTexture(); + + QUrl _fbxPath; + fbxsdk::FbxManager* _sdkManager; + fbxsdk::FbxScene* _scene { nullptr }; + + QStringList _errorList; + + QList _unbakedTextures; }; #endif // hifi_FBXBaker_h diff --git a/tools/oven/src/main.cpp b/tools/oven/src/main.cpp index 831ba00328..e74df068dd 100644 --- a/tools/oven/src/main.cpp +++ b/tools/oven/src/main.cpp @@ -8,7 +8,13 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +#include + int main (int argc, char** argv) { + + FBXBaker baker(QUrl("file:///Users/birarda/code/hifi/lod/test-oven/DiscGolfBasket.fbx")); + baker.start(); + return 0; }