it's working!

This commit is contained in:
SamGondelman 2019-02-27 18:00:37 -08:00 committed by sabrina-shanman
parent 94de0c12bc
commit 1a1277e9e7
9 changed files with 203 additions and 55 deletions

View file

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

View file

@ -19,10 +19,17 @@
#include <SharedUtil.h>
#include <PathUtils.h>
#include <graphics-scripting/GraphicsScriptingInterface.h>
std::function<QThread*()> MaterialBaker::_getNextOvenWorkerThreadOperator;
static int materialNum = 0;
MaterialBaker::MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir) :
_materialData(materialData),
_isURL(isURL),
_bakedOutputDir(bakedOutputDir)
_bakedOutputDir(bakedOutputDir),
_textureOutputDir(bakedOutputDir + "/materialTextures/" + QString::number(materialNum++))
{
}
@ -51,7 +58,7 @@ void MaterialBaker::loadMaterial() {
_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());
_materialResource->parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(_materialData.toUtf8()), QUrl());
} else {
qCDebug(material_baking) << "Downloading material" << _materialData;
_materialResource = MaterialCache::instance().getMaterial(_materialData);
@ -71,47 +78,151 @@ void MaterialBaker::loadMaterial() {
void MaterialBaker::processMaterial() {
if (!_materialResource || _materialResource->parsedMaterials.networkMaterials.size() == 0) {
handleError("Error processing " + _materialData);
return;
}
_numTexturesToLoad = _materialResource->parsedMaterials.networkMaterials.size();
_numTexturesLoaded = 0;
if (QDir(_textureOutputDir).exists()) {
qWarning() << "Output path" << _textureOutputDir << "already exists. Continuing.";
} else {
qCDebug(material_baking) << "Creating materialTextures output folder" << _textureOutputDir;
if (!QDir().mkpath(_textureOutputDir)) {
handleError("Failed to create materialTextures output folder " + _textureOutputDir);
}
}
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;
auto texture = textureMap.second->getTextureSource();
qDebug() << "boop" << mapChannel << texture->getUrl();
QUrl url = texture->getUrl();
QString cleanURL = url.adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment).toDisplayString();
auto idx = cleanURL.lastIndexOf('.');
auto extension = idx >= 0 ? url.toDisplayString().mid(idx + 1).toLower() : "";
if (QImageReader::supportedImageFormats().contains(extension.toLatin1())) {
QUrl textureURL = url.adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment);
// FIXME: this isn't properly handling bumpMaps or glossMaps
static const std::unordered_map<graphics::Material::MapChannel, image::TextureUsage::Type> MAP_CHANNEL_TO_TEXTURE_USAGE_TYPE_MAP {
{ graphics::Material::MapChannel::EMISSIVE_MAP, image::TextureUsage::EMISSIVE_TEXTURE },
{ graphics::Material::MapChannel::ALBEDO_MAP, image::TextureUsage::ALBEDO_TEXTURE },
{ graphics::Material::MapChannel::METALLIC_MAP, image::TextureUsage::METALLIC_TEXTURE },
{ graphics::Material::MapChannel::ROUGHNESS_MAP, image::TextureUsage::ROUGHNESS_TEXTURE },
{ graphics::Material::MapChannel::NORMAL_MAP, image::TextureUsage::NORMAL_TEXTURE },
{ graphics::Material::MapChannel::OCCLUSION_MAP, image::TextureUsage::OCCLUSION_TEXTURE },
{ graphics::Material::MapChannel::LIGHTMAP_MAP, image::TextureUsage::LIGHTMAP_TEXTURE },
{ graphics::Material::MapChannel::SCATTERING_MAP, image::TextureUsage::SCATTERING_TEXTURE }
};
auto it = MAP_CHANNEL_TO_TEXTURE_USAGE_TYPE_MAP.find(mapChannel);
if (it == MAP_CHANNEL_TO_TEXTURE_USAGE_TYPE_MAP.end()) {
handleError("Unknown map channel");
return;
}
QPair<QUrl, image::TextureUsage::Type> textureKey = { textureURL, it->second };
if (!_textureBakers.contains(textureKey)) {
QSharedPointer<TextureBaker> textureBaker {
new TextureBaker(textureURL, it->second, _textureOutputDir),
&TextureBaker::deleteLater
};
textureBaker->setMapChannel(mapChannel);
connect(textureBaker.data(), &TextureBaker::finished, this, &MaterialBaker::handleFinishedTextureBaker);
_textureBakers.insert(textureKey, textureBaker);
textureBaker->moveToThread(_getNextOvenWorkerThreadOperator ? _getNextOvenWorkerThreadOperator() : thread());
QMetaObject::invokeMethod(textureBaker.data(), "bake");
}
_materialsNeedingRewrite.insert(textureKey, networkMaterial.second);
} else {
qCDebug(material_baking) << "Texture extension not supported: " << extension;
}
}
}
}
}
if (_textureBakers.empty()) {
outputMaterial();
}
}
void MaterialBaker::handleFinishedTextureBaker() {
auto baker = qobject_cast<TextureBaker*>(sender());
if (baker) {
QPair<QUrl, image::TextureUsage::Type> textureKey = { baker->getTextureURL(), baker->getTextureType() };
if (!baker->hasErrors()) {
// this TextureBaker is done and everything went according to plan
qCDebug(material_baking) << "Re-writing texture references to" << baker->getTextureURL();
auto newURL = QUrl(_textureOutputDir).resolved(baker->getMetaTextureFileName());
// Replace the old texture URLs
for (auto networkMaterial : _materialsNeedingRewrite.values(textureKey)) {
networkMaterial->getTextureMap(baker->getMapChannel())->getTextureSource()->setUrl(newURL);
}
} else {
// this texture failed to bake - this doesn't fail the entire bake but we need to add the errors from
// the texture to our warnings
_warningList << baker->getWarnings();
}
_materialsNeedingRewrite.remove(textureKey);
_textureBakers.remove(textureKey);
if (_textureBakers.empty()) {
outputMaterial();
}
}
}
void MaterialBaker::outputMaterial() {
//if (_isURL) {
// auto fileName = _materialData;
// auto baseName = fileName.left(fileName.lastIndexOf('.'));
// auto bakedFilename = baseName + BAKED_MATERIAL_EXTENSION;
if (_materialResource) {
QJsonDocument json;
if (_materialResource->parsedMaterials.networkMaterials.size() == 1) {
auto networkMaterial = _materialResource->parsedMaterials.networkMaterials.begin();
auto scriptableMaterial = scriptable::ScriptableMaterial(networkMaterial->second);
QVariant materialVariant = scriptable::scriptableMaterialToScriptValue(&_scriptEngine, scriptableMaterial).toVariant();
json = QJsonDocument::fromVariant(materialVariant);
} else {
QJsonArray materialArray;
for (auto networkMaterial : _materialResource->parsedMaterials.networkMaterials) {
auto scriptableMaterial = scriptable::ScriptableMaterial(networkMaterial.second);
QVariant materialVariant = scriptable::scriptableMaterialToScriptValue(&_scriptEngine, scriptableMaterial).toVariant();
materialArray.append(QJsonDocument::fromVariant(materialVariant).object());
}
json.setArray(materialArray);
}
// _bakedMaterialData = _bakedOutputDir + "/" + bakedFilename;
QByteArray outputMaterial = json.toJson(QJsonDocument::Compact);
if (_isURL) {
auto fileName = QUrl(_materialData).fileName();
auto baseName = fileName.left(fileName.lastIndexOf('.'));
auto bakedFilename = baseName + BAKED_MATERIAL_EXTENSION;
// QFile bakedFile;
// bakedFile.setFileName(_bakedMaterialData);
// if (!bakedFile.open(QIODevice::WriteOnly)) {
// handleError("Error opening " + _bakedMaterialData + " for writing");
// return;
// }
_bakedMaterialData = _bakedOutputDir + "/" + bakedFilename;
// bakedFile.write(outputMaterial);
QFile bakedFile;
bakedFile.setFileName(_bakedMaterialData);
if (!bakedFile.open(QIODevice::WriteOnly)) {
handleError("Error opening " + _bakedMaterialData + " for writing");
return;
}
// // Export successful
// _outputFiles.push_back(_bakedMaterialData);
// qCDebug(material_baking) << "Exported" << _materialData << "to" << _bakedMaterialData;
//}
bakedFile.write(outputMaterial);
// Export successful
_outputFiles.push_back(_bakedMaterialData);
qCDebug(material_baking) << "Exported" << _materialData << "to" << _bakedMaterialData;
} else {
_bakedMaterialData = QString(outputMaterial);
qCDebug(material_baking) << "Converted" << _materialData << "to" << _bakedMaterialData;
}
}
// emit signal to indicate the material baking is finished
emit finished();

View file

@ -29,6 +29,8 @@ public:
bool isURL() const { return _isURL; }
QString getBakedMaterialData() const { return _bakedMaterialData; }
static void setNextOvenWorkerThreadOperator(std::function<QThread*()> getNextOvenWorkerThreadOperator) { _getNextOvenWorkerThreadOperator = getNextOvenWorkerThreadOperator; }
public slots:
virtual void bake() override;
@ -38,6 +40,7 @@ signals:
private slots:
void processMaterial();
void outputMaterial();
void handleFinishedTextureBaker();
private:
void loadMaterial();
@ -46,13 +49,16 @@ private:
bool _isURL;
NetworkMaterialResourcePointer _materialResource;
size_t _numTexturesToLoad { 0 };
size_t _numTexturesLoaded { 0 };
QHash<QUrl, QSharedPointer<TextureBaker>> _textureBakers;
QHash<QPair<QUrl, image::TextureUsage::Type>, QSharedPointer<TextureBaker>> _textureBakers;
QMultiHash<QPair<QUrl, image::TextureUsage::Type>, std::shared_ptr<NetworkMaterial>> _materialsNeedingRewrite;
QString _bakedOutputDir;
QString _textureOutputDir;
QString _bakedMaterialData;
QScriptEngine _scriptEngine;
static std::function<QThread*()> _getNextOvenWorkerThreadOperator;
};
#endif // !hifi_MaterialBaker_h

