working on material baker

This commit is contained in:
SamGondelman 2019-02-26 15:02:13 -08:00 committed by sabrina-shanman
parent 4965adbc2f
commit 94de0c12bc
13 changed files with 491 additions and 78 deletions

View file

@ -1,7 +1,7 @@
set(TARGET_NAME baking)
setup_hifi_library(Concurrent)
link_hifi_libraries(shared graphics networking ktx image fbx)
link_hifi_libraries(shared shaders graphics networking material-networking ktx image fbx)
include_hifi_library_headers(gpu)
include_hifi_library_headers(hfm)

View file

@ -0,0 +1,118 @@
//
// MaterialBaker.cpp
// libraries/baking/src
//
// Created by Sam Gondelman on 2/26/2019
// 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 "MaterialBaker.h"
#include "QJsonObject"
#include "QJsonDocument"
#include "MaterialBakingLoggingCategory.h"
#include <SharedUtil.h>
#include <PathUtils.h>
MaterialBaker::MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir) :
_materialData(materialData),
_isURL(isURL),
_bakedOutputDir(bakedOutputDir)
{
}
void MaterialBaker::bake() {
qDebug(material_baking) << "Material Baker" << _materialData << "bake starting";
// once our script is loaded, kick off a the processing
connect(this, &MaterialBaker::originalMaterialLoaded, this, &MaterialBaker::processMaterial);
if (!_materialResource) {
// first load the material (either locally or remotely)
loadMaterial();
} else {
// we already have a material passed to us, use that
if (_materialResource->isLoaded()) {
emit originalMaterialLoaded();
} else {
connect(_materialResource.data(), &Resource::finished, this, &MaterialBaker::originalMaterialLoaded);
}
}
}
void MaterialBaker::loadMaterial() {
if (!_isURL) {
qCDebug(material_baking) << "Loading local material" << _materialData;
_materialResource = NetworkMaterialResourcePointer(new NetworkMaterialResource());
// TODO: add baseURL to allow these to reference relative files next to them
_materialResource->parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromVariant(_materialData), QUrl());
} else {
qCDebug(material_baking) << "Downloading material" << _materialData;
_materialResource = MaterialCache::instance().getMaterial(_materialData);
}
if (_materialResource) {
if (_materialResource->isLoaded()) {
emit originalMaterialLoaded();
} else {
connect(_materialResource.data(), &Resource::finished, this, &MaterialBaker::originalMaterialLoaded);
}
} else {
handleError("Error loading " + _materialData);
}
}
void MaterialBaker::processMaterial() {
if (!_materialResource || _materialResource->parsedMaterials.networkMaterials.size() == 0) {
handleError("Error processing " + _materialData);
}
_numTexturesToLoad = _materialResource->parsedMaterials.networkMaterials.size();
_numTexturesLoaded = 0;
for (auto networkMaterial : _materialResource->parsedMaterials.networkMaterials) {
if (networkMaterial.second) {
auto textureMaps = networkMaterial.second->getTextureMaps();
for (auto textureMap : textureMaps) {
if (textureMap.second && textureMap.second->getTextureSource()) {
auto texture = textureMap.second->getTextureSource();
graphics::Material::MapChannel mapChannel = textureMap.first;
qDebug() << "boop" << mapChannel << texture->getUrl();
}
}
}
}
}
void MaterialBaker::outputMaterial() {
//if (_isURL) {
// auto fileName = _materialData;
// auto baseName = fileName.left(fileName.lastIndexOf('.'));
// auto bakedFilename = baseName + BAKED_MATERIAL_EXTENSION;
// _bakedMaterialData = _bakedOutputDir + "/" + bakedFilename;
// QFile bakedFile;
// bakedFile.setFileName(_bakedMaterialData);
// if (!bakedFile.open(QIODevice::WriteOnly)) {
// handleError("Error opening " + _bakedMaterialData + " for writing");
// return;
// }
// bakedFile.write(outputMaterial);
// // Export successful
// _outputFiles.push_back(_bakedMaterialData);
// qCDebug(material_baking) << "Exported" << _materialData << "to" << _bakedMaterialData;
//}
// emit signal to indicate the material baking is finished
emit finished();
}

View file

