mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 18:42:58 +02:00
cleaning up oven
This commit is contained in:
parent
86c948f116
commit
270b96aa8d
9 changed files with 431 additions and 288 deletions
|
@ -25,6 +25,9 @@ public:
|
||||||
JSBaker(const QUrl& jsURL, const QString& bakedOutputDir);
|
JSBaker(const QUrl& jsURL, const QString& bakedOutputDir);
|
||||||
static bool bakeJS(const QByteArray& inputFile, QByteArray& outputFile);
|
static bool bakeJS(const QByteArray& inputFile, QByteArray& outputFile);
|
||||||
|
|
||||||
|
QString getJSPath() const { return _jsURL.fileName(); }
|
||||||
|
QString getBakedJSFilePath() const { return _bakedJSFilePath; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void bake() override;
|
virtual void bake() override;
|
||||||
|
|
||||||
|
|
|
@ -32,11 +32,12 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ModelBaker::ModelBaker(const QUrl& inputModelURL, TextureBakerThreadGetter inputTextureThreadGetter,
|
ModelBaker::ModelBaker(const QUrl& inputModelURL, TextureBakerThreadGetter inputTextureThreadGetter,
|
||||||
const QString& bakedOutputDirectory, const QString& originalOutputDirectory) :
|
const QString& bakedOutputDirectory, const QString& originalOutputDirectory, bool hasBeenBaked) :
|
||||||
_modelURL(inputModelURL),
|
_modelURL(inputModelURL),
|
||||||
_bakedOutputDir(bakedOutputDirectory),
|
_bakedOutputDir(bakedOutputDirectory),
|
||||||
_originalOutputDir(originalOutputDirectory),
|
_originalOutputDir(originalOutputDirectory),
|
||||||
_textureThreadGetter(inputTextureThreadGetter)
|
_textureThreadGetter(inputTextureThreadGetter),
|
||||||
|
_hasBeenBaked(hasBeenBaked)
|
||||||
{
|
{
|
||||||
auto tempDir = PathUtils::generateTemporaryDir();
|
auto tempDir = PathUtils::generateTemporaryDir();
|
||||||
|
|
||||||
|
|
|
@ -30,16 +30,19 @@
|
||||||
using TextureBakerThreadGetter = std::function<QThread*()>;
|
using TextureBakerThreadGetter = std::function<QThread*()>;
|
||||||
using GetMaterialIDCallback = std::function <int(int)>;
|
using GetMaterialIDCallback = std::function <int(int)>;
|
||||||
|
|
||||||
static const QString BAKED_FBX_EXTENSION = ".baked.fbx";
|
static const QString FST_EXTENSION { ".fst" };
|
||||||
static const QString BAKEABLE_MODEL_FBX_EXTENSION { ".fbx" };
|
static const QString BAKED_FST_EXTENSION { ".baked.fst" };
|
||||||
static const QString BAKEABLE_MODEL_OBJ_EXTENSION { ".obj" };
|
static const QString FBX_EXTENSION { ".fbx" };
|
||||||
|
static const QString BAKED_FBX_EXTENSION { ".baked.fbx" };
|
||||||
|
static const QString OBJ_EXTENSION { ".obj" };
|
||||||
|
static const QString GLTF_EXTENSION { ".gltf" };
|
||||||
|
|
||||||
class ModelBaker : public Baker {
|
class ModelBaker : public Baker {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ModelBaker(const QUrl& inputModelURL, TextureBakerThreadGetter inputTextureThreadGetter,
|
ModelBaker(const QUrl& inputModelURL, TextureBakerThreadGetter inputTextureThreadGetter,
|
||||||
const QString& bakedOutputDirectory, const QString& originalOutputDirectory = "");
|
const QString& bakedOutputDirectory, const QString& originalOutputDirectory = "", bool hasBeenBaked = false);
|
||||||
virtual ~ModelBaker();
|
virtual ~ModelBaker();
|
||||||
|
|
||||||
void initializeOutputDirs();
|
void initializeOutputDirs();
|
||||||
|
@ -59,7 +62,7 @@ protected:
|
||||||
void texturesFinished();
|
void texturesFinished();
|
||||||
void embedTextureMetaData();
|
void embedTextureMetaData();
|
||||||
void exportScene();
|
void exportScene();
|
||||||
|
|
||||||
FBXNode _rootNode;
|
FBXNode _rootNode;
|
||||||
QHash<QByteArray, QByteArray> _textureContentMap;
|
QHash<QByteArray, QByteArray> _textureContentMap;
|
||||||
QUrl _modelURL;
|
QUrl _modelURL;
|
||||||
|
@ -79,12 +82,14 @@ private:
|
||||||
void bakeTexture(const QUrl & textureURL, image::TextureUsage::Type textureType, const QDir & outputDir,
|
void bakeTexture(const QUrl & textureURL, image::TextureUsage::Type textureType, const QDir & outputDir,
|
||||||
const QString & bakedFilename, const QByteArray & textureContent);
|
const QString & bakedFilename, const QByteArray & textureContent);
|
||||||
QString texturePathRelativeToModel(QUrl modelURL, QUrl textureURL);
|
QString texturePathRelativeToModel(QUrl modelURL, QUrl textureURL);
|
||||||
|
|
||||||
TextureBakerThreadGetter _textureThreadGetter;
|
TextureBakerThreadGetter _textureThreadGetter;
|
||||||
QMultiHash<QUrl, QSharedPointer<TextureBaker>> _bakingTextures;
|
QMultiHash<QUrl, QSharedPointer<TextureBaker>> _bakingTextures;
|
||||||
QHash<QString, int> _textureNameMatchCount;
|
QHash<QString, int> _textureNameMatchCount;
|
||||||
QHash<QUrl, QString> _remappedTexturePaths;
|
QHash<QUrl, QString> _remappedTexturePaths;
|
||||||
bool _pendingErrorEmission{ false };
|
bool _pendingErrorEmission { false };
|
||||||
|
|
||||||
|
bool _hasBeenBaked { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ModelBaker_h
|
#endif // hifi_ModelBaker_h
|
||||||
|
|
|
@ -14,33 +14,26 @@
|
||||||
#include "../FBXBaker.h"
|
#include "../FBXBaker.h"
|
||||||
#include "../OBJBaker.h"
|
#include "../OBJBaker.h"
|
||||||
|
|
||||||
QUrl getBakeableModelURL(const QUrl& url, bool shouldRebakeOriginals) {
|
// Check if the file pointed to by this URL is a bakeable model, by comparing extensions
|
||||||
// Check if the file pointed to by this URL is a bakeable model, by comparing extensions
|
QUrl getBakeableModelURL(const QUrl& url) {
|
||||||
auto modelFileName = url.fileName();
|
static const std::vector<QString> extensionsToBake = {
|
||||||
|
FST_EXTENSION,
|
||||||
|
BAKED_FST_EXTENSION,
|
||||||
|
FBX_EXTENSION,
|
||||||
|
BAKED_FBX_EXTENSION,
|
||||||
|
OBJ_EXTENSION,
|
||||||
|
GLTF_EXTENSION
|
||||||
|
};
|
||||||
|
|
||||||
bool isBakedModel = modelFileName.endsWith(BAKED_FBX_EXTENSION, Qt::CaseInsensitive);
|
QUrl cleanURL = url.adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment);
|
||||||
bool isBakeableFBX = modelFileName.endsWith(BAKEABLE_MODEL_FBX_EXTENSION, Qt::CaseInsensitive);
|
QString cleanURLString = cleanURL.fileName();
|
||||||
bool isBakeableOBJ = modelFileName.endsWith(BAKEABLE_MODEL_OBJ_EXTENSION, Qt::CaseInsensitive);
|
for (auto& extension : extensionsToBake) {
|
||||||
bool isBakeable = isBakeableFBX || isBakeableOBJ;
|
if (cleanURLString.endsWith(extension, Qt::CaseInsensitive)) {
|
||||||
|
return cleanURL;
|
||||||
if (isBakeable || (shouldRebakeOriginals && isBakedModel)) {
|
|
||||||
if (isBakedModel) {
|
|
||||||
// Grab a URL to the original, that we assume is stored a directory up, in the "original" folder
|
|
||||||
// with just the fbx extension
|
|
||||||
qDebug() << "Inferring original URL for baked model URL" << url;
|
|
||||||
|
|
||||||
auto originalFileName = modelFileName;
|
|
||||||
originalFileName.replace(".baked", "");
|
|
||||||
qDebug() << "Original model URL must be present at" << url;
|
|
||||||
|
|
||||||
return url.resolved("../original/" + originalFileName);
|
|
||||||
} else {
|
|
||||||
// Grab a clean version of the URL without a query or fragment
|
|
||||||
return url.adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qWarning() << "Unknown model type: " << modelFileName;
|
qWarning() << "Unknown model type: " << url.fileName();
|
||||||
return QUrl();
|
return QUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,11 +52,14 @@ std::unique_ptr<ModelBaker> getModelBaker(const QUrl& bakeableModelURL, TextureB
|
||||||
QString originalOutputDirectory = contentOutputPath + subDirName + "/original";
|
QString originalOutputDirectory = contentOutputPath + subDirName + "/original";
|
||||||
|
|
||||||
std::unique_ptr<ModelBaker> baker;
|
std::unique_ptr<ModelBaker> baker;
|
||||||
|
if (filename.endsWith(FST_EXTENSION, Qt::CaseInsensitive)) {
|
||||||
if (filename.endsWith(BAKEABLE_MODEL_FBX_EXTENSION, Qt::CaseInsensitive)) {
|
//baker = std::make_unique<FSTBaker>(bakeableModelURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory, filename.endsWith(BAKED_FST_EXTENSION, Qt::CaseInsensitive));
|
||||||
baker = std::make_unique<FBXBaker>(bakeableModelURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory);
|
} else if (filename.endsWith(FBX_EXTENSION, Qt::CaseInsensitive)) {
|
||||||
} else if (filename.endsWith(BAKEABLE_MODEL_OBJ_EXTENSION, Qt::CaseInsensitive)) {
|
baker = std::make_unique<FBXBaker>(bakeableModelURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory, filename.endsWith(BAKED_FBX_EXTENSION, Qt::CaseInsensitive));
|
||||||
|
} else if (filename.endsWith(OBJ_EXTENSION, Qt::CaseInsensitive)) {
|
||||||
baker = std::make_unique<OBJBaker>(bakeableModelURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory);
|
baker = std::make_unique<OBJBaker>(bakeableModelURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory);
|
||||||
|
} else if (filename.endsWith(GLTF_EXTENSION, Qt::CaseInsensitive)) {
|
||||||
|
//baker = std::make_unique<GLTFBaker>(bakeableModelURL, inputTextureThreadGetter, bakedOutputDirectory, originalOutputDirectory);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Could not create ModelBaker for url" << bakeableModelURL;
|
qDebug() << "Could not create ModelBaker for url" << bakeableModelURL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
// Returns either the given model URL, or, if the model is baked and shouldRebakeOriginals is true,
|
// Returns either the given model URL, or, if the model is baked and shouldRebakeOriginals is true,
|
||||||
// the guessed location of the original model
|
// the guessed location of the original model
|
||||||
// Returns an empty URL if no bakeable URL found
|
// Returns an empty URL if no bakeable URL found
|
||||||
QUrl getBakeableModelURL(const QUrl& url, bool shouldRebakeOriginals);
|
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
|
// 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
|
// Returns an empty pointer if a baker could not be created
|
||||||
|
|
|
@ -37,25 +37,16 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString&
|
||||||
|
|
||||||
qDebug() << "Baking file type: " << type;
|
qDebug() << "Baking file type: " << type;
|
||||||
|
|
||||||
static const QString MODEL_EXTENSION { "fbx" };
|
static const QString MODEL_EXTENSION { "model" };
|
||||||
|
static const QString FBX_EXTENSION { "fbx" }; // legacy
|
||||||
|
static const QString MATERIAL_EXTENSION { "material" };
|
||||||
static const QString SCRIPT_EXTENSION { "js" };
|
static const QString SCRIPT_EXTENSION { "js" };
|
||||||
|
|
||||||
// check what kind of baker we should be creating
|
|
||||||
bool isModel = type == MODEL_EXTENSION;
|
|
||||||
bool isScript = type == SCRIPT_EXTENSION;
|
|
||||||
|
|
||||||
// If the type doesn't match the above, we assume we have a texture, and the type specified is the
|
|
||||||
// texture usage type (albedo, cubemap, normals, etc.)
|
|
||||||
auto url = inputUrl.toDisplayString();
|
|
||||||
auto idx = url.lastIndexOf('.');
|
|
||||||
auto extension = idx >= 0 ? url.mid(idx + 1).toLower() : "";
|
|
||||||
bool isSupportedImage = QImageReader::supportedImageFormats().contains(extension.toLatin1());
|
|
||||||
|
|
||||||
_outputPath = outputPath;
|
_outputPath = outputPath;
|
||||||
|
|
||||||
// create our appropiate baker
|
// create our appropiate baker
|
||||||
if (isModel) {
|
if (type == MODEL_EXTENSION || type == FBX_EXTENSION) {
|
||||||
QUrl bakeableModelURL = getBakeableModelURL(inputUrl, false);
|
QUrl bakeableModelURL = getBakeableModelURL(inputUrl);
|
||||||
if (!bakeableModelURL.isEmpty()) {
|
if (!bakeableModelURL.isEmpty()) {
|
||||||
auto getWorkerThreadCallback = []() -> QThread* {
|
auto getWorkerThreadCallback = []() -> QThread* {
|
||||||
return Oven::instance().getNextWorkerThread();
|
return Oven::instance().getNextWorkerThread();
|
||||||
|
@ -65,35 +56,49 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString&
|
||||||
_baker->moveToThread(Oven::instance().getNextWorkerThread());
|
_baker->moveToThread(Oven::instance().getNextWorkerThread());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (isScript) {
|
} else if (type == SCRIPT_EXTENSION) {
|
||||||
_baker = std::unique_ptr<Baker> { new JSBaker(inputUrl, outputPath) };
|
_baker = std::unique_ptr<Baker> { new JSBaker(inputUrl, outputPath) };
|
||||||
_baker->moveToThread(Oven::instance().getNextWorkerThread());
|
_baker->moveToThread(Oven::instance().getNextWorkerThread());
|
||||||
} else if (isSupportedImage) {
|
} else if (type == MATERIAL_EXTENSION) {
|
||||||
static const std::unordered_map<QString, image::TextureUsage::Type> STRING_TO_TEXTURE_USAGE_TYPE_MAP {
|
//_baker = std::unique_ptr<Baker> { new MaterialBaker(inputUrl, outputPath) };
|
||||||
{ "default", image::TextureUsage::DEFAULT_TEXTURE },
|
//_baker->moveToThread(Oven::instance().getNextWorkerThread());
|
||||||
{ "strict", image::TextureUsage::STRICT_TEXTURE },
|
|
||||||
{ "albedo", image::TextureUsage::ALBEDO_TEXTURE },
|
|
||||||
{ "normal", image::TextureUsage::NORMAL_TEXTURE },
|
|
||||||
{ "bump", image::TextureUsage::BUMP_TEXTURE },
|
|
||||||
{ "specular", image::TextureUsage::SPECULAR_TEXTURE },
|
|
||||||
{ "metallic", image::TextureUsage::METALLIC_TEXTURE },
|
|
||||||
{ "roughness", image::TextureUsage::ROUGHNESS_TEXTURE },
|
|
||||||
{ "gloss", image::TextureUsage::GLOSS_TEXTURE },
|
|
||||||
{ "emissive", image::TextureUsage::EMISSIVE_TEXTURE },
|
|
||||||
{ "cube", image::TextureUsage::CUBE_TEXTURE },
|
|
||||||
{ "occlusion", image::TextureUsage::OCCLUSION_TEXTURE },
|
|
||||||
{ "scattering", image::TextureUsage::SCATTERING_TEXTURE },
|
|
||||||
{ "lightmap", image::TextureUsage::LIGHTMAP_TEXTURE },
|
|
||||||
};
|
|
||||||
|
|
||||||
auto it = STRING_TO_TEXTURE_USAGE_TYPE_MAP.find(type);
|
|
||||||
if (it == STRING_TO_TEXTURE_USAGE_TYPE_MAP.end()) {
|
|
||||||
qCDebug(model_baking) << "Unknown texture usage type:" << type;
|
|
||||||
QCoreApplication::exit(OVEN_STATUS_CODE_FAIL);
|
|
||||||
}
|
|
||||||
_baker = std::unique_ptr<Baker> { new TextureBaker(inputUrl, it->second, outputPath) };
|
|
||||||
_baker->moveToThread(Oven::instance().getNextWorkerThread());
|
|
||||||
} else {
|
} else {
|
||||||
|
// If the type doesn't match the above, we assume we have a texture, and the type specified is the
|
||||||
|
// texture usage type (albedo, cubemap, normals, etc.)
|
||||||
|
auto url = inputUrl.toDisplayString();
|
||||||
|
auto idx = url.lastIndexOf('.');
|
||||||
|
auto extension = idx >= 0 ? url.mid(idx + 1).toLower() : "";
|
||||||
|
|
||||||
|
if (QImageReader::supportedImageFormats().contains(extension.toLatin1())) {
|
||||||
|
static const std::unordered_map<QString, image::TextureUsage::Type> STRING_TO_TEXTURE_USAGE_TYPE_MAP {
|
||||||
|
{ "default", image::TextureUsage::DEFAULT_TEXTURE },
|
||||||
|
{ "strict", image::TextureUsage::STRICT_TEXTURE },
|
||||||
|
{ "albedo", image::TextureUsage::ALBEDO_TEXTURE },
|
||||||
|
{ "normal", image::TextureUsage::NORMAL_TEXTURE },
|
||||||
|
{ "bump", image::TextureUsage::BUMP_TEXTURE },
|
||||||
|
{ "specular", image::TextureUsage::SPECULAR_TEXTURE },
|
||||||
|
{ "metallic", image::TextureUsage::METALLIC_TEXTURE },
|
||||||
|
{ "roughness", image::TextureUsage::ROUGHNESS_TEXTURE },
|
||||||
|
{ "gloss", image::TextureUsage::GLOSS_TEXTURE },
|
||||||
|
{ "emissive", image::TextureUsage::EMISSIVE_TEXTURE },
|
||||||
|
{ "cube", image::TextureUsage::CUBE_TEXTURE },
|
||||||
|
{ "skybox", image::TextureUsage::CUBE_TEXTURE },
|
||||||
|
{ "occlusion", image::TextureUsage::OCCLUSION_TEXTURE },
|
||||||
|
{ "scattering", image::TextureUsage::SCATTERING_TEXTURE },
|
||||||
|
{ "lightmap", image::TextureUsage::LIGHTMAP_TEXTURE },
|
||||||
|
};
|
||||||
|
|
||||||
|
auto it = STRING_TO_TEXTURE_USAGE_TYPE_MAP.find(type);
|
||||||
|
if (it == STRING_TO_TEXTURE_USAGE_TYPE_MAP.end()) {
|
||||||
|
qCDebug(model_baking) << "Unknown texture usage type:" << type;
|
||||||
|
QCoreApplication::exit(OVEN_STATUS_CODE_FAIL);
|
||||||
|
}
|
||||||
|
_baker = std::unique_ptr<Baker> { new TextureBaker(inputUrl, it->second, outputPath) };
|
||||||
|
_baker->moveToThread(Oven::instance().getNextWorkerThread());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_baker) {
|
||||||
qCDebug(model_baking) << "Failed to determine baker type for file" << inputUrl;
|
qCDebug(model_baking) << "Failed to determine baker type for file" << inputUrl;
|
||||||
QCoreApplication::exit(OVEN_STATUS_CODE_FAIL);
|
QCoreApplication::exit(OVEN_STATUS_CODE_FAIL);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -27,8 +27,7 @@ DomainBaker::DomainBaker(const QUrl& localModelFileURL, const QString& domainNam
|
||||||
bool shouldRebakeOriginals) :
|
bool shouldRebakeOriginals) :
|
||||||
_localEntitiesFileURL(localModelFileURL),
|
_localEntitiesFileURL(localModelFileURL),
|
||||||
_domainName(domainName),
|
_domainName(domainName),
|
||||||
_baseOutputPath(baseOutputPath),
|
_baseOutputPath(baseOutputPath)
|
||||||
_shouldRebakeOriginals(shouldRebakeOriginals)
|
|
||||||
{
|
{
|
||||||
// make sure the destination path has a trailing slash
|
// make sure the destination path has a trailing slash
|
||||||
if (!destinationPath.toString().endsWith('/')) {
|
if (!destinationPath.toString().endsWith('/')) {
|
||||||
|
@ -145,11 +144,139 @@ void DomainBaker::loadLocalFile() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString ENTITY_MODEL_URL_KEY = "modelURL";
|
void DomainBaker::addModelBaker(const QString& property, const QString& url, QJsonValueRef& jsonRef) {
|
||||||
const QString ENTITY_SKYBOX_KEY = "skybox";
|
// grab a QUrl for the model URL
|
||||||
const QString ENTITY_SKYBOX_URL_KEY = "url";
|
QUrl bakeableModelURL = getBakeableModelURL(url);
|
||||||
const QString ENTITY_KEYLIGHT_KEY = "keyLight";
|
if (!bakeableModelURL.isEmpty()) {
|
||||||
const QString ENTITY_KEYLIGHT_AMBIENT_URL_KEY = "ambientURL";
|
// setup a ModelBaker for this URL, as long as we don't already have one
|
||||||
|
if (!_modelBakers.contains(bakeableModelURL)) {
|
||||||
|
auto getWorkerThreadCallback = []() -> QThread* {
|
||||||
|
return Oven::instance().getNextWorkerThread();
|
||||||
|
};
|
||||||
|
QSharedPointer<ModelBaker> baker = QSharedPointer<ModelBaker>(getModelBaker(bakeableModelURL, getWorkerThreadCallback, _contentOutputPath).release(), &ModelBaker::deleteLater);
|
||||||
|
if (baker) {
|
||||||
|
// make sure our handler is called when the baker is done
|
||||||
|
connect(baker.data(), &Baker::finished, this, &DomainBaker::handleFinishedModelBaker);
|
||||||
|
|
||||||
|
// insert it into our bakers hash so we hold a strong pointer to it
|
||||||
|
_modelBakers.insert(bakeableModelURL, baker);
|
||||||
|
|
||||||
|
// move the baker to the baker thread
|
||||||
|
// and kickoff the bake
|
||||||
|
baker->moveToThread(Oven::instance().getNextWorkerThread());
|
||||||
|
QMetaObject::invokeMethod(baker.data(), "bake");
|
||||||
|
|
||||||
|
// keep track of the total number of baking entities
|
||||||
|
++_totalNumberOfSubBakes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add this QJsonValueRef to our multi hash so that we can easily re-write
|
||||||
|
// the model URL to the baked version once the baker is complete
|
||||||
|
_entitiesNeedingRewrite.insert(bakeableModelURL, { property, jsonRef });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainBaker::addTextureBaker(const QString& property, const QString& url, image::TextureUsage::Type type, QJsonValueRef& jsonRef) {
|
||||||
|
auto idx = url.lastIndexOf('.');
|
||||||
|
auto extension = idx >= 0 ? url.mid(idx + 1).toLower() : "";
|
||||||
|
|
||||||
|
if (QImageReader::supportedImageFormats().contains(extension.toLatin1())) {
|
||||||
|
// grab a clean version of the URL without a query or fragment
|
||||||
|
QUrl textureURL = QUrl(url).adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment);
|
||||||
|
|
||||||
|
// setup a texture baker for this URL, as long as we aren't baking a texture already
|
||||||
|
if (!_textureBakers.contains(textureURL)) {
|
||||||
|
// setup a baker for this texture
|
||||||
|
|
||||||
|
QSharedPointer<TextureBaker> textureBaker {
|
||||||
|
new TextureBaker(textureURL, type, _contentOutputPath),
|
||||||
|
&TextureBaker::deleteLater
|
||||||
|
};
|
||||||
|
|
||||||
|
// make sure our handler is called when the texture baker is done
|
||||||
|
connect(textureBaker.data(), &TextureBaker::finished, this, &DomainBaker::handleFinishedTextureBaker);
|
||||||
|
|
||||||
|
// insert it into our bakers hash so we hold a strong pointer to it
|
||||||
|
_textureBakers.insert(textureURL, textureBaker);
|
||||||
|
|
||||||
|
// move the baker to a worker thread and kickoff the bake
|
||||||
|
textureBaker->moveToThread(Oven::instance().getNextWorkerThread());
|
||||||
|
QMetaObject::invokeMethod(textureBaker.data(), "bake");
|
||||||
|
|
||||||
|
// keep track of the total number of baking entities
|
||||||
|
++_totalNumberOfSubBakes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add this QJsonValueRef to our multi hash so that it can re-write the texture URL
|
||||||
|
// to the baked version once the baker is complete
|
||||||
|
_entitiesNeedingRewrite.insert(textureURL, { property, jsonRef });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainBaker::addScriptBaker(const QString& property, const QString& url, QJsonValueRef& jsonRef) {
|
||||||
|
// grab a clean version of the URL without a query or fragment
|
||||||
|
QUrl scriptURL = QUrl(url).adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment);
|
||||||
|
|
||||||
|
// setup a texture baker for this URL, as long as we aren't baking a texture already
|
||||||
|
if (!_scriptBakers.contains(scriptURL)) {
|
||||||
|
// setup a baker for this texture
|
||||||
|
|
||||||
|
QSharedPointer<JSBaker> scriptBaker {
|
||||||
|
new JSBaker(scriptURL, _contentOutputPath),
|
||||||
|
&JSBaker::deleteLater
|
||||||
|
};
|
||||||
|
|
||||||
|
// make sure our handler is called when the texture baker is done
|
||||||
|
connect(scriptBaker.data(), &JSBaker::finished, this, &DomainBaker::handleFinishedScriptBaker);
|
||||||
|
|
||||||
|
// insert it into our bakers hash so we hold a strong pointer to it
|
||||||
|
_scriptBakers.insert(scriptURL, scriptBaker);
|
||||||
|
|
||||||
|
// move the baker to a worker thread and kickoff the bake
|
||||||
|
scriptBaker->moveToThread(Oven::instance().getNextWorkerThread());
|
||||||
|
QMetaObject::invokeMethod(scriptBaker.data(), "bake");
|
||||||
|
|
||||||
|
// keep track of the total number of baking entities
|
||||||
|
++_totalNumberOfSubBakes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add this QJsonValueRef to our multi hash so that it can re-write the texture URL
|
||||||
|
// to the baked version once the baker is complete
|
||||||
|
_entitiesNeedingRewrite.insert(scriptURL, { property, jsonRef });
|
||||||
|
}
|
||||||
|
|
||||||
|
// All the Entity Properties that can be baked
|
||||||
|
// ***************************************************************************************
|
||||||
|
|
||||||
|
// Models
|
||||||
|
const QString MODEL_URL_KEY = "modelURL";
|
||||||
|
const QString COMPOUND_SHAPE_URL_KEY = "compoundShapeURL";
|
||||||
|
const QString GRAP_KEY = "grab";
|
||||||
|
const QString EQUIPPABLE_INDICATOR_URL_KEY = "equippableIndicatorURL";
|
||||||
|
const QString ANIMATION_KEY = "animation";
|
||||||
|
const QString ANIMATION_URL_KEY = "url";
|
||||||
|
|
||||||
|
// Textures
|
||||||
|
const QString TEXTURES_KEY = "textures";
|
||||||
|
const QString IMAGE_URL_KEY = "imageURL";
|
||||||
|
const QString X_TEXTURE_URL_KEY = "xTextureURL";
|
||||||
|
const QString Y_TEXTURE_URL_KEY = "yTextureURL";
|
||||||
|
const QString Z_TEXTURE_URL_KEY = "zTextureURL";
|
||||||
|
const QString AMBIENT_LIGHT_KEY = "ambientLight";
|
||||||
|
const QString AMBIENT_URL_KEY = "ambientURL";
|
||||||
|
const QString SKYBOX_KEY = "skybox";
|
||||||
|
const QString SKYBOX_URL_KEY = "url";
|
||||||
|
|
||||||
|
// Scripts
|
||||||
|
const QString SCRIPT_KEY = "script";
|
||||||
|
const QString SERVER_SCRIPTS_KEY = "serverScripts";
|
||||||
|
|
||||||
|
// Materials
|
||||||
|
const QString MATERIAL_URL_KEY = "materialURL";
|
||||||
|
const QString MATERIAL_DATA_KEY = "materialData";
|
||||||
|
|
||||||
|
// ***************************************************************************************
|
||||||
|
|
||||||
void DomainBaker::enumerateEntities() {
|
void DomainBaker::enumerateEntities() {
|
||||||
qDebug() << "Enumerating" << _entities.size() << "entities from domain";
|
qDebug() << "Enumerating" << _entities.size() << "entities from domain";
|
||||||
|
@ -159,65 +286,65 @@ void DomainBaker::enumerateEntities() {
|
||||||
if (it->isObject()) {
|
if (it->isObject()) {
|
||||||
auto entity = it->toObject();
|
auto entity = it->toObject();
|
||||||
|
|
||||||
// check if this is an entity with a model URL or is a skybox texture
|
// Models
|
||||||
if (entity.contains(ENTITY_MODEL_URL_KEY)) {
|
if (entity.contains(MODEL_URL_KEY)) {
|
||||||
// grab a QUrl for the model URL
|
addModelBaker(MODEL_URL_KEY, entity[MODEL_URL_KEY].toString(), *it);
|
||||||
QUrl bakeableModelURL = getBakeableModelURL(entity[ENTITY_MODEL_URL_KEY].toString(), _shouldRebakeOriginals);
|
|
||||||
|
|
||||||
if (!bakeableModelURL.isEmpty()) {
|
|
||||||
|
|
||||||
// setup a ModelBaker for this URL, as long as we don't already have one
|
|
||||||
if (!_modelBakers.contains(bakeableModelURL)) {
|
|
||||||
auto getWorkerThreadCallback = []() -> QThread* {
|
|
||||||
return Oven::instance().getNextWorkerThread();
|
|
||||||
};
|
|
||||||
QSharedPointer<ModelBaker> baker = QSharedPointer<ModelBaker>(getModelBaker(bakeableModelURL, getWorkerThreadCallback, _contentOutputPath).release(), &ModelBaker::deleteLater);
|
|
||||||
if (baker) {
|
|
||||||
// make sure our handler is called when the baker is done
|
|
||||||
connect(baker.data(), &Baker::finished, this, &DomainBaker::handleFinishedModelBaker);
|
|
||||||
|
|
||||||
// insert it into our bakers hash so we hold a strong pointer to it
|
|
||||||
_modelBakers.insert(bakeableModelURL, baker);
|
|
||||||
|
|
||||||
// move the baker to the baker thread
|
|
||||||
// and kickoff the bake
|
|
||||||
baker->moveToThread(Oven::instance().getNextWorkerThread());
|
|
||||||
QMetaObject::invokeMethod(baker.data(), "bake");
|
|
||||||
|
|
||||||
// keep track of the total number of baking entities
|
|
||||||
++_totalNumberOfSubBakes;
|
|
||||||
|
|
||||||
// add this QJsonValueRef to our multi hash so that we can easily re-write
|
|
||||||
// the model URL to the baked version once the baker is complete
|
|
||||||
_entitiesNeedingRewrite.insert(bakeableModelURL, *it);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// // We check now to see if we have either a texture for a skybox or a keylight, or both.
|
|
||||||
// if (entity.contains(ENTITY_SKYBOX_KEY)) {
|
|
||||||
// auto skyboxObject = entity[ENTITY_SKYBOX_KEY].toObject();
|
|
||||||
// if (skyboxObject.contains(ENTITY_SKYBOX_URL_KEY)) {
|
|
||||||
// // we have a URL to a skybox, grab it
|
|
||||||
// QUrl skyboxURL { skyboxObject[ENTITY_SKYBOX_URL_KEY].toString() };
|
|
||||||
//
|
|
||||||
// // setup a bake of the skybox
|
|
||||||
// bakeSkybox(skyboxURL, *it);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (entity.contains(ENTITY_KEYLIGHT_KEY)) {
|
|
||||||
// auto keyLightObject = entity[ENTITY_KEYLIGHT_KEY].toObject();
|
|
||||||
// if (keyLightObject.contains(ENTITY_KEYLIGHT_AMBIENT_URL_KEY)) {
|
|
||||||
// // we have a URL to a skybox, grab it
|
|
||||||
// QUrl skyboxURL { keyLightObject[ENTITY_KEYLIGHT_AMBIENT_URL_KEY].toString() };
|
|
||||||
//
|
|
||||||
// // setup a bake of the skybox
|
|
||||||
// bakeSkybox(skyboxURL, *it);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
if (entity.contains(COMPOUND_SHAPE_URL_KEY)) {
|
||||||
|
// TODO: handle compoundShapeURL
|
||||||
|
}
|
||||||
|
if (entity.contains(ANIMATION_KEY)) {
|
||||||
|
auto animationObject = entity[ANIMATION_KEY].toObject();
|
||||||
|
if (animationObject.contains(ANIMATION_URL_KEY)) {
|
||||||
|
addModelBaker(ANIMATION_KEY + "." + ANIMATION_URL_KEY, animationObject[ANIMATION_URL_KEY].toString(), *it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (entity.contains(GRAP_KEY)) {
|
||||||
|
auto grabObject = entity[GRAP_KEY].toObject();
|
||||||
|
if (grabObject.contains(EQUIPPABLE_INDICATOR_URL_KEY)) {
|
||||||
|
addModelBaker(GRAP_KEY + "." + EQUIPPABLE_INDICATOR_URL_KEY, grabObject[EQUIPPABLE_INDICATOR_URL_KEY].toString(), *it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Textures
|
||||||
|
if (entity.contains(TEXTURES_KEY)) {
|
||||||
|
// TODO: the textures property is treated differently for different entity types
|
||||||
|
}
|
||||||
|
if (entity.contains(IMAGE_URL_KEY)) {
|
||||||
|
addTextureBaker(IMAGE_URL_KEY, entity[IMAGE_URL_KEY].toString(), image::TextureUsage::DEFAULT_TEXTURE, *it);
|
||||||
|
}
|
||||||
|
if (entity.contains(X_TEXTURE_URL_KEY)) {
|
||||||
|
addTextureBaker(X_TEXTURE_URL_KEY, entity[X_TEXTURE_URL_KEY].toString(), image::TextureUsage::DEFAULT_TEXTURE, *it);
|
||||||
|
}
|
||||||
|
if (entity.contains(Y_TEXTURE_URL_KEY)) {
|
||||||
|
addTextureBaker(Y_TEXTURE_URL_KEY, entity[Y_TEXTURE_URL_KEY].toString(), image::TextureUsage::DEFAULT_TEXTURE, *it);
|
||||||
|
}
|
||||||
|
if (entity.contains(Z_TEXTURE_URL_KEY)) {
|
||||||
|
addTextureBaker(Z_TEXTURE_URL_KEY, entity[Z_TEXTURE_URL_KEY].toString(), image::TextureUsage::DEFAULT_TEXTURE, *it);
|
||||||
|
}
|
||||||
|
if (entity.contains(AMBIENT_LIGHT_KEY)) {
|
||||||
|
auto ambientLight = entity[AMBIENT_LIGHT_KEY].toObject();
|
||||||
|
if (ambientLight.contains(AMBIENT_URL_KEY)) {
|
||||||
|
addTextureBaker(AMBIENT_LIGHT_KEY + "." + AMBIENT_URL_KEY, ambientLight[AMBIENT_URL_KEY].toString(), image::TextureUsage::CUBE_TEXTURE, *it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (entity.contains(SKYBOX_KEY)) {
|
||||||
|
auto skybox = entity[SKYBOX_KEY].toObject();
|
||||||
|
if (skybox.contains(SKYBOX_URL_KEY)) {
|
||||||
|
addTextureBaker(SKYBOX_KEY + "." + SKYBOX_URL_KEY, skybox[SKYBOX_URL_KEY].toString(), image::TextureUsage::CUBE_TEXTURE, *it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scripts
|
||||||
|
if (entity.contains(SCRIPT_KEY)) {
|
||||||
|
addScriptBaker(SCRIPT_KEY, entity[SCRIPT_KEY].toString(), *it);
|
||||||
|
}
|
||||||
|
if (entity.contains(SERVER_SCRIPTS_KEY)) {
|
||||||
|
// TODO: serverScripts can be multiple scripts, need to handle that
|
||||||
|
}
|
||||||
|
|
||||||
|
// Materials
|
||||||
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,48 +352,6 @@ void DomainBaker::enumerateEntities() {
|
||||||
emit bakeProgress(0, _totalNumberOfSubBakes);
|
emit bakeProgress(0, _totalNumberOfSubBakes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainBaker::bakeSkybox(QUrl skyboxURL, QJsonValueRef entity) {
|
|
||||||
|
|
||||||
auto skyboxFileName = skyboxURL.fileName();
|
|
||||||
|
|
||||||
static const QStringList BAKEABLE_SKYBOX_EXTENSIONS {
|
|
||||||
".jpg", ".png", ".gif", ".bmp", ".pbm", ".pgm", ".ppm", ".xbm", ".xpm", ".svg"
|
|
||||||
};
|
|
||||||
auto completeLowerExtension = skyboxFileName.mid(skyboxFileName.indexOf('.')).toLower();
|
|
||||||
|
|
||||||
if (BAKEABLE_SKYBOX_EXTENSIONS.contains(completeLowerExtension)) {
|
|
||||||
// grab a clean version of the URL without a query or fragment
|
|
||||||
skyboxURL = skyboxURL.adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment);
|
|
||||||
|
|
||||||
// setup a texture baker for this URL, as long as we aren't baking a skybox already
|
|
||||||
if (!_skyboxBakers.contains(skyboxURL)) {
|
|
||||||
// setup a baker for this skybox
|
|
||||||
|
|
||||||
QSharedPointer<TextureBaker> skyboxBaker {
|
|
||||||
new TextureBaker(skyboxURL, image::TextureUsage::CUBE_TEXTURE, _contentOutputPath),
|
|
||||||
&TextureBaker::deleteLater
|
|
||||||
};
|
|
||||||
|
|
||||||
// make sure our handler is called when the skybox baker is done
|
|
||||||
connect(skyboxBaker.data(), &TextureBaker::finished, this, &DomainBaker::handleFinishedSkyboxBaker);
|
|
||||||
|
|
||||||
// insert it into our bakers hash so we hold a strong pointer to it
|
|
||||||
_skyboxBakers.insert(skyboxURL, skyboxBaker);
|
|
||||||
|
|
||||||
// move the baker to a worker thread and kickoff the bake
|
|
||||||
skyboxBaker->moveToThread(Oven::instance().getNextWorkerThread());
|
|
||||||
QMetaObject::invokeMethod(skyboxBaker.data(), "bake");
|
|
||||||
|
|
||||||
// keep track of the total number of baking entities
|
|
||||||
++_totalNumberOfSubBakes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add this QJsonValueRef to our multi hash so that it can re-write the skybox URL
|
|
||||||
// to the baked version once the baker is complete
|
|
||||||
_entitiesNeedingRewrite.insert(skyboxURL, entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DomainBaker::handleFinishedModelBaker() {
|
void DomainBaker::handleFinishedModelBaker() {
|
||||||
auto baker = qobject_cast<ModelBaker*>(sender());
|
auto baker = qobject_cast<ModelBaker*>(sender());
|
||||||
|
|
||||||
|
@ -275,62 +360,51 @@ void DomainBaker::handleFinishedModelBaker() {
|
||||||
// this FBXBaker is done and everything went according to plan
|
// this FBXBaker is done and everything went according to plan
|
||||||
qDebug() << "Re-writing entity references to" << baker->getModelURL();
|
qDebug() << "Re-writing entity references to" << baker->getModelURL();
|
||||||
|
|
||||||
// enumerate the QJsonRef values for the URL of this FBX from our multi hash of
|
// 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);
|
||||||
|
}
|
||||||
|
QUrl newURL = _destinationPath.resolved(relativeFBXFilePath);
|
||||||
|
|
||||||
|
// enumerate the QJsonRef values for the URL of this model from our multi hash of
|
||||||
// entity objects needing a URL re-write
|
// entity objects needing a URL re-write
|
||||||
for (QJsonValueRef entityValue : _entitiesNeedingRewrite.values(baker->getModelURL())) {
|
for (auto propertyEntityPair : _entitiesNeedingRewrite.values(baker->getModelURL())) {
|
||||||
|
QString property = propertyEntityPair.first;
|
||||||
// convert the entity QJsonValueRef to a QJsonObject so we can modify its URL
|
// convert the entity QJsonValueRef to a QJsonObject so we can modify its URL
|
||||||
auto entity = entityValue.toObject();
|
auto entity = propertyEntityPair.second.toObject();
|
||||||
|
|
||||||
// grab the old URL
|
if (!property.contains(".")) {
|
||||||
QUrl oldModelURL { entity[ENTITY_MODEL_URL_KEY].toString() };
|
// grab the old URL
|
||||||
|
QUrl oldURL = entity[property].toString();
|
||||||
|
|
||||||
// setup a new URL using the prefix we were passed
|
// copy the fragment and query, and user info from the old model URL
|
||||||
auto relativeFBXFilePath = baker->getBakedModelFilePath().remove(_contentOutputPath);
|
newURL.setQuery(oldURL.query());
|
||||||
if (relativeFBXFilePath.startsWith("/")) {
|
newURL.setFragment(oldURL.fragment());
|
||||||
relativeFBXFilePath = relativeFBXFilePath.right(relativeFBXFilePath.length() - 1);
|
newURL.setUserInfo(oldURL.userInfo());
|
||||||
|
|
||||||
|
// set the new URL as the value in our temp QJsonObject
|
||||||
|
entity[property] = newURL.toString();
|
||||||
|
} else {
|
||||||
|
// Group property
|
||||||
|
QStringList propertySplit = property.split(".");
|
||||||
|
assert(propertySplit.length() == 2);
|
||||||
|
// grab the old URL
|
||||||
|
auto oldObject = entity[propertySplit[0]].toObject();
|
||||||
|
QUrl oldURL = oldObject[propertySplit[1]].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
|
||||||
|
oldObject[propertySplit[1]] = newURL.toString();
|
||||||
|
entity[propertySplit[0]] = oldObject;
|
||||||
}
|
}
|
||||||
QUrl newModelURL = _destinationPath.resolved(relativeFBXFilePath);
|
|
||||||
|
|
||||||
// copy the fragment and query, and user info from the old model URL
|
|
||||||
newModelURL.setQuery(oldModelURL.query());
|
|
||||||
newModelURL.setFragment(oldModelURL.fragment());
|
|
||||||
newModelURL.setUserInfo(oldModelURL.userInfo());
|
|
||||||
|
|
||||||
// set the new model URL as the value in our temp QJsonObject
|
|
||||||
entity[ENTITY_MODEL_URL_KEY] = newModelURL.toString();
|
|
||||||
|
|
||||||
// check if the entity also had an animation at the same URL
|
|
||||||
// in which case it should be replaced with our baked model URL too
|
|
||||||
const QString ENTITY_ANIMATION_KEY = "animation";
|
|
||||||
const QString ENTITIY_ANIMATION_URL_KEY = "url";
|
|
||||||
|
|
||||||
if (entity.contains(ENTITY_ANIMATION_KEY)) {
|
|
||||||
auto animationObject = entity[ENTITY_ANIMATION_KEY].toObject();
|
|
||||||
|
|
||||||
if (animationObject.contains(ENTITIY_ANIMATION_URL_KEY)) {
|
|
||||||
// grab the old animation URL
|
|
||||||
QUrl oldAnimationURL { animationObject[ENTITIY_ANIMATION_URL_KEY].toString() };
|
|
||||||
|
|
||||||
// check if its stripped down version matches our stripped down model URL
|
|
||||||
if (oldAnimationURL.matches(oldModelURL, QUrl::RemoveQuery | QUrl::RemoveFragment)) {
|
|
||||||
// the animation URL matched the old model URL, so make the animation URL point to the baked FBX
|
|
||||||
// with its original query and fragment
|
|
||||||
auto newAnimationURL = _destinationPath.resolved(relativeFBXFilePath);
|
|
||||||
newAnimationURL.setQuery(oldAnimationURL.query());
|
|
||||||
newAnimationURL.setFragment(oldAnimationURL.fragment());
|
|
||||||
newAnimationURL.setUserInfo(oldAnimationURL.userInfo());
|
|
||||||
|
|
||||||
animationObject[ENTITIY_ANIMATION_URL_KEY] = newAnimationURL.toString();
|
|
||||||
|
|
||||||
// replace the animation object in the entity object
|
|
||||||
entity[ENTITY_ANIMATION_KEY] = animationObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace our temp object with the value referenced by our QJsonValueRef
|
// replace our temp object with the value referenced by our QJsonValueRef
|
||||||
entityValue = entity;
|
propertyEntityPair.second = entity;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// this model failed to bake - this doesn't fail the entire bake but we need to add
|
// this model failed to bake - this doesn't fail the entire bake but we need to add
|
||||||
|
@ -352,7 +426,7 @@ void DomainBaker::handleFinishedModelBaker() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainBaker::handleFinishedSkyboxBaker() {
|
void DomainBaker::handleFinishedTextureBaker() {
|
||||||
auto baker = qobject_cast<TextureBaker*>(sender());
|
auto baker = qobject_cast<TextureBaker*>(sender());
|
||||||
|
|
||||||
if (baker) {
|
if (baker) {
|
||||||
|
@ -360,36 +434,46 @@ void DomainBaker::handleFinishedSkyboxBaker() {
|
||||||
// this FBXBaker is done and everything went according to plan
|
// this FBXBaker is done and everything went according to plan
|
||||||
qDebug() << "Re-writing entity references to" << baker->getTextureURL();
|
qDebug() << "Re-writing entity references to" << baker->getTextureURL();
|
||||||
|
|
||||||
// enumerate the QJsonRef values for the URL of this FBX from our multi hash of
|
auto newURL = _destinationPath.resolved(baker->getMetaTextureFileName());
|
||||||
|
|
||||||
|
// enumerate the QJsonRef values for the URL of this texture from our multi hash of
|
||||||
// entity objects needing a URL re-write
|
// entity objects needing a URL re-write
|
||||||
for (QJsonValueRef entityValue : _entitiesNeedingRewrite.values(baker->getTextureURL())) {
|
for (auto propertyEntityPair : _entitiesNeedingRewrite.values(baker->getTextureURL())) {
|
||||||
|
QString property = propertyEntityPair.first;
|
||||||
// convert the entity QJsonValueRef to a QJsonObject so we can modify its URL
|
// convert the entity QJsonValueRef to a QJsonObject so we can modify its URL
|
||||||
auto entity = entityValue.toObject();
|
auto entity = propertyEntityPair.second.toObject();
|
||||||
|
|
||||||
if (entity.contains(ENTITY_SKYBOX_KEY)) {
|
if (!property.contains(".")) {
|
||||||
auto skyboxObject = entity[ENTITY_SKYBOX_KEY].toObject();
|
// grab the old URL
|
||||||
|
QUrl oldURL = entity[property].toString();
|
||||||
|
|
||||||
if (skyboxObject.contains(ENTITY_SKYBOX_URL_KEY)) {
|
// copy the fragment and query, and user info from the old model URL
|
||||||
if (rewriteSkyboxURL(skyboxObject[ENTITY_SKYBOX_URL_KEY], baker)) {
|
newURL.setQuery(oldURL.query());
|
||||||
// we re-wrote the URL, replace the skybox object referenced by the entity object
|
newURL.setFragment(oldURL.fragment());
|
||||||
entity[ENTITY_SKYBOX_KEY] = skyboxObject;
|
newURL.setUserInfo(oldURL.userInfo());
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity.contains(ENTITY_KEYLIGHT_KEY)) {
|
// set the new URL as the value in our temp QJsonObject
|
||||||
auto ambientObject = entity[ENTITY_KEYLIGHT_KEY].toObject();
|
entity[property] = newURL.toString();
|
||||||
|
} else {
|
||||||
|
// Group property
|
||||||
|
QStringList propertySplit = property.split(".");
|
||||||
|
assert(propertySplit.length() == 2);
|
||||||
|
// grab the old URL
|
||||||
|
auto oldObject = entity[propertySplit[0]].toObject();
|
||||||
|
QUrl oldURL = oldObject[propertySplit[1]].toString();
|
||||||
|
|
||||||
if (ambientObject.contains(ENTITY_KEYLIGHT_AMBIENT_URL_KEY)) {
|
// copy the fragment and query, and user info from the old model URL
|
||||||
if (rewriteSkyboxURL(ambientObject[ENTITY_KEYLIGHT_AMBIENT_URL_KEY], baker)) {
|
newURL.setQuery(oldURL.query());
|
||||||
// we re-wrote the URL, replace the ambient object referenced by the entity object
|
newURL.setFragment(oldURL.fragment());
|
||||||
entity[ENTITY_KEYLIGHT_KEY] = ambientObject;
|
newURL.setUserInfo(oldURL.userInfo());
|
||||||
}
|
|
||||||
}
|
// set the new URL as the value in our temp QJsonObject
|
||||||
|
oldObject[propertySplit[1]] = newURL.toString();
|
||||||
|
entity[propertySplit[0]] = oldObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace our temp object with the value referenced by our QJsonValueRef
|
// replace our temp object with the value referenced by our QJsonValueRef
|
||||||
entityValue = entity;
|
propertyEntityPair.second = entity;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// this skybox failed to bake - this doesn't fail the entire bake but we need to add the errors from
|
// this skybox failed to bake - this doesn't fail the entire bake but we need to add the errors from
|
||||||
|
@ -401,7 +485,7 @@ void DomainBaker::handleFinishedSkyboxBaker() {
|
||||||
_entitiesNeedingRewrite.remove(baker->getTextureURL());
|
_entitiesNeedingRewrite.remove(baker->getTextureURL());
|
||||||
|
|
||||||
// drop our shared pointer to this baker so that it gets cleaned up
|
// drop our shared pointer to this baker so that it gets cleaned up
|
||||||
_skyboxBakers.remove(baker->getTextureURL());
|
_textureBakers.remove(baker->getTextureURL());
|
||||||
|
|
||||||
// emit progress to tell listeners how many models we have baked
|
// emit progress to tell listeners how many models we have baked
|
||||||
emit bakeProgress(++_completedSubBakes, _totalNumberOfSubBakes);
|
emit bakeProgress(++_completedSubBakes, _totalNumberOfSubBakes);
|
||||||
|
@ -411,23 +495,72 @@ void DomainBaker::handleFinishedSkyboxBaker() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DomainBaker::rewriteSkyboxURL(QJsonValueRef urlValue, TextureBaker* baker) {
|
void DomainBaker::handleFinishedScriptBaker() {
|
||||||
// grab the old skybox URL
|
auto baker = qobject_cast<JSBaker*>(sender());
|
||||||
QUrl oldSkyboxURL { urlValue.toString() };
|
|
||||||
|
|
||||||
if (oldSkyboxURL.matches(baker->getTextureURL(), QUrl::RemoveQuery | QUrl::RemoveFragment)) {
|
if (baker) {
|
||||||
// change the URL to point to the baked texture with its original query and fragment
|
if (!baker->hasErrors()) {
|
||||||
|
// this FBXBaker is done and everything went according to plan
|
||||||
|
qDebug() << "Re-writing entity references to" << baker->getJSPath();
|
||||||
|
|
||||||
auto newSkyboxURL = _destinationPath.resolved(baker->getMetaTextureFileName());
|
auto newURL = _destinationPath.resolved(baker->getBakedJSFilePath());
|
||||||
newSkyboxURL.setQuery(oldSkyboxURL.query());
|
|
||||||
newSkyboxURL.setFragment(oldSkyboxURL.fragment());
|
|
||||||
newSkyboxURL.setUserInfo(oldSkyboxURL.userInfo());
|
|
||||||
|
|
||||||
urlValue = newSkyboxURL.toString();
|
// enumerate the QJsonRef values for the URL of this script from our multi hash of
|
||||||
|
// entity objects needing a URL re-write
|
||||||
|
for (auto propertyEntityPair : _entitiesNeedingRewrite.values(baker->getJSPath())) {
|
||||||
|
QString property = propertyEntityPair.first;
|
||||||
|
// convert the entity QJsonValueRef to a QJsonObject so we can modify its URL
|
||||||
|
auto entity = propertyEntityPair.second.toObject();
|
||||||
|
|
||||||
return true;
|
if (!property.contains(".")) {
|
||||||
} else {
|
// grab the old URL
|
||||||
return false;
|
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
|
||||||
|
entity[property] = newURL.toString();
|
||||||
|
} else {
|
||||||
|
// Group property
|
||||||
|
QStringList propertySplit = property.split(".");
|
||||||
|
assert(propertySplit.length() == 2);
|
||||||
|
// grab the old URL
|
||||||
|
auto oldObject = entity[propertySplit[0]].toObject();
|
||||||
|
QUrl oldURL = oldObject[propertySplit[1]].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
|
||||||
|
oldObject[propertySplit[1]] = newURL.toString();
|
||||||
|
entity[propertySplit[0]] = oldObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace our temp object with the value referenced by our QJsonValueRef
|
||||||
|
propertyEntityPair.second = entity;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// this model failed to bake - this doesn't fail the entire bake but we need to add
|
||||||
|
// the errors from the model to our warnings
|
||||||
|
_warningList << baker->getErrors();
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the baked URL from the multi hash of entities needing a re-write
|
||||||
|
_entitiesNeedingRewrite.remove(baker->getJSPath());
|
||||||
|
|
||||||
|
// drop our shared pointer to this baker so that it gets cleaned up
|
||||||
|
_scriptBakers.remove(baker->getJSPath());
|
||||||
|
|
||||||
|
// emit progress to tell listeners how many models we have baked
|
||||||
|
emit bakeProgress(++_completedSubBakes, _totalNumberOfSubBakes);
|
||||||
|
|
||||||
|
// check if this was the last model we needed to re-write and if we are done now
|
||||||
|
checkIfRewritingComplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,4 +613,3 @@ void DomainBaker::writeNewEntitiesFile() {
|
||||||
|
|
||||||
qDebug() << "Exported entities file with baked model URLs to" << bakedEntitiesFilePath;
|
qDebug() << "Exported entities file with baked model URLs to" << bakedEntitiesFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,9 @@
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
#include "Baker.h"
|
#include "Baker.h"
|
||||||
#include "FBXBaker.h"
|
#include "ModelBaker.h"
|
||||||
#include "TextureBaker.h"
|
#include "TextureBaker.h"
|
||||||
|
#include "JSBaker.h"
|
||||||
|
|
||||||
class DomainBaker : public Baker {
|
class DomainBaker : public Baker {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -38,7 +39,8 @@ signals:
|
||||||
private slots:
|
private slots:
|
||||||
virtual void bake() override;
|
virtual void bake() override;
|
||||||
void handleFinishedModelBaker();
|
void handleFinishedModelBaker();
|
||||||
void handleFinishedSkyboxBaker();
|
void handleFinishedTextureBaker();
|
||||||
|
void handleFinishedScriptBaker();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupOutputFolder();
|
void setupOutputFolder();
|
||||||
|
@ -47,9 +49,6 @@ private:
|
||||||
void checkIfRewritingComplete();
|
void checkIfRewritingComplete();
|
||||||
void writeNewEntitiesFile();
|
void writeNewEntitiesFile();
|
||||||
|
|
||||||
void bakeSkybox(QUrl skyboxURL, QJsonValueRef entity);
|
|
||||||
bool rewriteSkyboxURL(QJsonValueRef urlValue, TextureBaker* baker);
|
|
||||||
|
|
||||||
QUrl _localEntitiesFileURL;
|
QUrl _localEntitiesFileURL;
|
||||||
QString _domainName;
|
QString _domainName;
|
||||||
QString _baseOutputPath;
|
QString _baseOutputPath;
|
||||||
|
@ -62,14 +61,17 @@ private:
|
||||||
QJsonArray _entities;
|
QJsonArray _entities;
|
||||||
|
|
||||||
QHash<QUrl, QSharedPointer<ModelBaker>> _modelBakers;
|
QHash<QUrl, QSharedPointer<ModelBaker>> _modelBakers;
|
||||||
QHash<QUrl, QSharedPointer<TextureBaker>> _skyboxBakers;
|
QHash<QUrl, QSharedPointer<TextureBaker>> _textureBakers;
|
||||||
|
QHash<QUrl, QSharedPointer<JSBaker>> _scriptBakers;
|
||||||
|
|
||||||
QMultiHash<QUrl, QJsonValueRef> _entitiesNeedingRewrite;
|
QMultiHash<QUrl, std::pair<QString, QJsonValueRef>> _entitiesNeedingRewrite;
|
||||||
|
|
||||||
int _totalNumberOfSubBakes { 0 };
|
int _totalNumberOfSubBakes { 0 };
|
||||||
int _completedSubBakes { 0 };
|
int _completedSubBakes { 0 };
|
||||||
|
|
||||||
bool _shouldRebakeOriginals { false };
|
void addModelBaker(const QString& property, const QString& url, QJsonValueRef& jsonRef);
|
||||||
|
void addTextureBaker(const QString& property, const QString& url, image::TextureUsage::Type type, QJsonValueRef& jsonRef);
|
||||||
|
void addScriptBaker(const QString& property, const QString& url, QJsonValueRef& jsonRef);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_DomainBaker_h
|
#endif // hifi_DomainBaker_h
|
||||||
|
|
|
@ -116,7 +116,7 @@ void ModelBakeWidget::chooseFileButtonClicked() {
|
||||||
startDir = QDir::homePath();
|
startDir = QDir::homePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto selectedFiles = QFileDialog::getOpenFileNames(this, "Choose Model", startDir, "Models (*.fbx *.obj)");
|
auto selectedFiles = QFileDialog::getOpenFileNames(this, "Choose Model", startDir, "Models (*.fbx *.obj *.gltf *.fst)");
|
||||||
|
|
||||||
if (!selectedFiles.isEmpty()) {
|
if (!selectedFiles.isEmpty()) {
|
||||||
// set the contents of the model file text box to be the path to the selected file
|
// set the contents of the model file text box to be the path to the selected file
|
||||||
|
@ -165,21 +165,20 @@ void ModelBakeWidget::bakeButtonClicked() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure we have a valid output directory
|
||||||
|
QDir outputDirectory(_outputDirLineEdit->text());
|
||||||
|
if (!outputDirectory.exists()) {
|
||||||
|
QMessageBox::warning(this, "Unable to create directory", "Unable to create output directory. Please create it manually or choose a different directory.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// split the list from the model line edit to see how many models we need to bake
|
// split the list from the model line edit to see how many models we need to bake
|
||||||
auto fileURLStrings = _modelLineEdit->text().split(',');
|
auto fileURLStrings = _modelLineEdit->text().split(',');
|
||||||
foreach (QString fileURLString, fileURLStrings) {
|
foreach (QString fileURLString, fileURLStrings) {
|
||||||
// construct a URL from the path in the model file text box
|
// construct a URL from the path in the model file text box
|
||||||
QUrl modelToBakeURL(fileURLString);
|
QUrl modelToBakeURL(fileURLString);
|
||||||
|
|
||||||
// make sure we have a valid output directory
|
QUrl bakeableModelURL = getBakeableModelURL(QUrl(modelToBakeURL));
|
||||||
QDir outputDirectory(_outputDirLineEdit->text());
|
|
||||||
if (!outputDirectory.exists()) {
|
|
||||||
QMessageBox::warning(this, "Unable to create directory", "Unable to create output directory. Please create it manually or choose a different directory.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl bakeableModelURL = getBakeableModelURL(QUrl(modelToBakeURL), false);
|
|
||||||
|
|
||||||
if (!bakeableModelURL.isEmpty()) {
|
if (!bakeableModelURL.isEmpty()) {
|
||||||
auto getWorkerThreadCallback = []() -> QThread* {
|
auto getWorkerThreadCallback = []() -> QThread* {
|
||||||
return Oven::instance().getNextWorkerThread();
|
return Oven::instance().getNextWorkerThread();
|
||||||
|
|
Loading…
Reference in a new issue