Unified · Split
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | set(TARGET_NAME baking) |
| 2 | 2 | setup_hifi_library(Concurrent) |
| 3 | 3 | |
| 4 | −link_hifi_libraries(shared graphics networking ktx image fbx) | |
| 4 | +link_hifi_libraries(shared shaders graphics networking material-networking ktx image fbx) | |
| 5 | 5 | include_hifi_library_headers(gpu) |
| 6 | 6 | include_hifi_library_headers(hfm) |
| 7 | 7 | |
| @@ -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 | +} | |
| @@ -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 | |
| @@ -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"); | |
| @@ -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 | |
| @@ -96,6 +96,8 @@ namespace scriptable { · 95 unmodified lines | ||
| 96 | 96 | |
| 97 | 97 | bool defaultFallthrough; |
| 98 | 98 | std::unordered_map<uint, bool> propertyFallthroughs; // not actually exposed to script |
| 99 | + | |
| 100 | + graphics::MaterialKey key { 0 }; | |
| 99 | 101 | }; |
| 100 | 102 | |
| 101 | 103 | /**jsdoc |
| @@ -364,20 +364,81 @@ namespace scriptable { · 363 unmodified lines | ||
| 364 | 364 | obj.setProperty("model", material.model); |
| 365 | 365 | |
| 366 | 366 | 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 | + } | |
| 381 | 442 | |
| 382 | 443 | // Only set one of each of these |
| 383 | 444 | if (material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT)) { |
| @@ -45,75 +45,80 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const · 44 unmodified lines | ||
| 45 | 45 | defaultFallthrough = material.defaultFallthrough; |
| 46 | 46 | propertyFallthroughs = material.propertyFallthroughs; |
| 47 | 47 | |
| 48 | + key = material.key; | |
| 49 | + | |
| 48 | 50 | return *this; |
| 49 | 51 | } |
| 50 | 52 | |
| 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 | + } | |
| 68 | 72 | |
| 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 | + } | |
| 74 | 79 | } |
| 75 | − } | |
| 76 | 80 | |
| 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 | + } | |
| 83 | 88 | } |
| 84 | − } | |
| 85 | 89 | |
| 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 | + } | |
| 92 | 97 | } |
| 93 | − } | |
| 94 | 98 | |
| 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 | + } | |
| 101 | 106 | } |
| 102 | − } | |
| 103 | 107 | |
| 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 | + } | |
| 108 | 112 | |
| 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 | + } | |
| 113 | 117 | |
| 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 | + } | |
| 117 | 122 | } |
| 118 | 123 | } |
| 119 | 124 | |
| @@ -2,7 +2,7 @@ set(TARGET_NAME oven) · 1 unmodified line | ||
| 2 | 2 | |
| 3 | 3 | setup_hifi_project(Widgets Gui Concurrent) |
| 4 | 4 | |
| 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) | |
| 6 | 6 | |
| 7 | 7 | setup_memory_debugger() |
| 8 | 8 | |
| @@ -23,6 +23,7 @@ · 22 unmodified lines | ||
| 23 | 23 | #include "baking/BakerLibrary.h" |
| 24 | 24 | #include "JSBaker.h" |
| 25 | 25 | #include "TextureBaker.h" |
| 26 | +#include "MaterialBaker.h" | |
| 26 | 27 | |
| 27 | 28 | BakerCLI::BakerCLI(OvenCLIApplication* parent) : QObject(parent) { |
| 28 | 29 | |
| @@ -60,8 +61,8 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& · 31 unmodified lines | ||
| 60 | 61 | _baker = std::unique_ptr<Baker> { new JSBaker(inputUrl, outputPath) }; |
| 61 | 62 | _baker->moveToThread(Oven::instance().getNextWorkerThread()); |
| 62 | 63 | } 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()); | |
| 65 | 66 | } else { |
| 66 | 67 | // If the type doesn't match the above, we assume we have a texture, and the type specified is the |
| 67 | 68 | // texture usage type (albedo, cubemap, normals, etc.) |
| @@ -187,8 +187,8 @@ void DomainBaker::addTextureBaker(const QString& property, const QString& url, i · 186 unmodified lines | ||
| 187 | 187 | |
| 188 | 188 | // setup a texture baker for this URL, as long as we aren't baking a texture already |
| 189 | 189 | if (!_textureBakers.contains(textureURL)) { |
| 190 | − // setup a baker for this texture | |
| 191 | 190 | |
| 191 | + // setup a baker for this texture | |
| 192 | 192 | QSharedPointer<TextureBaker> textureBaker { |
| 193 | 193 | new TextureBaker(textureURL, type, _contentOutputPath), |
| 194 | 194 | &TextureBaker::deleteLater |
| @@ -220,16 +220,16 @@ void DomainBaker::addScriptBaker(const QString& property, const QString& url, QJ · 25 unmodified lines | ||
| 220 | 220 | // grab a clean version of the URL without a query or fragment |
| 221 | 221 | QUrl scriptURL = QUrl(url).adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment); |
| 222 | 222 | |
| 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 | |
| 224 | 224 | if (!_scriptBakers.contains(scriptURL)) { |
| 225 | − // setup a baker for this texture | |
| 226 | 225 | |
| 226 | + // setup a baker for this script | |
| 227 | 227 | QSharedPointer<JSBaker> scriptBaker { |
| 228 | 228 | new JSBaker(scriptURL, _contentOutputPath), |
| 229 | 229 | &JSBaker::deleteLater |
| 230 | 230 | }; |
| 231 | 231 | |
| 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 | |
| 233 | 233 | connect(scriptBaker.data(), &JSBaker::finished, this, &DomainBaker::handleFinishedScriptBaker); |
| 234 | 234 | |
| 235 | 235 | // 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 | ||
| 243 | 243 | ++_totalNumberOfSubBakes; |
| 244 | 244 | } |
| 245 | 245 | |
| 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 | |
| 247 | 247 | // to the baked version once the baker is complete |
| 248 | 248 | _entitiesNeedingRewrite.insert(scriptURL, { property, jsonRef }); |
| 249 | 249 | } |
| 250 | 250 | |
| 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 | + | |
| 251 | 288 | // All the Entity Properties that can be baked |
| 252 | 289 | // *************************************************************************************** |
| 253 | 290 | |
| @@ -348,7 +385,12 @@ void DomainBaker::enumerateEntities() { · 94 unmodified lines | ||
| 348 | 385 | } |
| 349 | 386 | |
| 350 | 387 | // 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 | + } | |
| 352 | 394 | } |
| 353 | 395 | } |
| 354 | 396 | |
| @@ -568,6 +610,91 @@ void DomainBaker::handleFinishedScriptBaker() { · 213 unmodified lines | ||
| 568 | 610 | } |
| 569 | 611 | } |
| 570 | 612 | |
| 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 | + | |
| 571 | 698 | void DomainBaker::checkIfRewritingComplete() { |
| 572 | 699 | if (_entitiesNeedingRewrite.isEmpty()) { |
| 573 | 700 | writeNewEntitiesFile(); |
| @@ -21,6 +21,7 @@ · 20 unmodified lines | ||
| 21 | 21 | #include "ModelBaker.h" |
| 22 | 22 | #include "TextureBaker.h" |
| 23 | 23 | #include "JSBaker.h" |
| 24 | +#include "MaterialBaker.h" | |
| 24 | 25 | |
| 25 | 26 | class DomainBaker : public Baker { |
| 26 | 27 | Q_OBJECT |
| @@ -40,6 +41,7 @@ private slots: · 13 unmodified lines | ||
| 40 | 41 | void handleFinishedModelBaker(); |
| 41 | 42 | void handleFinishedTextureBaker(); |
| 42 | 43 | void handleFinishedScriptBaker(); |
| 44 | + void handleFinishedMaterialBaker(); | |
| 43 | 45 | |
| 44 | 46 | private: |
| 45 | 47 | void setupOutputFolder(); |
| @@ -62,6 +64,7 @@ private: · 16 unmodified lines | ||
| 62 | 64 | QHash<QUrl, QSharedPointer<ModelBaker>> _modelBakers; |
| 63 | 65 | QHash<QUrl, QSharedPointer<TextureBaker>> _textureBakers; |
| 64 | 66 | QHash<QUrl, QSharedPointer<JSBaker>> _scriptBakers; |
| 67 | + QHash<QUrl, QSharedPointer<MaterialBaker>> _materialBakers; | |
| 65 | 68 | |
| 66 | 69 | QMultiHash<QUrl, std::pair<QString, QJsonValueRef>> _entitiesNeedingRewrite; |
| 67 | 70 | |
| @@ -71,6 +74,7 @@ private: · 3 unmodified lines | ||
| 71 | 74 | void addModelBaker(const QString& property, const QString& url, QJsonValueRef& jsonRef); |
| 72 | 75 | void addTextureBaker(const QString& property, const QString& url, image::TextureUsage::Type type, QJsonValueRef& jsonRef); |
| 73 | 76 | void addScriptBaker(const QString& property, const QString& url, QJsonValueRef& jsonRef); |
| 77 | + void addMaterialBaker(const QString& property, const QString& data, bool isURL, QJsonValueRef& jsonRef); | |
| 74 | 78 | }; |
| 75 | 79 | |
| 76 | 80 | #endif // hifi_DomainBaker_h |
| @@ -63,6 +63,10 @@ void Oven::setupWorkerThreads(int numWorkerThreads) { · 62 unmodified lines | ||
| 63 | 63 | } |
| 64 | 64 | |
| 65 | 65 | 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 | + | |
| 66 | 70 | // Here we replicate some of the functionality of QThreadPool by giving callers an available worker thread to use. |
| 67 | 71 | // We can't use QThreadPool because we want to put QObjects with signals/slots on these threads. |
| 68 | 72 | // So instead we setup our own list of threads, up to one less than the ideal thread count |