@ -0,0 +1,58 @@
//
// MaterialBaker.h
// libraries/baking/src
//
// Created by Sam Gondelman on 2/26/2019
// 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_MaterialBaker_h
#define hifi_MaterialBaker_h
#include "Baker.h"
#include "TextureBaker.h"
#include <material-networking/MaterialCache.h>
static const QString BAKED_MATERIAL_EXTENSION = ".baked.json";
class MaterialBaker : public Baker {
Q_OBJECT
public:
MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir);
QString getMaterialData() const { return _materialData; }
bool isURL() const { return _isURL; }
QString getBakedMaterialData() const { return _bakedMaterialData; }
public slots:
virtual void bake() override;
signals:
void originalMaterialLoaded();
private slots:
void processMaterial();
void outputMaterial();
private:
void loadMaterial();
QString _materialData;
bool _isURL;
NetworkMaterialResourcePointer _materialResource;
size_t _numTexturesToLoad { 0 };
size_t _numTexturesLoaded { 0 };
QHash<QUrl, QSharedPointer<TextureBaker>> _textureBakers;
QString _bakedOutputDir;
QString _bakedMaterialData;
};
#endif // !hifi_MaterialBaker_h

View file

@ -0,0 +1,14 @@
//
// MaterialBakingLoggingCategory.cpp
// libraries/baking/src
//
// Created by Sam Gondelman on 2/26/2019
// 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 "MaterialBakingLoggingCategory.h"
Q_LOGGING_CATEGORY(material_baking, "hifi.material-baking");

View file

@ -0,0 +1,19 @@
//
// MaterialBakingLoggingCategory.h
// libraries/baking/src
//
// Created by Sam Gondelman on 2/26/2019
// 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_MaterialBakingLoggingCategory_h
#define hifi_MaterialBakingLoggingCategory_h
#include <QtCore/QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(material_baking)
#endif // hifi_MaterialBakingLoggingCategory_h

View file