View file

@ -128,7 +128,14 @@ void TextureBaker::processTexture() {
TextureMeta meta;
auto originalCopyFilePath = _outputDirectory.absoluteFilePath(_textureURL.fileName());
// If two textures have the same URL but are used differently, we need to process them separately
QString addMapChannel = QString::fromStdString("_" + std::to_string(_textureType));
_baseFilename += addMapChannel;
QString newFilename = _textureURL.fileName();
newFilename.replace(QString("."), addMapChannel + ".");
QString originalCopyFilePath = _outputDirectory.absoluteFilePath(newFilename);
{
QFile file { originalCopyFilePath };
if (!file.open(QIODevice::WriteOnly) || file.write(_originalTexture) == -1) {
@ -138,7 +145,7 @@ void TextureBaker::processTexture() {
// IMPORTANT: _originalTexture is empty past this point
_originalTexture.clear();
_outputFiles.push_back(originalCopyFilePath);
meta.original = _metaTexturePathPrefix + _textureURL.fileName();
meta.original = _metaTexturePathPrefix + newFilename;
}
auto buffer = std::static_pointer_cast<QIODevice>(std::make_shared<QFile>(originalCopyFilePath));

View file

@ -22,6 +22,8 @@
#include "Baker.h"
#include <material-networking/MaterialCache.h>
extern const QString BAKED_TEXTURE_KTX_EXT;
extern const QString BAKED_META_TEXTURE_SUFFIX;
@ -43,6 +45,10 @@ public:
static void setCompressionEnabled(bool enabled) { _compressionEnabled = enabled; }
void setMapChannel(graphics::Material::MapChannel mapChannel) { _mapChannel = mapChannel; }
graphics::Material::MapChannel getMapChannel() const { return _mapChannel; }
image::TextureUsage::Type getTextureType() const { return _textureType; }
public slots:
virtual void bake() override;
virtual void abort() override;
@ -60,6 +66,8 @@ private:
QUrl _textureURL;
QByteArray _originalTexture;
image::TextureUsage::Type _textureType;
graphics::Material::MapChannel _mapChannel;
bool _mapChannelSet { false };
QString _baseFilename;
QDir _outputDirectory;

View file

@ -363,56 +363,58 @@ namespace scriptable {
obj.setProperty("name", material.name);
obj.setProperty("model", material.model);
bool hasPropertyFallthroughs = !material.propertyFallthroughs.empty();
const QScriptValue FALLTHROUGH("fallthrough");
if (material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_VAL_BIT)) {
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_VAL_BIT)) {
obj.setProperty("opacity", FALLTHROUGH);
} else if (material.key.isTranslucentFactor()) {
obj.setProperty("opacity", material.opacity);
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT)) {
if (hasPropertyFallthroughs && 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)) {
if (hasPropertyFallthroughs && 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)) {
if (hasPropertyFallthroughs && 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)) {
if (hasPropertyFallthroughs && 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)) {
if (hasPropertyFallthroughs && 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)) {
if (hasPropertyFallthroughs && 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)) {
if (hasPropertyFallthroughs && 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)) {
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_MAP_BIT)) {
obj.setProperty("albedoMap", FALLTHROUGH);
} else if (!material.albedoMap.isEmpty()) {
obj.setProperty("albedoMap", material.albedoMap);
@ -422,26 +424,26 @@ namespace scriptable {
obj.setProperty("opacityMap", material.opacityMap);
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT)) {
if (hasPropertyFallthroughs && 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)) {
if (hasPropertyFallthroughs && 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)) {
if (hasPropertyFallthroughs && 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)) {
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT)) {
obj.setProperty("metallicMap", FALLTHROUGH);
} else if (!material.metallicMap.isEmpty()) {
obj.setProperty("metallicMap", material.metallicMap);
@ -449,7 +451,7 @@ namespace scriptable {
obj.setProperty("specularMap", material.specularMap);
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT)) {
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT)) {
obj.setProperty("roughnessMap", FALLTHROUGH);
} else if (!material.roughnessMap.isEmpty()) {
obj.setProperty("roughnessMap", material.roughnessMap);
@ -457,7 +459,7 @@ namespace scriptable {
obj.setProperty("glossMap", material.glossMap);
}
if (material.propertyFallthroughs.at(graphics::MaterialKey::NORMAL_MAP_BIT)) {
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::NORMAL_MAP_BIT)) {
obj.setProperty("normalMap", FALLTHROUGH);
} else if (!material.normalMap.isEmpty()) {
obj.setProperty("normalMap", material.normalMap);
@ -466,16 +468,16 @@ namespace scriptable {
}
// These need to be implemented, but set the fallthrough for now
if (material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM0)) {
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM0)) {
obj.setProperty("texCoordTransform0", FALLTHROUGH);
}
if (material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM1)) {
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM1)) {
obj.setProperty("texCoordTransform1", FALLTHROUGH);
}
if (material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) {
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) {
obj.setProperty("lightmapParams", FALLTHROUGH);
}
if (material.propertyFallthroughs.at(graphics::Material::MATERIAL_PARAMS)) {
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::MATERIAL_PARAMS)) {
obj.setProperty("materialParams", FALLTHROUGH);
}

