From efc9f993f59db17556049aa9cdcfe40136fa2359 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 7 Mar 2019 17:39:19 -0800 Subject: [PATCH] Add FSTBaker, and make ModelBaker output an FST Restore feature to look for baked model file in other oven directory --- libraries/baking/src/Baker.h | 2 +- libraries/baking/src/FBXBaker.cpp | 11 ++ libraries/baking/src/FBXBaker.h | 3 +- libraries/baking/src/ModelBaker.cpp | 98 ++++++++++---- libraries/baking/src/ModelBaker.h | 15 ++- libraries/baking/src/baking/BakerLibrary.cpp | 15 ++- libraries/baking/src/baking/BakerLibrary.h | 9 +- libraries/baking/src/baking/FSTBaker.cpp | 128 +++++++++++++++++++ libraries/baking/src/baking/FSTBaker.h | 45 +++++++ libraries/fbx/src/FSTReader.h | 2 + tools/oven/src/DomainBaker.cpp | 20 +-- tools/oven/src/DomainBaker.h | 1 - 12 files changed, 301 insertions(+), 48 deletions(-) create mode 100644 libraries/baking/src/baking/FSTBaker.cpp create mode 100644 libraries/baking/src/baking/FSTBaker.h diff --git a/libraries/baking/src/Baker.h b/libraries/baking/src/Baker.h index c1b2ddf959..611f992c96 100644 --- a/libraries/baking/src/Baker.h +++ b/libraries/baking/src/Baker.h @@ -52,7 +52,7 @@ protected: void handleErrors(const QStringList& errors); // List of baked output files. For instance, for an FBX this would - // include the .fbx and all of its texture files. + // include the .fbx, a .fst pointing to the fbx, and all of the fbx texture files. std::vector _outputFiles; QStringList _errorList; diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index e1bb86d051..d2dc86c783 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -37,6 +37,17 @@ #include "FBXToJSON.h" #endif +FBXBaker::FBXBaker(const QUrl& inputModelURL, TextureBakerThreadGetter inputTextureThreadGetter, + const QString& bakedOutputDirectory, const QString& originalOutputDirectory, bool hasBeenBaked) : + ModelBaker(inputModelURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory, hasBeenBaked) { + if (hasBeenBaked) { + // Look for the original model file one directory higher. Perhaps this is an oven output directory. + QUrl originalRelativePath = QUrl("../original/" + inputModelURL.fileName().replace(BAKED_FBX_EXTENSION, FBX_EXTENSION)); + QUrl newInputModelURL = inputModelURL.adjusted(QUrl::RemoveFilename).resolved(originalRelativePath); + _modelURL = newInputModelURL; + } +} + void FBXBaker::bakeProcessedSource(const hfm::Model::Pointer& hfmModel, const std::vector& dracoMeshes, const std::vector>& dracoMaterialLists) { _hfmModel = hfmModel; // Load the root node from the FBX file diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 7770e3014d..f8a023f431 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -31,7 +31,8 @@ using TextureBakerThreadGetter = std::function; class FBXBaker : public ModelBaker { Q_OBJECT public: - using ModelBaker::ModelBaker; + FBXBaker(const QUrl& inputModelURL, TextureBakerThreadGetter inputTextureThreadGetter, + const QString& bakedOutputDirectory, const QString& originalOutputDirectory = "", bool hasBeenBaked = false); protected: virtual void bakeProcessedSource(const hfm::Model::Pointer& hfmModel, const std::vector& dracoMeshes, const std::vector>& dracoMaterialLists) override; diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index 6568850c1f..c80df2db2e 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -21,6 +21,7 @@ #include #include +#include #ifdef _WIN32 #pragma warning( push ) @@ -61,12 +62,20 @@ ModelBaker::ModelBaker(const QUrl& inputModelURL, TextureBakerThreadGetter input qDebug() << "Made temporary dir " << _modelTempDir; qDebug() << "Origin file path: " << _originalModelFilePath; + { + auto bakedFilename = _modelURL.fileName(); + if (!hasBeenBaked) { + bakedFilename = bakedFilename.left(bakedFilename.lastIndexOf('.')); + bakedFilename += BAKED_FBX_EXTENSION; + } + _bakedModelURL = _bakedOutputDir + "/" + bakedFilename; + } } ModelBaker::~ModelBaker() { if (_modelTempDir.exists()) { if (!_modelTempDir.remove(_originalModelFilePath)) { - qCWarning(model_baking) << "Failed to remove temporary copy of fbx file:" << _originalModelFilePath; + qCWarning(model_baking) << "Failed to remove temporary copy of model file:" << _originalModelFilePath; } if (!_modelTempDir.rmdir(".")) { qCWarning(model_baking) << "Failed to remove temporary directory:" << _modelTempDir; @@ -74,6 +83,26 @@ ModelBaker::~ModelBaker() { } } +void ModelBaker::setOutputURLSuffix(const QUrl& outputURLSuffix) { + _outputURLSuffix = outputURLSuffix; +} + +void ModelBaker::setMappingURL(const QUrl& mappingURL) { + _mappingURL = mappingURL; +} + +void ModelBaker::setMapping(const hifi::VariantHash& mapping) { + _mapping = mapping; +} + +QUrl ModelBaker::getFullOutputMappingURL() const { + QUrl appendedURL = _outputMappingURL; + appendedURL.setFragment(_outputURLSuffix.fragment()); + appendedURL.setQuery(_outputURLSuffix.query()); + appendedURL.setUserInfo(_outputURLSuffix.userInfo()); + return appendedURL; +} + void ModelBaker::bake() { qDebug() << "ModelBaker" << _modelURL << "bake starting"; @@ -92,19 +121,24 @@ void ModelBaker::bake() { void ModelBaker::initializeOutputDirs() { // Attempt to make the output folders - // Warn if there is an output directory using the same name + // Warn if there is an output directory using the same name, unless we know a parent FST baker created them already if (QDir(_bakedOutputDir).exists()) { - qWarning() << "Output path" << _bakedOutputDir << "already exists. Continuing."; + if (_mappingURL.isEmpty()) { + qWarning() << "Output path" << _bakedOutputDir << "already exists. Continuing."; + } } else { qCDebug(model_baking) << "Creating baked output folder" << _bakedOutputDir; if (!QDir().mkpath(_bakedOutputDir)) { handleError("Failed to create baked output folder " + _bakedOutputDir); + return; } } if (QDir(_originalOutputDir).exists()) { - qWarning() << "Output path" << _originalOutputDir << "already exists. Continuing."; + if (_mappingURL.isEmpty()) { + qWarning() << "Output path" << _originalOutputDir << "already exists. Continuing."; + } } else { qCDebug(model_baking) << "Creating original output folder" << _originalOutputDir; if (!QDir().mkpath(_originalOutputDir)) { @@ -122,7 +156,7 @@ void ModelBaker::saveSourceModel() { qDebug() << "Local file url: " << _modelURL << _modelURL.toString() << _modelURL.toLocalFile() << ", copying to: " << _originalModelFilePath; if (!localModelURL.exists()) { - //QMessageBox::warning(this, "Could not find " + _fbxURL.toString(), ""); + //QMessageBox::warning(this, "Could not find " + _modelURL.toString(), ""); handleError("Could not find " + _modelURL.toString()); return; } @@ -135,7 +169,7 @@ void ModelBaker::saveSourceModel() { localModelURL.copy(_originalModelFilePath); - // emit our signal to start the import of the FBX source copy + // emit our signal to start the import of the model source copy emit modelLoaded(); } else { // remote file, kick off a download @@ -214,11 +248,11 @@ void ModelBaker::bakeSourceCopy() { handleError("Could not recognize file type of model file " + _originalModelFilePath); return; } - hifi::VariantHash mapping; - mapping["combineParts"] = true; // set true so that OBJSerializer reads material info from material library - hfm::Model::Pointer loadedModel = serializer->read(modelData, mapping, _modelURL); + hifi::VariantHash serializerMapping = _mapping; + serializerMapping["combineParts"] = true; // set true so that OBJSerializer reads material info from material library + hfm::Model::Pointer loadedModel = serializer->read(modelData, serializerMapping, _modelURL); - baker::Baker baker(loadedModel, mapping); + baker::Baker baker(loadedModel, serializerMapping); auto config = baker.getConfiguration(); // Enable compressed draco mesh generation config->getJobConfig("BuildDracoMesh")->setEnabled(true); @@ -269,6 +303,32 @@ void ModelBaker::bakeSourceCopy() { return; } + // Output FST file, copying over input mappings if available + QString outputFSTFilename = !_mappingURL.isEmpty() ? _mappingURL.fileName() : _modelURL.fileName(); + auto extensionStart = outputFSTFilename.indexOf("."); + if (extensionStart != -1) { + outputFSTFilename.resize(extensionStart); + } + outputFSTFilename += ".baked.fst"; + QString outputFSTURL = _bakedOutputDir + "/" + outputFSTFilename; + + auto outputMapping = _mapping; + outputMapping[FST_VERSION_FIELD] = FST_VERSION; + outputMapping[FILENAME_FIELD] = _bakedModelURL.fileName(); + hifi::ByteArray fstOut = FSTReader::writeMapping(outputMapping); + + QFile fstOutputFile { outputFSTURL }; + if (!fstOutputFile.open(QIODevice::WriteOnly)) { + handleError("Failed to open file '" + outputFSTURL + "' for writing"); + return; + } + if (fstOutputFile.write(fstOut) == -1) { + handleError("Failed to write to file '" + outputFSTURL + "'"); + return; + } + _outputFiles.push_back(outputFSTURL); + _outputMappingURL = outputFSTURL; + // check if we're already done with textures (in case we had none to re-write) checkIfTexturesFinished(); } @@ -657,31 +717,25 @@ void ModelBaker::embedTextureMetaData() { } void ModelBaker::exportScene() { - // save the relative path to this FBX inside our passed output folder - auto fileName = _modelURL.fileName(); - auto baseName = fileName.left(fileName.lastIndexOf('.')); - auto bakedFilename = baseName + BAKED_FBX_EXTENSION; - - _bakedModelFilePath = _bakedOutputDir + "/" + bakedFilename; - auto fbxData = FBXWriter::encodeFBX(_rootNode); - QFile bakedFile(_bakedModelFilePath); + QString bakedModelURL = _bakedModelURL.toString(); + QFile bakedFile(bakedModelURL); if (!bakedFile.open(QIODevice::WriteOnly)) { - handleError("Error opening " + _bakedModelFilePath + " for writing"); + handleError("Error opening " + bakedModelURL + " for writing"); return; } bakedFile.write(fbxData); - _outputFiles.push_back(_bakedModelFilePath); + _outputFiles.push_back(bakedModelURL); #ifdef HIFI_DUMP_FBX { FBXToJSON fbxToJSON; fbxToJSON << _rootNode; - QFileInfo modelFile(_bakedModelFilePath); + QFileInfo modelFile(_bakedModelURL.toString()); QString outFilename(modelFile.dir().absolutePath() + "/" + modelFile.completeBaseName() + "_FBX.json"); QFile jsonFile(outFilename); if (jsonFile.open(QIODevice::WriteOnly)) { @@ -691,5 +745,5 @@ void ModelBaker::exportScene() { } #endif - qCDebug(model_baking) << "Exported" << _modelURL << "with re-written paths to" << _bakedModelFilePath; + qCDebug(model_baking) << "Exported" << _modelURL << "with re-written paths to" << bakedModelURL; } diff --git a/libraries/baking/src/ModelBaker.h b/libraries/baking/src/ModelBaker.h index b0bd3798ff..f1ef6db56d 100644 --- a/libraries/baking/src/ModelBaker.h +++ b/libraries/baking/src/ModelBaker.h @@ -45,6 +45,10 @@ public: const QString& bakedOutputDirectory, const QString& originalOutputDirectory = "", bool hasBeenBaked = false); virtual ~ModelBaker(); + void setOutputURLSuffix(const QUrl& urlSuffix); + void setMappingURL(const QUrl& mappingURL); + void setMapping(const hifi::VariantHash& mapping); + void initializeOutputDirs(); bool buildDracoMeshNode(FBXNode& dracoMeshNode, const QByteArray& dracoMeshBytes, const std::vector& dracoMaterialList); @@ -52,7 +56,8 @@ public: virtual void setWasAborted(bool wasAborted) override; QUrl getModelURL() const { return _modelURL; } - QString getBakedModelFilePath() const { return _bakedModelFilePath; } + virtual QUrl getFullOutputMappingURL() const; + QUrl getBakedModelURL() const { return _bakedModelURL; } signals: void modelLoaded(); @@ -72,9 +77,14 @@ protected: FBXNode _rootNode; QHash _textureContentMap; QUrl _modelURL; + QUrl _outputURLSuffix; + QUrl _mappingURL; + hifi::VariantHash _mapping; QString _bakedOutputDir; QString _originalOutputDir; - QString _bakedModelFilePath; + TextureBakerThreadGetter _textureThreadGetter; + QString _outputMappingURL; + QUrl _bakedModelURL; QDir _modelTempDir; QString _originalModelFilePath; @@ -93,7 +103,6 @@ private: const QString & bakedFilename, const QByteArray & textureContent); QString texturePathRelativeToModel(QUrl modelURL, QUrl textureURL); - TextureBakerThreadGetter _textureThreadGetter; QMultiHash> _bakingTextures; QHash _textureNameMatchCount; QHash _remappedTexturePaths; diff --git a/libraries/baking/src/baking/BakerLibrary.cpp b/libraries/baking/src/baking/BakerLibrary.cpp index 202fd4b3d8..c95745146b 100644 --- a/libraries/baking/src/baking/BakerLibrary.cpp +++ b/libraries/baking/src/baking/BakerLibrary.cpp @@ -11,6 +11,7 @@ #include "BakerLibrary.h" +#include "FSTBaker.h" #include "../FBXBaker.h" #include "../OBJBaker.h" @@ -51,22 +52,24 @@ std::unique_ptr getModelBaker(const QUrl& bakeableModelURL, TextureB QString bakedOutputDirectory = contentOutputPath + subDirName + "/baked"; QString originalOutputDirectory = contentOutputPath + subDirName + "/original"; + return getModelBakerWithOutputDirectories(bakeableModelURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory); +} + +std::unique_ptr getModelBakerWithOutputDirectories(const QUrl& bakeableModelURL, TextureBakerThreadGetter inputTextureThreadGetter, const QString& bakedOutputDirectory, const QString& originalOutputDirectory) { + auto filename = bakeableModelURL.fileName(); + std::unique_ptr baker; if (filename.endsWith(FST_EXTENSION, Qt::CaseInsensitive)) { - //baker = std::make_unique(bakeableModelURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory, filename.endsWith(BAKED_FST_EXTENSION, Qt::CaseInsensitive)); + baker = std::make_unique(bakeableModelURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory, filename.endsWith(BAKED_FST_EXTENSION, Qt::CaseInsensitive)); } else if (filename.endsWith(FBX_EXTENSION, Qt::CaseInsensitive)) { baker = std::make_unique(bakeableModelURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory, filename.endsWith(BAKED_FBX_EXTENSION, Qt::CaseInsensitive)); } else if (filename.endsWith(OBJ_EXTENSION, Qt::CaseInsensitive)) { baker = std::make_unique(bakeableModelURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory); - } else if (filename.endsWith(GLTF_EXTENSION, Qt::CaseInsensitive)) { + //} else if (filename.endsWith(GLTF_EXTENSION, Qt::CaseInsensitive)) { //baker = std::make_unique(bakeableModelURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory); } else { qDebug() << "Could not create ModelBaker for url" << bakeableModelURL; } - if (baker) { - QDir(contentOutputPath).mkpath(subDirName); - } - return baker; } diff --git a/libraries/baking/src/baking/BakerLibrary.h b/libraries/baking/src/baking/BakerLibrary.h index e77463b502..57197b53fd 100644 --- a/libraries/baking/src/baking/BakerLibrary.h +++ b/libraries/baking/src/baking/BakerLibrary.h @@ -16,13 +16,14 @@ #include "../ModelBaker.h" -// Returns either the given model URL, or, if the model is baked and shouldRebakeOriginals is true, -// the guessed location of the original model -// Returns an empty URL if no bakeable URL found +// Returns either the given model URL if valid, or an empty URL QUrl getBakeableModelURL(const QUrl& url); // Assuming the URL is valid, gets the appropriate baker for the given URL, and creates the base directory where the baker's output will later be stored // Returns an empty pointer if a baker could not be created std::unique_ptr getModelBaker(const QUrl& bakeableModelURL, TextureBakerThreadGetter inputTextureThreadGetter, const QString& contentOutputPath); -#endif hifi_BakerLibrary_h +// Similar to getModelBaker, but gives control over where the output folders will be +std::unique_ptr getModelBakerWithOutputDirectories(const QUrl& bakeableModelURL, TextureBakerThreadGetter inputTextureThreadGetter, const QString& bakedOutputDirectory, const QString& originalOutputDirectory); + +#endif // hifi_BakerLibrary_h diff --git a/libraries/baking/src/baking/FSTBaker.cpp b/libraries/baking/src/baking/FSTBaker.cpp new file mode 100644 index 0000000000..f76180bb58 --- /dev/null +++ b/libraries/baking/src/baking/FSTBaker.cpp @@ -0,0 +1,128 @@ +// +// FSTBaker.cpp +// libraries/baking/src/baking +// +// Created by Sabrina Shanman on 2019/03/06. +// Copyright 2019 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 "FSTBaker.h" + +#include +#include + +#include "BakerLibrary.h" + +#include + +FSTBaker::FSTBaker(const QUrl& inputMappingURL, TextureBakerThreadGetter inputTextureThreadGetter, + const QString& bakedOutputDirectory, const QString& originalOutputDirectory, bool hasBeenBaked) : + ModelBaker(inputMappingURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory, hasBeenBaked) { + if (hasBeenBaked) { + // Look for the original model file one directory higher. Perhaps this is an oven output directory. + QUrl originalRelativePath = QUrl("../original/" + inputMappingURL.fileName().replace(BAKED_FST_EXTENSION, FST_EXTENSION)); + QUrl newInputMappingURL = inputMappingURL.adjusted(QUrl::RemoveFilename).resolved(originalRelativePath); + _modelURL = newInputMappingURL; + } + _mappingURL = _modelURL; + + { + // Unused, but defined for consistency + auto bakedFilename = _modelURL.fileName(); + bakedFilename.replace(FST_EXTENSION, BAKED_FST_EXTENSION); + _bakedModelURL = _bakedOutputDir + "/" + bakedFilename; + } +} + +QUrl FSTBaker::getFullOutputMappingURL() const { + if (_modelBaker) { + return _modelBaker->getFullOutputMappingURL(); + } + return QUrl(); +} + +void FSTBaker::bakeSourceCopy() { + if (shouldStop()) { + return; + } + + QFile fstFile(_originalModelFilePath); + if (!fstFile.open(QIODevice::ReadOnly)) { + handleError("Error opening " + _originalModelFilePath + " for reading"); + return; + } + + hifi::ByteArray fstData = fstFile.readAll(); + _mapping = FSTReader::readMapping(fstData); + + auto filenameField = _mapping[FILENAME_FIELD].toString(); + if (filenameField.isEmpty()) { + handleError("The '" + FILENAME_FIELD + "' property in the FST file '" + _originalModelFilePath + "' could not be found"); + return; + } + auto modelURL = _mappingURL.adjusted(QUrl::RemoveFilename).resolved(filenameField); + auto bakeableModelURL = getBakeableModelURL(modelURL); + if (bakeableModelURL.isEmpty()) { + handleError("The '" + FILENAME_FIELD + "' property in the FST file '" + _originalModelFilePath + "' could not be resolved to a valid bakeable model url"); + return; + } + + auto baker = getModelBakerWithOutputDirectories(bakeableModelURL, _textureThreadGetter, _bakedOutputDir, _originalOutputDir); + _modelBaker = std::unique_ptr(dynamic_cast(baker.release())); + if (!_modelBaker) { + handleError("The model url '" + bakeableModelURL.toString() + "' from the FST file '" + _originalModelFilePath + "' (property: '" + FILENAME_FIELD + "') could not be used to initialize a valid model baker"); + return; + } + if (dynamic_cast(_modelBaker.get())) { + // Could be interesting, but for now let's just prevent infinite FST loops in the most straightforward way possible + handleError("The FST file '" + _originalModelFilePath + "' (property: '" + FILENAME_FIELD + "') references another FST file. FST chaining is not supported."); + return; + } + _modelBaker->setMappingURL(_mappingURL); + _modelBaker->setMapping(_mapping); + // Hold on to the old url userinfo/query/fragment data so ModelBaker::getFullOutputMappingURL retains that data from the original model URL + _modelBaker->setOutputURLSuffix(modelURL); + + connect(_modelBaker.get(), &ModelBaker::aborted, this, &FSTBaker::handleModelBakerAborted); + connect(_modelBaker.get(), &ModelBaker::finished, this, &FSTBaker::handleModelBakerFinished); + + // FSTBaker can't do much while waiting for the ModelBaker to finish, so start the bake on this thread. + _modelBaker->bake(); +} + +void FSTBaker::handleModelBakerEnded() { + for (auto& warning : _modelBaker->getWarnings()) { + _warningList.push_back(warning); + } + for (auto& error : _modelBaker->getErrors()) { + _errorList.push_back(error); + } + + // Get the output files, including but not limited to the FST file and the baked model file + for (auto& outputFile : _modelBaker->getOutputFiles()) { + _outputFiles.push_back(outputFile); + } + +} + +void FSTBaker::handleModelBakerAborted() { + handleModelBakerEnded(); + if (!wasAborted()) { + setWasAborted(true); + } +} + +void FSTBaker::handleModelBakerFinished() { + handleModelBakerEnded(); + setIsFinished(true); +} + +void FSTBaker::abort() { + ModelBaker::abort(); + if (_modelBaker) { + _modelBaker->abort(); + } +} diff --git a/libraries/baking/src/baking/FSTBaker.h b/libraries/baking/src/baking/FSTBaker.h new file mode 100644 index 0000000000..aeb7286af3 --- /dev/null +++ b/libraries/baking/src/baking/FSTBaker.h @@ -0,0 +1,45 @@ +// +// FSTBaker.h +// libraries/baking/src/baking +// +// Created by Sabrina Shanman on 2019/03/06. +// Copyright 2019 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_FSTBaker_h +#define hifi_FSTBaker_h + +#include "../ModelBaker.h" + +class FSTBaker : public ModelBaker { + Q_OBJECT + +public: + FSTBaker(const QUrl& inputMappingURL, TextureBakerThreadGetter inputTextureThreadGetter, + const QString& bakedOutputDirectory, const QString& originalOutputDirectory = "", bool hasBeenBaked = false); + + virtual QUrl getFullOutputMappingURL() const; + +signals: + void fstLoaded(); + +public slots: + virtual void abort() override; + +protected: + std::unique_ptr _modelBaker; + +protected slots: + virtual void bakeSourceCopy() override; + virtual void bakeProcessedSource(const hfm::Model::Pointer& hfmModel, const std::vector& dracoMeshes, const std::vector>& dracoMaterialLists) override {}; + void handleModelBakerAborted(); + void handleModelBakerFinished(); + +private: + void handleModelBakerEnded(); +}; + +#endif // hifi_FSTBaker_h diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index ad952c4ed7..fade0fa5bc 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -15,6 +15,8 @@ #include #include +static const unsigned int FST_VERSION = 1; +static const QString FST_VERSION_FIELD = "version"; static const QString NAME_FIELD = "name"; static const QString TYPE_FIELD = "type"; static const QString FILENAME_FIELD = "filename"; diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index 42dfe59241..15b5a1ae12 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -152,8 +152,11 @@ void DomainBaker::addModelBaker(const QString& property, const QString& url, QJs auto getWorkerThreadCallback = []() -> QThread* { return Oven::instance().getNextWorkerThread(); }; - QSharedPointer baker = QSharedPointer(getModelBaker(bakeableModelURL, getWorkerThreadCallback, _contentOutputPath).release(), &ModelBaker::deleteLater); + QSharedPointer baker = QSharedPointer(getModelBaker(bakeableModelURL, getWorkerThreadCallback, _contentOutputPath).release(), &Baker::deleteLater); if (baker) { + // Hold on to the old url userinfo/query/fragment data so ModelBaker::getFullOutputMappingURL retains that data from the original model URL + baker->setOutputURLSuffix(url); + // make sure our handler is called when the baker is done connect(baker.data(), &Baker::finished, this, &DomainBaker::handleFinishedModelBaker); @@ -332,6 +335,7 @@ void DomainBaker::enumerateEntities() { addModelBaker(MODEL_URL_KEY, entity[MODEL_URL_KEY].toString(), *it); } if (entity.contains(COMPOUND_SHAPE_URL_KEY)) { + // TODO: Do not combine mesh parts, otherwise the collision behavior will be different // TODO: this could be optimized so that we don't do the full baking pass for collision shapes, // but we have to handle the case where it's also used as a modelURL somewhere addModelBaker(COMPOUND_SHAPE_URL_KEY, entity[COMPOUND_SHAPE_URL_KEY].toString(), *it); @@ -415,11 +419,11 @@ void DomainBaker::handleFinishedModelBaker() { qDebug() << "Re-writing entity references to" << baker->getModelURL(); // setup a new URL using the prefix we were passed - auto relativeFBXFilePath = baker->getBakedModelFilePath().remove(_contentOutputPath); - if (relativeFBXFilePath.startsWith("/")) { - relativeFBXFilePath = relativeFBXFilePath.right(relativeFBXFilePath.length() - 1); + auto relativeMappingFilePath = baker->getFullOutputMappingURL().toString().remove(_contentOutputPath); + if (relativeMappingFilePath.startsWith("/")) { + relativeMappingFilePath = relativeMappingFilePath.right(relativeMappingFilePath.length() - 1); } - QUrl newURL = _destinationPath.resolved(relativeFBXFilePath); + QUrl newURL = _destinationPath.resolved(relativeMappingFilePath); // enumerate the QJsonRef values for the URL of this model from our multi hash of // entity objects needing a URL re-write @@ -432,12 +436,8 @@ void DomainBaker::handleFinishedModelBaker() { // grab the old URL QUrl oldURL = entity[property].toString(); - // copy the fragment and query, and user info from the old model URL - newURL.setQuery(oldURL.query()); - newURL.setFragment(oldURL.fragment()); - newURL.setUserInfo(oldURL.userInfo()); - // set the new URL as the value in our temp QJsonObject + // The fragment, query, and user info from the original model URL should now be present on the filename in the FST file entity[property] = newURL.toString(); } else { // Group property diff --git a/tools/oven/src/DomainBaker.h b/tools/oven/src/DomainBaker.h index 4504d5b8fa..dbbf182fa7 100644 --- a/tools/oven/src/DomainBaker.h +++ b/tools/oven/src/DomainBaker.h @@ -17,7 +17,6 @@ #include #include -#include "Baker.h" #include "ModelBaker.h" #include "TextureBaker.h" #include "JSBaker.h"