@ -96,6 +96,8 @@ namespace scriptable {
bool defaultFallthrough;
std::unordered_map<uint, bool> propertyFallthroughs; // not actually exposed to script
graphics::MaterialKey key { 0 };
};
/**jsdoc

View file

@ -364,20 +364,81 @@ namespace scriptable {
obj.setProperty("model", material.model);
const QScriptValue FALLTHROUGH("fallthrough");
obj.setProperty("opacity", material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_VAL_BIT) ? FALLTHROUGH : material.opacity);
obj.setProperty("roughness", material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT) ? FALLTHROUGH : material.roughness);
obj.setProperty("metallic", material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT) ? FALLTHROUGH : material.metallic);
obj.setProperty("scattering", material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT) ? FALLTHROUGH : material.scattering);
obj.setProperty("unlit", material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT) ? FALLTHROUGH : material.unlit);
obj.setProperty("emissive", material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_VAL_BIT) ? FALLTHROUGH : vec3ColorToScriptValue(engine, material.emissive));
obj.setProperty("albedo", material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_VAL_BIT) ? FALLTHROUGH : vec3ColorToScriptValue(engine, material.albedo));
if (material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_VAL_BIT)) {
obj.setProperty("opacity", FALLTHROUGH);
} else if (material.key.isTranslucentFactor()) {
obj.setProperty("opacity", material.opacity);
}
obj.setProperty("emissiveMap", material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_MAP_BIT) ? FALLTHROUGH : material.emissiveMap);
obj.setProperty("albedoMap", material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_MAP_BIT) ? FALLTHROUGH : material.albedoMap);
obj.setProperty("opacityMap", material.opacityMap);
obj.setProperty("occlusionMap", material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT) ? FALLTHROUGH : material.occlusionMap);
obj.setProperty("lightmapMap", material.propertyFallthroughs.at(graphics::MaterialKey::LIGHTMAP_MAP_BIT) ? FALLTHROUGH : material.lightmapMap);
obj.setProperty("scatteringMap", material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT) ? FALLTHROUGH : material.scatteringMap);
if (material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT)) {
obj.setProperty("roughness", FALLTHROUGH);
} else if (material.key.isGlossy()) {
obj.setProperty("roughness", material.roughness);
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT)) {
obj.setProperty("metallic", FALLTHROUGH);
} else if (material.key.isMetallic()) {
obj.setProperty("metallic", material.metallic);
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT)) {
obj.setProperty("scattering", FALLTHROUGH);
} else if (material.key.isScattering()) {
obj.setProperty("scattering", material.scattering);
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT)) {
obj.setProperty("unlit", FALLTHROUGH);
} else if (material.key.isUnlit()) {
obj.setProperty("unlit", material.unlit);
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_VAL_BIT)) {
obj.setProperty("emissive", FALLTHROUGH);
} else if (material.key.isEmissive()) {
obj.setProperty("emissive", vec3ColorToScriptValue(engine, material.emissive));
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_VAL_BIT)) {
obj.setProperty("albedo", FALLTHROUGH);
} else if (material.key.isAlbedo()) {
obj.setProperty("albedo", vec3ColorToScriptValue(engine, material.albedo));
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_MAP_BIT)) {
obj.setProperty("emissiveMap", FALLTHROUGH);
} else if (!material.emissiveMap.isEmpty()) {
obj.setProperty("emissiveMap", material.emissiveMap);
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_MAP_BIT)) {
obj.setProperty("albedoMap", FALLTHROUGH);
} else if (!material.albedoMap.isEmpty()) {
obj.setProperty("albedoMap", material.albedoMap);
}
if (!material.opacityMap.isEmpty()) {
obj.setProperty("opacityMap", material.opacityMap);
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT)) {
obj.setProperty("occlusionMap", FALLTHROUGH);
} else if (!material.occlusionMap.isEmpty()) {
obj.setProperty("occlusionMap", material.occlusionMap);
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::LIGHTMAP_MAP_BIT)) {
obj.setProperty("lightmapMap", FALLTHROUGH);
} else if (!material.lightmapMap.isEmpty()) {
obj.setProperty("lightmapMap", material.lightmapMap);
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT)) {
obj.setProperty("scatteringMap", FALLTHROUGH);
} else if (!material.scatteringMap.isEmpty()) {
obj.setProperty("scatteringMap", material.scatteringMap);
}
// Only set one of each of these
if (material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT)) {

View file

@ -45,75 +45,80 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const
defaultFallthrough = material.defaultFallthrough;
propertyFallthroughs = material.propertyFallthroughs;
key = material.key;
return *this;
}
scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPointer& material) :
name(material->getName().c_str()),
model(material->getModel().c_str()),
opacity(material->getOpacity()),
roughness(material->getRoughness()),
metallic(material->getMetallic()),
scattering(material->getScattering()),
unlit(material->isUnlit()),
emissive(material->getEmissive()),
albedo(material->getAlbedo()),
defaultFallthrough(material->getDefaultFallthrough()),
propertyFallthroughs(material->getPropertyFallthroughs())
{
auto map = material->getTextureMap(graphics::Material::MapChannel::EMISSIVE_MAP);
if (map && map->getTextureSource()) {
emissiveMap = map->getTextureSource()->getUrl().toString();
}
scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPointer& material) {
if (material) {
name = material->getName().c_str();
model = material->getModel().c_str();
opacity = material->getOpacity();
roughness = material->getRoughness();
metallic = material->getMetallic();
scattering = material->getScattering();
unlit = material->isUnlit();
emissive = material->getEmissive();
albedo = material->getAlbedo();
defaultFallthrough = material->getDefaultFallthrough();
propertyFallthroughs = material->getPropertyFallthroughs();
key = material->getKey();
map = material->getTextureMap(graphics::Material::MapChannel::ALBEDO_MAP);
if (map && map->getTextureSource()) {
albedoMap = map->getTextureSource()->getUrl().toString();
if (map->useAlphaChannel()) {
opacityMap = albedoMap;
auto map = material->getTextureMap(graphics::Material::MapChannel::EMISSIVE_MAP);
if (map && map->getTextureSource()) {
emissiveMap = map->getTextureSource()->getUrl().toString();
}
}
map = material->getTextureMap(graphics::Material::MapChannel::METALLIC_MAP);
if (map && map->getTextureSource()) {
if (map->getTextureSource()->getType() == image::TextureUsage::Type::METALLIC_TEXTURE) {
metallicMap = map->getTextureSource()->getUrl().toString();
} else if (map->getTextureSource()->getType() == image::TextureUsage::Type::SPECULAR_TEXTURE) {
specularMap = map->getTextureSource()->getUrl().toString();
map = material->getTextureMap(graphics::Material::MapChannel::ALBEDO_MAP);
if (map && map->getTextureSource()) {
albedoMap = map->getTextureSource()->getUrl().toString();
if (map->useAlphaChannel()) {
opacityMap = albedoMap;
}
}
}
map = material->getTextureMap(graphics::Material::MapChannel::ROUGHNESS_MAP);
if (map && map->getTextureSource()) {
if (map->getTextureSource()->getType() == image::TextureUsage::Type::ROUGHNESS_TEXTURE) {
roughnessMap = map->getTextureSource()->getUrl().toString();
} else if (map->getTextureSource()->getType() == image::TextureUsage::Type::GLOSS_TEXTURE) {
glossMap = map->getTextureSource()->getUrl().toString();
map = material->getTextureMap(graphics::Material::MapChannel::METALLIC_MAP);
if (map && map->getTextureSource()) {
if (map->getTextureSource()->getType() == image::TextureUsage::Type::METALLIC_TEXTURE) {
metallicMap = map->getTextureSource()->getUrl().toString();
} else if (map->getTextureSource()->getType() == image::TextureUsage::Type::SPECULAR_TEXTURE) {
specularMap = map->getTextureSource()->getUrl().toString();
}
}
}
map = material->getTextureMap(graphics::Material::MapChannel::NORMAL_MAP);
if (map && map->getTextureSource()) {
if (map->getTextureSource()->getType() == image::TextureUsage::Type::NORMAL_TEXTURE) {
normalMap = map->getTextureSource()->getUrl().toString();
} else if (map->getTextureSource()->getType() == image::TextureUsage::Type::BUMP_TEXTURE) {
bumpMap = map->getTextureSource()->getUrl().toString();
map = material->getTextureMap(graphics::Material::MapChannel::ROUGHNESS_MAP);
if (map && map->getTextureSource()) {
if (map->getTextureSource()->getType() == image::TextureUsage::Type::ROUGHNESS_TEXTURE) {
roughnessMap = map->getTextureSource()->getUrl().toString();
} else if (map->getTextureSource()->getType() == image::TextureUsage::Type::GLOSS_TEXTURE) {
glossMap = map->getTextureSource()->getUrl().toString();
}
}
}
map = material->getTextureMap(graphics::Material::MapChannel::OCCLUSION_MAP);
if (map && map->getTextureSource()) {
occlusionMap = map->getTextureSource()->getUrl().toString();
}
map = material->getTextureMap(graphics::Material::MapChannel::NORMAL_MAP);
if (map && map->getTextureSource()) {
if (map->getTextureSource()->getType() == image::TextureUsage::Type::NORMAL_TEXTURE) {
normalMap = map->getTextureSource()->getUrl().toString();
} else if (map->getTextureSource()->getType() == image::TextureUsage::Type::BUMP_TEXTURE) {
bumpMap = map->getTextureSource()->getUrl().toString();
}
}
map = material->getTextureMap(graphics::Material::MapChannel::LIGHTMAP_MAP);
if (map && map->getTextureSource()) {
lightmapMap = map->getTextureSource()->getUrl().toString();
}
map = material->getTextureMap(graphics::Material::MapChannel::OCCLUSION_MAP);
if (map && map->getTextureSource()) {
occlusionMap = map->getTextureSource()->getUrl().toString();
}
map = material->getTextureMap(graphics::Material::MapChannel::SCATTERING_MAP);
if (map && map->getTextureSource()) {
scatteringMap = map->getTextureSource()->getUrl().toString();
map = material->getTextureMap(graphics::Material::MapChannel::LIGHTMAP_MAP);
if (map && map->getTextureSource()) {
lightmapMap = map->getTextureSource()->getUrl().toString();
}
map = material->getTextureMap(graphics::Material::MapChannel::SCATTERING_MAP);
if (map && map->getTextureSource()) {
scatteringMap = map->getTextureSource()->getUrl().toString();
}
}
}

View file

@ -2,7 +2,7 @@ set(TARGET_NAME oven)
setup_hifi_project(Widgets Gui Concurrent)
link_hifi_libraries(networking shared image gpu ktx fbx hfm baking graphics)
link_hifi_libraries(shared shaders image gpu ktx fbx hfm baking graphics networking material-networking)
setup_memory_debugger()

View file

@ -23,6 +23,7 @@
#include "baking/BakerLibrary.h"
#include "JSBaker.h"
#include "TextureBaker.h"
#include "MaterialBaker.h"
BakerCLI::BakerCLI(OvenCLIApplication* parent) : QObject(parent) {
@ -60,8 +61,8 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString&
_baker = std::unique_ptr<Baker> { new JSBaker(inputUrl, outputPath) };
_baker->moveToThread(Oven::instance().getNextWorkerThread());
} else if (type == MATERIAL_EXTENSION) {
//_baker = std::unique_ptr<Baker> { new MaterialBaker(inputUrl, outputPath) };
//_baker->moveToThread(Oven::instance().getNextWorkerThread());
_baker = std::unique_ptr<Baker> { new MaterialBaker(inputUrl.toDisplayString(), true, outputPath) };
_baker->moveToThread(Oven::instance().getNextWorkerThread());
} 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.)

View file

@ -187,8 +187,8 @@ void DomainBaker::addTextureBaker(const QString& property, const QString& url, i
// 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
// setup a baker for this texture
QSharedPointer<TextureBaker> textureBaker {
new TextureBaker(textureURL, type, _contentOutputPath),
&TextureBaker::deleteLater
@ -220,16 +220,16 @@ void DomainBaker::addScriptBaker(const QString& property, const QString& url, QJ
// 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
// setup a script baker for this URL, as long as we aren't baking a texture already
if (!_scriptBakers.contains(scriptURL)) {
// setup a baker for this texture
// setup a baker for this script
QSharedPointer<JSBaker> scriptBaker {
new JSBaker(scriptURL, _contentOutputPath),
&JSBaker::deleteLater
};
// make sure our handler is called when the texture baker is done
// make sure our handler is called when the script 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
@ -243,11 +243,48 @@ void DomainBaker::addScriptBaker(const QString& property, const QString& url, QJ
++_totalNumberOfSubBakes;
}
// add this QJsonValueRef to our multi hash so that it can re-write the texture URL
// add this QJsonValueRef to our multi hash so that it can re-write the script URL
// to the baked version once the baker is complete
_entitiesNeedingRewrite.insert(scriptURL, { property, jsonRef });
}
void DomainBaker::addMaterialBaker(const QString& property, const QString& data, bool isURL, QJsonValueRef& jsonRef) {
// grab a clean version of the URL without a query or fragment
QString materialData;
if (isURL) {
materialData = QUrl(data).adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment).toDisplayString();
} else {
materialData = data;
}
// setup a material baker for this URL, as long as we aren't baking a material already
if (!_materialBakers.contains(materialData)) {
// setup a baker for this material
QSharedPointer<MaterialBaker> materialBaker {
new MaterialBaker(data, isURL, _contentOutputPath),
&MaterialBaker::deleteLater
};
// make sure our handler is called when the material baker is done
connect(materialBaker.data(), &MaterialBaker::finished, this, &DomainBaker::handleFinishedMaterialBaker);
// insert it into our bakers hash so we hold a strong pointer to it
_materialBakers.insert(materialData, materialBaker);
// move the baker to a worker thread and kickoff the bake
materialBaker->moveToThread(Oven::instance().getNextWorkerThread());
QMetaObject::invokeMethod(materialBaker.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(materialData, { property, jsonRef });
}
// All the Entity Properties that can be baked
// ***************************************************************************************
@ -348,7 +385,12 @@ void DomainBaker::enumerateEntities() {
}
// Materials
// TODO
if (entity.contains(MATERIAL_URL_KEY)) {
addMaterialBaker(MATERIAL_URL_KEY, entity[MATERIAL_URL_KEY].toString(), true, *it);
}
if (entity.contains(MATERIAL_DATA_KEY)) {
addMaterialBaker(MATERIAL_DATA_KEY, entity[MATERIAL_URL_KEY].toString(), false, *it);
}
}
}
@ -568,6 +610,91 @@ void DomainBaker::handleFinishedScriptBaker() {
}
}
void DomainBaker::handleFinishedMaterialBaker() {
auto baker = qobject_cast<MaterialBaker*>(sender());
if (baker) {
if (!baker->hasErrors()) {
// this MaterialBaker is done and everything went according to plan
qDebug() << "Re-writing entity references to" << baker->getMaterialData();
QString newDataOrURL;
if (baker->isURL()) {
newDataOrURL = _destinationPath.resolved(baker->getBakedMaterialData()).toDisplayString();
} else {
newDataOrURL = baker->getBakedMaterialData();
}
// enumerate the QJsonRef values for the URL of this material from our multi hash of
// entity objects needing a URL re-write
for (auto propertyEntityPair : _entitiesNeedingRewrite.values(baker->getMaterialData())) {
QString property = propertyEntityPair.first;
// convert the entity QJsonValueRef to a QJsonObject so we can modify its URL
auto entity = propertyEntityPair.second.toObject();
if (!property.contains(".")) {
// grab the old URL
QUrl oldURL = entity[property].toString();
// copy the fragment and query, and user info from the old material data
if (baker->isURL()) {
QUrl newURL = newDataOrURL;
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 {
entity[property] = newDataOrURL;
}
} 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 material data
if (baker->isURL()) {
QUrl newURL = newDataOrURL;
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;
} else {
oldObject[propertySplit[1]] = newDataOrURL;
entity[propertySplit[0]] = oldObject;
}
}
// replace our temp object with the value referenced by our QJsonValueRef
propertyEntityPair.second = entity;
}
} else {
// this material failed to bake - this doesn't fail the entire bake but we need to add
// the errors from the material to our warnings
_warningList << baker->getErrors();
}
// remove the baked URL from the multi hash of entities needing a re-write
_entitiesNeedingRewrite.remove(baker->getMaterialData());
// drop our shared pointer to this baker so that it gets cleaned up
_materialBakers.remove(baker->getMaterialData());
// emit progress to tell listeners how many materials we have baked
emit bakeProgress(++_completedSubBakes, _totalNumberOfSubBakes);
// check if this was the last material we needed to re-write and if we are done now
checkIfRewritingComplete();
}
}
void DomainBaker::checkIfRewritingComplete() {
if (_entitiesNeedingRewrite.isEmpty()) {
writeNewEntitiesFile();

View file

@ -21,6 +21,7 @@
#include "ModelBaker.h"
#include "TextureBaker.h"
#include "JSBaker.h"
#include "MaterialBaker.h"
class DomainBaker : public Baker {
Q_OBJECT
@ -40,6 +41,7 @@ private slots:
void handleFinishedModelBaker();
void handleFinishedTextureBaker();
void handleFinishedScriptBaker();
void handleFinishedMaterialBaker();
private:
void setupOutputFolder();
@ -62,6 +64,7 @@ private:
QHash<QUrl, QSharedPointer<ModelBaker>> _modelBakers;
QHash<QUrl, QSharedPointer<TextureBaker>> _textureBakers;
QHash<QUrl, QSharedPointer<JSBaker>> _scriptBakers;
QHash<QUrl, QSharedPointer<MaterialBaker>> _materialBakers;
QMultiHash<QUrl, std::pair<QString, QJsonValueRef>> _entitiesNeedingRewrite;
@ -71,6 +74,7 @@ private:
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);
void addMaterialBaker(const QString& property, const QString& data, bool isURL, QJsonValueRef& jsonRef);
};
#endif // hifi_DomainBaker_h

View file

@ -63,6 +63,10 @@ void Oven::setupWorkerThreads(int numWorkerThreads) {
}
QThread* Oven::getNextWorkerThread() {
// FIXME: we assign these threads when we make the bakers, but if certain bakers finish quickly, we could end up
// in a situation where threads have finished and others have tons of work queued. Instead of assigning them at initialization,
// we should build a queue of bakers, and when threads finish, they can take the next available baker.
// Here we replicate some of the functionality of QThreadPool by giving callers an available worker thread to use.
// We can't use QThreadPool because we want to put QObjects with signals/slots on these threads.
// So instead we setup our own list of threads, up to one less than the ideal thread count