History

working on material baker

SamGondelman committed 7 years ago

Commit 94de0c11 parent 4965adb13 files · +492 −79Browse files

Unified · Split

13 files
  1. libraries/baking/CMakeLists.txt +1 −1
  2. libraries/baking/src/MaterialBaker.cpp +118 −0
  3. libraries/baking/src/MaterialBaker.h +58 −0
  4. libraries/baking/src/MaterialBakingLoggingCategory.cpp +14 −0
  5. libraries/baking/src/MaterialBakingLoggingCategory.h +19 −0
  6. libraries/graphics-scripting/src/graphics-scripting/Forward.h +2 −0
  7. libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +75 −14
  8. libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +60 −55
  9. tools/oven/CMakeLists.txt +1 −1
  10. tools/oven/src/BakerCLI.cpp +3 −2
  11. tools/oven/src/DomainBaker.cpp +133 −6
  12. tools/oven/src/DomainBaker.h +4 −0
  13. tools/oven/src/Oven.cpp +4 −0
libraries/baking/CMakeLists.txt+1−1
@@ -1,7 +1,7 @@
11 set(TARGET_NAME baking)
22 setup_hifi_library(Concurrent)
33
4−link_hifi_libraries(shared graphics networking ktx image fbx)
4+link_hifi_libraries(shared shaders graphics networking material-networking ktx image fbx)
55 include_hifi_library_headers(gpu)
66 include_hifi_library_headers(hfm)
77
libraries/baking/src/MaterialBaker.cpp+118−0
@@ -0,0 +1,118 @@
1+//
2+// MaterialBaker.cpp
3+// libraries/baking/src
4+//
5+// Created by Sam Gondelman on 2/26/2019
6+// Copyright 2019 High Fidelity, Inc.
7+//
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+
12+#include "MaterialBaker.h"
13+
14+#include "QJsonObject"
15+#include "QJsonDocument"
16+
17+#include "MaterialBakingLoggingCategory.h"
18+
19+#include <SharedUtil.h>
20+#include <PathUtils.h>
21+
22+MaterialBaker::MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir) :
23+ _materialData(materialData),
24+ _isURL(isURL),
25+ _bakedOutputDir(bakedOutputDir)
26+{
27+}
28+
29+void MaterialBaker::bake() {
30+ qDebug(material_baking) << "Material Baker" << _materialData << "bake starting";
31+
32+ // once our script is loaded, kick off a the processing
33+ connect(this, &MaterialBaker::originalMaterialLoaded, this, &MaterialBaker::processMaterial);
34+
35+ if (!_materialResource) {
36+ // first load the material (either locally or remotely)
37+ loadMaterial();
38+ } else {
39+ // we already have a material passed to us, use that
40+ if (_materialResource->isLoaded()) {
41+ emit originalMaterialLoaded();
42+ } else {
43+ connect(_materialResource.data(), &Resource::finished, this, &MaterialBaker::originalMaterialLoaded);
44+ }
45+ }
46+}
47+
48+void MaterialBaker::loadMaterial() {
49+ if (!_isURL) {
50+ qCDebug(material_baking) << "Loading local material" << _materialData;
51+
52+ _materialResource = NetworkMaterialResourcePointer(new NetworkMaterialResource());
53+ // TODO: add baseURL to allow these to reference relative files next to them
54+ _materialResource->parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromVariant(_materialData), QUrl());
55+ } else {
56+ qCDebug(material_baking) << "Downloading material" << _materialData;
57+ _materialResource = MaterialCache::instance().getMaterial(_materialData);
58+ }
59+
60+ if (_materialResource) {
61+ if (_materialResource->isLoaded()) {
62+ emit originalMaterialLoaded();
63+ } else {
64+ connect(_materialResource.data(), &Resource::finished, this, &MaterialBaker::originalMaterialLoaded);
65+ }
66+ } else {
67+ handleError("Error loading " + _materialData);
68+ }
69+}
70+
71+void MaterialBaker::processMaterial() {
72+ if (!_materialResource || _materialResource->parsedMaterials.networkMaterials.size() == 0) {
73+ handleError("Error processing " + _materialData);
74+ }
75+
76+ _numTexturesToLoad = _materialResource->parsedMaterials.networkMaterials.size();
77+ _numTexturesLoaded = 0;
78+
79+ for (auto networkMaterial : _materialResource->parsedMaterials.networkMaterials) {
80+ if (networkMaterial.second) {
81+ auto textureMaps = networkMaterial.second->getTextureMaps();
82+ for (auto textureMap : textureMaps) {
83+ if (textureMap.second && textureMap.second->getTextureSource()) {
84+ auto texture = textureMap.second->getTextureSource();
85+ graphics::Material::MapChannel mapChannel = textureMap.first;
86+
87+ qDebug() << "boop" << mapChannel << texture->getUrl();
88+ }
89+ }
90+ }
91+ }
92+}
93+
94+void MaterialBaker::outputMaterial() {
95+ //if (_isURL) {
96+ // auto fileName = _materialData;
97+ // auto baseName = fileName.left(fileName.lastIndexOf('.'));
98+ // auto bakedFilename = baseName + BAKED_MATERIAL_EXTENSION;
99+
100+ // _bakedMaterialData = _bakedOutputDir + "/" + bakedFilename;
101+
102+ // QFile bakedFile;
103+ // bakedFile.setFileName(_bakedMaterialData);
104+ // if (!bakedFile.open(QIODevice::WriteOnly)) {
105+ // handleError("Error opening " + _bakedMaterialData + " for writing");
106+ // return;
107+ // }
108+
109+ // bakedFile.write(outputMaterial);
110+
111+ // // Export successful
112+ // _outputFiles.push_back(_bakedMaterialData);
113+ // qCDebug(material_baking) << "Exported" << _materialData << "to" << _bakedMaterialData;
114+ //}
115+
116+ // emit signal to indicate the material baking is finished
117+ emit finished();
118+}
libraries/baking/src/MaterialBaker.h+58−0
@@ -0,0 +1,58 @@
1+//
2+// MaterialBaker.h
3+// libraries/baking/src
4+//
5+// Created by Sam Gondelman on 2/26/2019
6+// Copyright 2019 High Fidelity, Inc.
7+//
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+
12+#ifndef hifi_MaterialBaker_h
13+#define hifi_MaterialBaker_h
14+
15+#include "Baker.h"
16+
17+#include "TextureBaker.h"
18+
19+#include <material-networking/MaterialCache.h>
20+
21+static const QString BAKED_MATERIAL_EXTENSION = ".baked.json";
22+
23+class MaterialBaker : public Baker {
24+ Q_OBJECT
25+public:
26+ MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir);
27+
28+ QString getMaterialData() const { return _materialData; }
29+ bool isURL() const { return _isURL; }
30+ QString getBakedMaterialData() const { return _bakedMaterialData; }
31+
32+public slots:
33+ virtual void bake() override;
34+
35+signals:
36+ void originalMaterialLoaded();
37+
38+private slots:
39+ void processMaterial();
40+ void outputMaterial();
41+
42+private:
43+ void loadMaterial();
44+
45+ QString _materialData;
46+ bool _isURL;
47+
48+ NetworkMaterialResourcePointer _materialResource;
49+ size_t _numTexturesToLoad { 0 };
50+ size_t _numTexturesLoaded { 0 };
51+
52+ QHash<QUrl, QSharedPointer<TextureBaker>> _textureBakers;
53+
54+ QString _bakedOutputDir;
55+ QString _bakedMaterialData;
56+};
57+
58+#endif // !hifi_MaterialBaker_h
libraries/baking/src/MaterialBakingLoggingCategory.cpp+14−0
@@ -0,0 +1,14 @@
1+//
2+// MaterialBakingLoggingCategory.cpp
3+// libraries/baking/src
4+//
5+// Created by Sam Gondelman on 2/26/2019
6+// Copyright 2019 High Fidelity, Inc.
7+//
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+
12+#include "MaterialBakingLoggingCategory.h"
13+
14+Q_LOGGING_CATEGORY(material_baking, "hifi.material-baking");
libraries/baking/src/MaterialBakingLoggingCategory.h+19−0
@@ -0,0 +1,19 @@
1+//
2+// MaterialBakingLoggingCategory.h
3+// libraries/baking/src
4+//
5+// Created by Sam Gondelman on 2/26/2019
6+// Copyright 2019 High Fidelity, Inc.
7+//
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+
12+#ifndef hifi_MaterialBakingLoggingCategory_h
13+#define hifi_MaterialBakingLoggingCategory_h
14+
15+#include <QtCore/QLoggingCategory>
16+
17+Q_DECLARE_LOGGING_CATEGORY(material_baking)
18+
19+#endif // hifi_MaterialBakingLoggingCategory_h
libraries/graphics-scripting/src/graphics-scripting/Forward.h+2−0
@@ -96,6 +96,8 @@ namespace scriptable { · 95 unmodified lines
9696
9797 bool defaultFallthrough;
9898 std::unordered_map<uint, bool> propertyFallthroughs; // not actually exposed to script
99+
100+ graphics::MaterialKey key { 0 };
99101 };
100102
101103 /**jsdoc
libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp+75−14
@@ -364,20 +364,81 @@ namespace scriptable { · 363 unmodified lines
364364 obj.setProperty("model", material.model);
365365
366366 const QScriptValue FALLTHROUGH("fallthrough");
367− obj.setProperty("opacity", material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_VAL_BIT) ? FALLTHROUGH : material.opacity);
368− obj.setProperty("roughness", material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT) ? FALLTHROUGH : material.roughness);
369− obj.setProperty("metallic", material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT) ? FALLTHROUGH : material.metallic);
370− obj.setProperty("scattering", material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT) ? FALLTHROUGH : material.scattering);
371− obj.setProperty("unlit", material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT) ? FALLTHROUGH : material.unlit);
372− obj.setProperty("emissive", material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_VAL_BIT) ? FALLTHROUGH : vec3ColorToScriptValue(engine, material.emissive));
373− obj.setProperty("albedo", material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_VAL_BIT) ? FALLTHROUGH : vec3ColorToScriptValue(engine, material.albedo));
374−
375− obj.setProperty("emissiveMap", material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_MAP_BIT) ? FALLTHROUGH : material.emissiveMap);
376− obj.setProperty("albedoMap", material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_MAP_BIT) ? FALLTHROUGH : material.albedoMap);
377− obj.setProperty("opacityMap", material.opacityMap);
378− obj.setProperty("occlusionMap", material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT) ? FALLTHROUGH : material.occlusionMap);
379− obj.setProperty("lightmapMap", material.propertyFallthroughs.at(graphics::MaterialKey::LIGHTMAP_MAP_BIT) ? FALLTHROUGH : material.lightmapMap);
380− obj.setProperty("scatteringMap", material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT) ? FALLTHROUGH : material.scatteringMap);
367+ if (material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_VAL_BIT)) {
368+ obj.setProperty("opacity", FALLTHROUGH);
369+ } else if (material.key.isTranslucentFactor()) {
370+ obj.setProperty("opacity", material.opacity);
371+ }
372+
373+ if (material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT)) {
374+ obj.setProperty("roughness", FALLTHROUGH);
375+ } else if (material.key.isGlossy()) {
376+ obj.setProperty("roughness", material.roughness);
377+ }
378+
379+ if (material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT)) {
380+ obj.setProperty("metallic", FALLTHROUGH);
381+ } else if (material.key.isMetallic()) {
382+ obj.setProperty("metallic", material.metallic);
383+ }
384+
385+ if (material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT)) {
386+ obj.setProperty("scattering", FALLTHROUGH);
387+ } else if (material.key.isScattering()) {
388+ obj.setProperty("scattering", material.scattering);
389+ }
390+
391+ if (material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT)) {
392+ obj.setProperty("unlit", FALLTHROUGH);
393+ } else if (material.key.isUnlit()) {
394+ obj.setProperty("unlit", material.unlit);
395+ }
396+
397+ if (material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_VAL_BIT)) {
398+ obj.setProperty("emissive", FALLTHROUGH);
399+ } else if (material.key.isEmissive()) {
400+ obj.setProperty("emissive", vec3ColorToScriptValue(engine, material.emissive));
401+ }
402+
403+ if (material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_VAL_BIT)) {
404+ obj.setProperty("albedo", FALLTHROUGH);
405+ } else if (material.key.isAlbedo()) {
406+ obj.setProperty("albedo", vec3ColorToScriptValue(engine, material.albedo));
407+ }
408+
409+ if (material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_MAP_BIT)) {
410+ obj.setProperty("emissiveMap", FALLTHROUGH);
411+ } else if (!material.emissiveMap.isEmpty()) {
412+ obj.setProperty("emissiveMap", material.emissiveMap);
413+ }
414+
415+ if (material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_MAP_BIT)) {
416+ obj.setProperty("albedoMap", FALLTHROUGH);
417+ } else if (!material.albedoMap.isEmpty()) {
418+ obj.setProperty("albedoMap", material.albedoMap);
419+ }
420+
421+ if (!material.opacityMap.isEmpty()) {
422+ obj.setProperty("opacityMap", material.opacityMap);
423+ }
424+
425+ if (material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT)) {
426+ obj.setProperty("occlusionMap", FALLTHROUGH);
427+ } else if (!material.occlusionMap.isEmpty()) {
428+ obj.setProperty("occlusionMap", material.occlusionMap);
429+ }
430+
431+ if (material.propertyFallthroughs.at(graphics::MaterialKey::LIGHTMAP_MAP_BIT)) {
432+ obj.setProperty("lightmapMap", FALLTHROUGH);
433+ } else if (!material.lightmapMap.isEmpty()) {
434+ obj.setProperty("lightmapMap", material.lightmapMap);
435+ }
436+
437+ if (material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT)) {
438+ obj.setProperty("scatteringMap", FALLTHROUGH);
439+ } else if (!material.scatteringMap.isEmpty()) {
440+ obj.setProperty("scatteringMap", material.scatteringMap);
441+ }
381442
382443 // Only set one of each of these
383444 if (material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT)) {
libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp+60−55
@@ -45,75 +45,80 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const · 44 unmodified lines
4545 defaultFallthrough = material.defaultFallthrough;
4646 propertyFallthroughs = material.propertyFallthroughs;
4747
48+ key = material.key;
49+
4850 return *this;
4951 }
5052
51−scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPointer& material) :
52− name(material->getName().c_str()),
53− model(material->getModel().c_str()),
54− opacity(material->getOpacity()),
55− roughness(material->getRoughness()),
56− metallic(material->getMetallic()),
57− scattering(material->getScattering()),
58− unlit(material->isUnlit()),
59− emissive(material->getEmissive()),
60− albedo(material->getAlbedo()),
61− defaultFallthrough(material->getDefaultFallthrough()),
62− propertyFallthroughs(material->getPropertyFallthroughs())
63−{
64− auto map = material->getTextureMap(graphics::Material::MapChannel::EMISSIVE_MAP);
65− if (map && map->getTextureSource()) {
66− emissiveMap = map->getTextureSource()->getUrl().toString();
67− }
53+scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPointer& material) {
54+ if (material) {
55+ name = material->getName().c_str();
56+ model = material->getModel().c_str();
57+ opacity = material->getOpacity();
58+ roughness = material->getRoughness();
59+ metallic = material->getMetallic();
60+ scattering = material->getScattering();
61+ unlit = material->isUnlit();
62+ emissive = material->getEmissive();
63+ albedo = material->getAlbedo();
64+ defaultFallthrough = material->getDefaultFallthrough();
65+ propertyFallthroughs = material->getPropertyFallthroughs();
66+ key = material->getKey();
67+
68+ auto map = material->getTextureMap(graphics::Material::MapChannel::EMISSIVE_MAP);
69+ if (map && map->getTextureSource()) {
70+ emissiveMap = map->getTextureSource()->getUrl().toString();
71+ }
6872
69− map = material->getTextureMap(graphics::Material::MapChannel::ALBEDO_MAP);
70− if (map && map->getTextureSource()) {
71− albedoMap = map->getTextureSource()->getUrl().toString();
72− if (map->useAlphaChannel()) {
73− opacityMap = albedoMap;
73+ map = material->getTextureMap(graphics::Material::MapChannel::ALBEDO_MAP);
74+ if (map && map->getTextureSource()) {
75+ albedoMap = map->getTextureSource()->getUrl().toString();
76+ if (map->useAlphaChannel()) {
77+ opacityMap = albedoMap;
78+ }
7479 }
75− }
7680
77− map = material->getTextureMap(graphics::Material::MapChannel::METALLIC_MAP);
78− if (map && map->getTextureSource()) {
79− if (map->getTextureSource()->getType() == image::TextureUsage::Type::METALLIC_TEXTURE) {
80− metallicMap = map->getTextureSource()->getUrl().toString();
81− } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::SPECULAR_TEXTURE) {
82− specularMap = map->getTextureSource()->getUrl().toString();
81+ map = material->getTextureMap(graphics::Material::MapChannel::METALLIC_MAP);
82+ if (map && map->getTextureSource()) {
83+ if (map->getTextureSource()->getType() == image::TextureUsage::Type::METALLIC_TEXTURE) {
84+ metallicMap = map->getTextureSource()->getUrl().toString();
85+ } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::SPECULAR_TEXTURE) {
86+ specularMap = map->getTextureSource()->getUrl().toString();
87+ }
8388 }
84− }
8589
86− map = material->getTextureMap(graphics::Material::MapChannel::ROUGHNESS_MAP);
87− if (map && map->getTextureSource()) {
88− if (map->getTextureSource()->getType() == image::TextureUsage::Type::ROUGHNESS_TEXTURE) {
89− roughnessMap = map->getTextureSource()->getUrl().toString();
90− } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::GLOSS_TEXTURE) {
91− glossMap = map->getTextureSource()->getUrl().toString();
90+ map = material->getTextureMap(graphics::Material::MapChannel::ROUGHNESS_MAP);
91+ if (map && map->getTextureSource()) {
92+ if (map->getTextureSource()->getType() == image::TextureUsage::Type::ROUGHNESS_TEXTURE) {
93+ roughnessMap = map->getTextureSource()->getUrl().toString();
94+ } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::GLOSS_TEXTURE) {
95+ glossMap = map->getTextureSource()->getUrl().toString();
96+ }
9297 }
93− }
9498
95− map = material->getTextureMap(graphics::Material::MapChannel::NORMAL_MAP);
96− if (map && map->getTextureSource()) {
97− if (map->getTextureSource()->getType() == image::TextureUsage::Type::NORMAL_TEXTURE) {
98− normalMap = map->getTextureSource()->getUrl().toString();
99− } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::BUMP_TEXTURE) {
100− bumpMap = map->getTextureSource()->getUrl().toString();
99+ map = material->getTextureMap(graphics::Material::MapChannel::NORMAL_MAP);
100+ if (map && map->getTextureSource()) {
101+ if (map->getTextureSource()->getType() == image::TextureUsage::Type::NORMAL_TEXTURE) {
102+ normalMap = map->getTextureSource()->getUrl().toString();
103+ } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::BUMP_TEXTURE) {
104+ bumpMap = map->getTextureSource()->getUrl().toString();
105+ }
101106 }
102− }
103107
104− map = material->getTextureMap(graphics::Material::MapChannel::OCCLUSION_MAP);
105− if (map && map->getTextureSource()) {
106− occlusionMap = map->getTextureSource()->getUrl().toString();
107− }
108+ map = material->getTextureMap(graphics::Material::MapChannel::OCCLUSION_MAP);
109+ if (map && map->getTextureSource()) {
110+ occlusionMap = map->getTextureSource()->getUrl().toString();
111+ }
108112
109− map = material->getTextureMap(graphics::Material::MapChannel::LIGHTMAP_MAP);
110− if (map && map->getTextureSource()) {
111− lightmapMap = map->getTextureSource()->getUrl().toString();
112− }
113+ map = material->getTextureMap(graphics::Material::MapChannel::LIGHTMAP_MAP);
114+ if (map && map->getTextureSource()) {
115+ lightmapMap = map->getTextureSource()->getUrl().toString();
116+ }
113117
114− map = material->getTextureMap(graphics::Material::MapChannel::SCATTERING_MAP);
115− if (map && map->getTextureSource()) {
116− scatteringMap = map->getTextureSource()->getUrl().toString();
118+ map = material->getTextureMap(graphics::Material::MapChannel::SCATTERING_MAP);
119+ if (map && map->getTextureSource()) {
120+ scatteringMap = map->getTextureSource()->getUrl().toString();
121+ }
117122 }
118123 }
119124
tools/oven/CMakeLists.txt+1−1
@@ -2,7 +2,7 @@ set(TARGET_NAME oven) · 1 unmodified line
22
33 setup_hifi_project(Widgets Gui Concurrent)
44
5−link_hifi_libraries(networking shared image gpu ktx fbx hfm baking graphics)
5+link_hifi_libraries(shared shaders image gpu ktx fbx hfm baking graphics networking material-networking)
66
77 setup_memory_debugger()
88
tools/oven/src/BakerCLI.cpp+3−2
@@ -23,6 +23,7 @@ · 22 unmodified lines
2323 #include "baking/BakerLibrary.h"
2424 #include "JSBaker.h"
2525 #include "TextureBaker.h"
26+#include "MaterialBaker.h"
2627
2728 BakerCLI::BakerCLI(OvenCLIApplication* parent) : QObject(parent) {
2829
@@ -60,8 +61,8 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& · 31 unmodified lines
6061 _baker = std::unique_ptr<Baker> { new JSBaker(inputUrl, outputPath) };
6162 _baker->moveToThread(Oven::instance().getNextWorkerThread());
6263 } else if (type == MATERIAL_EXTENSION) {
63− //_baker = std::unique_ptr<Baker> { new MaterialBaker(inputUrl, outputPath) };
64− //_baker->moveToThread(Oven::instance().getNextWorkerThread());
64+ _baker = std::unique_ptr<Baker> { new MaterialBaker(inputUrl.toDisplayString(), true, outputPath) };
65+ _baker->moveToThread(Oven::instance().getNextWorkerThread());
6566 } else {
6667 // If the type doesn't match the above, we assume we have a texture, and the type specified is the
6768 // texture usage type (albedo, cubemap, normals, etc.)
tools/oven/src/DomainBaker.cpp+133−6
@@ -187,8 +187,8 @@ void DomainBaker::addTextureBaker(const QString& property, const QString& url, i · 186 unmodified lines
187187
188188 // setup a texture baker for this URL, as long as we aren't baking a texture already
189189 if (!_textureBakers.contains(textureURL)) {
190− // setup a baker for this texture
191190
191+ // setup a baker for this texture
192192 QSharedPointer<TextureBaker> textureBaker {
193193 new TextureBaker(textureURL, type, _contentOutputPath),
194194 &TextureBaker::deleteLater
@@ -220,16 +220,16 @@ void DomainBaker::addScriptBaker(const QString& property, const QString& url, QJ · 25 unmodified lines
220220 // grab a clean version of the URL without a query or fragment
221221 QUrl scriptURL = QUrl(url).adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment);
222222
223− // setup a texture baker for this URL, as long as we aren't baking a texture already
223+ // setup a script baker for this URL, as long as we aren't baking a texture already
224224 if (!_scriptBakers.contains(scriptURL)) {
225− // setup a baker for this texture
226225
226+ // setup a baker for this script
227227 QSharedPointer<JSBaker> scriptBaker {
228228 new JSBaker(scriptURL, _contentOutputPath),
229229 &JSBaker::deleteLater
230230 };
231231
232− // make sure our handler is called when the texture baker is done
232+ // make sure our handler is called when the script baker is done
233233 connect(scriptBaker.data(), &JSBaker::finished, this, &DomainBaker::handleFinishedScriptBaker);
234234
235235 // 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 · 7 unmodified lines
243243 ++_totalNumberOfSubBakes;
244244 }
245245
246− // add this QJsonValueRef to our multi hash so that it can re-write the texture URL
246+ // add this QJsonValueRef to our multi hash so that it can re-write the script URL
247247 // to the baked version once the baker is complete
248248 _entitiesNeedingRewrite.insert(scriptURL, { property, jsonRef });
249249 }
250250
251+void DomainBaker::addMaterialBaker(const QString& property, const QString& data, bool isURL, QJsonValueRef& jsonRef) {
252+ // grab a clean version of the URL without a query or fragment
253+ QString materialData;
254+ if (isURL) {
255+ materialData = QUrl(data).adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment).toDisplayString();
256+ } else {
257+ materialData = data;
258+ }
259+
260+ // setup a material baker for this URL, as long as we aren't baking a material already
261+ if (!_materialBakers.contains(materialData)) {
262+
263+ // setup a baker for this material
264+ QSharedPointer<MaterialBaker> materialBaker {
265+ new MaterialBaker(data, isURL, _contentOutputPath),
266+ &MaterialBaker::deleteLater
267+ };
268+
269+ // make sure our handler is called when the material baker is done
270+ connect(materialBaker.data(), &MaterialBaker::finished, this, &DomainBaker::handleFinishedMaterialBaker);
271+
272+ // insert it into our bakers hash so we hold a strong pointer to it
273+ _materialBakers.insert(materialData, materialBaker);
274+
275+ // move the baker to a worker thread and kickoff the bake
276+ materialBaker->moveToThread(Oven::instance().getNextWorkerThread());
277+ QMetaObject::invokeMethod(materialBaker.data(), "bake");
278+
279+ // keep track of the total number of baking entities
280+ ++_totalNumberOfSubBakes;
281+ }
282+
283+ // add this QJsonValueRef to our multi hash so that it can re-write the texture URL
284+ // to the baked version once the baker is complete
285+ _entitiesNeedingRewrite.insert(materialData, { property, jsonRef });
286+}
287+
251288 // All the Entity Properties that can be baked
252289 // ***************************************************************************************
253290
@@ -348,7 +385,12 @@ void DomainBaker::enumerateEntities() { · 94 unmodified lines
348385 }
349386
350387 // Materials
351− // TODO
388+ if (entity.contains(MATERIAL_URL_KEY)) {
389+ addMaterialBaker(MATERIAL_URL_KEY, entity[MATERIAL_URL_KEY].toString(), true, *it);
390+ }
391+ if (entity.contains(MATERIAL_DATA_KEY)) {
392+ addMaterialBaker(MATERIAL_DATA_KEY, entity[MATERIAL_URL_KEY].toString(), false, *it);
393+ }
352394 }
353395 }
354396
@@ -568,6 +610,91 @@ void DomainBaker::handleFinishedScriptBaker() { · 213 unmodified lines
568610 }
569611 }
570612
613+void DomainBaker::handleFinishedMaterialBaker() {
614+ auto baker = qobject_cast<MaterialBaker*>(sender());
615+
616+ if (baker) {
617+ if (!baker->hasErrors()) {
618+ // this MaterialBaker is done and everything went according to plan
619+ qDebug() << "Re-writing entity references to" << baker->getMaterialData();
620+
621+ QString newDataOrURL;
622+ if (baker->isURL()) {
623+ newDataOrURL = _destinationPath.resolved(baker->getBakedMaterialData()).toDisplayString();
624+ } else {
625+ newDataOrURL = baker->getBakedMaterialData();
626+ }
627+
628+ // enumerate the QJsonRef values for the URL of this material from our multi hash of
629+ // entity objects needing a URL re-write
630+ for (auto propertyEntityPair : _entitiesNeedingRewrite.values(baker->getMaterialData())) {
631+ QString property = propertyEntityPair.first;
632+ // convert the entity QJsonValueRef to a QJsonObject so we can modify its URL
633+ auto entity = propertyEntityPair.second.toObject();
634+
635+ if (!property.contains(".")) {
636+ // grab the old URL
637+ QUrl oldURL = entity[property].toString();
638+
639+ // copy the fragment and query, and user info from the old material data
640+ if (baker->isURL()) {
641+ QUrl newURL = newDataOrURL;
642+ newURL.setQuery(oldURL.query());
643+ newURL.setFragment(oldURL.fragment());
644+ newURL.setUserInfo(oldURL.userInfo());
645+
646+ // set the new URL as the value in our temp QJsonObject
647+ entity[property] = newURL.toString();
648+ } else {
649+ entity[property] = newDataOrURL;
650+ }
651+ } else {
652+ // Group property
653+ QStringList propertySplit = property.split(".");
654+ assert(propertySplit.length() == 2);
655+ // grab the old URL
656+ auto oldObject = entity[propertySplit[0]].toObject();
657+ QUrl oldURL = oldObject[propertySplit[1]].toString();
658+
659+ // copy the fragment and query, and user info from the old material data
660+ if (baker->isURL()) {
661+ QUrl newURL = newDataOrURL;
662+ newURL.setQuery(oldURL.query());
663+ newURL.setFragment(oldURL.fragment());
664+ newURL.setUserInfo(oldURL.userInfo());
665+
666+ // set the new URL as the value in our temp QJsonObject
667+ oldObject[propertySplit[1]] = newURL.toString();
668+ entity[propertySplit[0]] = oldObject;
669+ } else {
670+ oldObject[propertySplit[1]] = newDataOrURL;
671+ entity[propertySplit[0]] = oldObject;
672+ }
673+ }
674+
675+ // replace our temp object with the value referenced by our QJsonValueRef
676+ propertyEntityPair.second = entity;
677+ }
678+ } else {
679+ // this material failed to bake - this doesn't fail the entire bake but we need to add
680+ // the errors from the material to our warnings
681+ _warningList << baker->getErrors();
682+ }
683+
684+ // remove the baked URL from the multi hash of entities needing a re-write
685+ _entitiesNeedingRewrite.remove(baker->getMaterialData());
686+
687+ // drop our shared pointer to this baker so that it gets cleaned up
688+ _materialBakers.remove(baker->getMaterialData());
689+
690+ // emit progress to tell listeners how many materials we have baked
691+ emit bakeProgress(++_completedSubBakes, _totalNumberOfSubBakes);
692+
693+ // check if this was the last material we needed to re-write and if we are done now
694+ checkIfRewritingComplete();
695+ }
696+}
697+
571698 void DomainBaker::checkIfRewritingComplete() {
572699 if (_entitiesNeedingRewrite.isEmpty()) {
573700 writeNewEntitiesFile();
tools/oven/src/DomainBaker.h+4−0
@@ -21,6 +21,7 @@ · 20 unmodified lines
2121 #include "ModelBaker.h"
2222 #include "TextureBaker.h"
2323 #include "JSBaker.h"
24+#include "MaterialBaker.h"
2425
2526 class DomainBaker : public Baker {
2627 Q_OBJECT
@@ -40,6 +41,7 @@ private slots: · 13 unmodified lines
4041 void handleFinishedModelBaker();
4142 void handleFinishedTextureBaker();
4243 void handleFinishedScriptBaker();
44+ void handleFinishedMaterialBaker();
4345
4446 private:
4547 void setupOutputFolder();
@@ -62,6 +64,7 @@ private: · 16 unmodified lines
6264 QHash<QUrl, QSharedPointer<ModelBaker>> _modelBakers;
6365 QHash<QUrl, QSharedPointer<TextureBaker>> _textureBakers;
6466 QHash<QUrl, QSharedPointer<JSBaker>> _scriptBakers;
67+ QHash<QUrl, QSharedPointer<MaterialBaker>> _materialBakers;
6568
6669 QMultiHash<QUrl, std::pair<QString, QJsonValueRef>> _entitiesNeedingRewrite;
6770
@@ -71,6 +74,7 @@ private: · 3 unmodified lines
7174 void addModelBaker(const QString& property, const QString& url, QJsonValueRef& jsonRef);
7275 void addTextureBaker(const QString& property, const QString& url, image::TextureUsage::Type type, QJsonValueRef& jsonRef);
7376 void addScriptBaker(const QString& property, const QString& url, QJsonValueRef& jsonRef);
77+ void addMaterialBaker(const QString& property, const QString& data, bool isURL, QJsonValueRef& jsonRef);
7478 };
7579
7680 #endif // hifi_DomainBaker_h
tools/oven/src/Oven.cpp+4−0
@@ -63,6 +63,10 @@ void Oven::setupWorkerThreads(int numWorkerThreads) { · 62 unmodified lines
6363 }
6464
6565 QThread* Oven::getNextWorkerThread() {
66+ // FIXME: we assign these threads when we make the bakers, but if certain bakers finish quickly, we could end up
67+ // in a situation where threads have finished and others have tons of work queued. Instead of assigning them at initialization,
68+ // we should build a queue of bakers, and when threads finish, they can take the next available baker.
69+
6670 // Here we replicate some of the functionality of QThreadPool by giving callers an available worker thread to use.
6771 // We can't use QThreadPool because we want to put QObjects with signals/slots on these threads.
6872 // So instead we setup our own list of threads, up to one less than the ideal thread count