View file

@ -103,6 +103,10 @@ private:
};
namespace scriptable {
QScriptValue scriptableMaterialToScriptValue(QScriptEngine* engine, const scriptable::ScriptableMaterial &material);
};
Q_DECLARE_METATYPE(glm::uint32)
Q_DECLARE_METATYPE(QVector<glm::uint32>)
Q_DECLARE_METATYPE(NestableType)

View file

@ -220,7 +220,7 @@ 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 script 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 script already
if (!_scriptBakers.contains(scriptURL)) {
// setup a baker for this script
@ -255,7 +255,7 @@ void DomainBaker::addMaterialBaker(const QString& property, const QString& data,
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)) {
@ -280,7 +280,7 @@ void DomainBaker::addMaterialBaker(const QString& property, const QString& data,
++_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 material URL
// to the baked version once the baker is complete
_entitiesNeedingRewrite.insert(materialData, { property, jsonRef });
}
@ -389,7 +389,7 @@ void DomainBaker::enumerateEntities() {
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);
addMaterialBaker(MATERIAL_DATA_KEY, entity[MATERIAL_DATA_KEY].toString(), false, *it);
}
}
}
@ -533,11 +533,11 @@ void DomainBaker::handleFinishedTextureBaker() {
// drop our shared pointer to this baker so that it gets cleaned up
_textureBakers.remove(baker->getTextureURL());
// emit progress to tell listeners how many textures we have baked
emit bakeProgress(++_completedSubBakes, _totalNumberOfSubBakes);
// emit progress to tell listeners how many textures we have baked
emit bakeProgress(++_completedSubBakes, _totalNumberOfSubBakes);
// check if this was the last texture we needed to re-write and if we are done now
checkIfRewritingComplete();
// check if this was the last texture we needed to re-write and if we are done now
checkIfRewritingComplete();
}
}

View file

@ -20,6 +20,10 @@
#include <StatTracker.h>
#include <ResourceManager.h>
#include <ResourceRequestObserver.h>
#include <ResourceCache.h>
#include <material-networking/TextureCache.h>
#include "MaterialBaker.h"
Oven* Oven::_staticInstance { nullptr };
@ -33,6 +37,12 @@ Oven::Oven() {
DependencyManager::set<StatTracker>();
DependencyManager::set<ResourceManager>(false);
DependencyManager::set<ResourceRequestObserver>();
DependencyManager::set<ResourceCacheSharedItems>();
DependencyManager::set<TextureCache>();
MaterialBaker::setNextOvenWorkerThreadOperator([] {
return Oven::instance().getNextWorkerThread();
});
}
Oven::~Oven() {