diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index c47904213b..d5a8aaafe2 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -42,10 +42,23 @@ ShapeEntityRenderer::ShapeEntityRenderer(const EntityItemPointer& entity) : Pare // TODO: move into Procedural.cpp PrepareStencil::testMaskDrawShape(*_procedural._opaqueState); PrepareStencil::testMask(*_procedural._transparentState); + + addMaterial(graphics::MaterialLayer(_material, 0), "0"); } bool ShapeEntityRenderer::needsRenderUpdate() const { - if (_procedural.isEnabled() && _procedural.isFading()) { + if (resultWithReadLock([&] { + if (_procedural.isEnabled() && _procedural.isFading()) { + return true; + } + + auto mat = _materials.find("0"); + if (mat != _materials.end() && mat->second.needsUpdate()) { + return true; + } + + return false; + })) { return true; } @@ -56,7 +69,11 @@ bool ShapeEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin if (_lastUserData != entity->getUserData()) { return true; } - if (_material != entity->getMaterial()) { + + if (_color != entity->getColor()) { + return true; + } + if (_alpha != entity->getAlpha()) { return true; } @@ -79,10 +96,6 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce _procedural.setProceduralData(ProceduralData::parse(_lastUserData)); } - removeMaterial(_material, "0"); - _material = entity->getMaterial(); - addMaterial(graphics::MaterialLayer(_material, 0), "0"); - _shape = entity->getShape(); }); @@ -111,6 +124,20 @@ void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint _procedural.setIsFading(isFading); } }); + + glm::u8vec3 color = entity->getColor(); + float alpha = entity->getAlpha(); + if (_color != color || _alpha != alpha) { + _color = color; + _alpha = alpha; + _material->setAlbedo(toGlm(_color)); + _material->setOpacity(_alpha); + + auto materials = _materials.find("0"); + if (materials != _materials.end()) { + materials->second.setNeedsUpdate(true); + } + } } bool ShapeEntityRenderer::isTransparent() const { @@ -120,18 +147,15 @@ bool ShapeEntityRenderer::isTransparent() const { auto mat = _materials.find("0"); if (mat != _materials.end()) { - if (mat->second.top().material) { - auto matKey = mat->second.top().material->getKey(); - if (matKey.isTranslucent()) { - return true; - } + if (mat->second.getMaterialKey().isTranslucent()) { + return true; } } return Parent::isTransparent(); } -bool ShapeEntityRenderer::useMaterialPipeline() const { +bool ShapeEntityRenderer::useMaterialPipeline(const graphics::MultiMaterial& materials) const { bool proceduralReady = resultWithReadLock([&] { return _procedural.isReady(); }); @@ -139,12 +163,7 @@ bool ShapeEntityRenderer::useMaterialPipeline() const { return false; } - graphics::MaterialKey drawMaterialKey; - auto mat = _materials.find("0"); - if (mat != _materials.end() && mat->second.top().material) { - drawMaterialKey = mat->second.top().material->getKey(); - } - + graphics::MaterialKey drawMaterialKey = materials.getMaterialKey(); if (drawMaterialKey.isEmissive() || drawMaterialKey.isUnlit() || drawMaterialKey.isMetallic() || drawMaterialKey.isScattering()) { return true; } @@ -159,11 +178,13 @@ bool ShapeEntityRenderer::useMaterialPipeline() const { } ShapeKey ShapeEntityRenderer::getShapeKey() { - if (useMaterialPipeline()) { - graphics::MaterialKey drawMaterialKey; - if (_materials["0"].top().material) { - drawMaterialKey = _materials["0"].top().material->getKey(); - } + auto mat = _materials.find("0"); + if (mat != _materials.end() && mat->second.needsUpdate()) { + RenderPipelines::updateMultiMaterial(mat->second); + } + + if (mat != _materials.end() && useMaterialPipeline(mat->second)) { + graphics::MaterialKey drawMaterialKey = mat->second.getMaterialKey(); bool isTranslucent = drawMaterialKey.isTranslucent(); bool hasTangents = drawMaterialKey.isNormalMap(); @@ -216,7 +237,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { gpu::Batch& batch = *args->_batch; - std::shared_ptr mat; + graphics::MultiMaterial materials; auto geometryCache = DependencyManager::get(); GeometryCache::Shape geometryShape; bool proceduralRender = false; @@ -224,29 +245,24 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { withReadLock([&] { geometryShape = geometryCache->getShapeForEntityShape(_shape); batch.setModelTransform(_renderTransform); // use a transform with scale, rotation, registration point and translation - mat = _materials["0"].top().material; - if (mat) { - outColor = glm::vec4(mat->getAlbedo(), mat->getOpacity()); - if (_procedural.isReady()) { - outColor = _procedural.getColor(outColor); - outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f; - _procedural.prepare(batch, _position, _dimensions, _orientation, ProceduralProgramKey(outColor.a < 1.0f)); - proceduralRender = true; - } + materials = _materials["0"]; + auto& schema = materials.getSchemaBuffer().get(); + outColor = glm::vec4(schema._albedo, schema._opacity); + if (_procedural.isReady()) { + outColor = _procedural.getColor(outColor); + outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f; + _procedural.prepare(batch, _position, _dimensions, _orientation, ProceduralProgramKey(outColor.a < 1.0f)); + proceduralRender = true; } }); - if (!mat) { - return; - } - if (proceduralRender) { if (render::ShapeKey(args->_globalShapeKey).isWireframe()) { geometryCache->renderWireShape(batch, geometryShape, outColor); } else { geometryCache->renderShape(batch, geometryShape, outColor); } - } else if (!useMaterialPipeline()) { + } else if (!useMaterialPipeline(materials)) { // FIXME, support instanced multi-shape rendering using multidraw indirect outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; render::ShapePipelinePointer pipeline; @@ -262,7 +278,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { } } else { if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { - RenderPipelines::bindMaterial(mat, batch, args->_enableTexturing); + RenderPipelines::bindMaterials(materials, batch, args->_enableTexturing); args->_details._materialSwitches++; } @@ -281,8 +297,9 @@ scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel() { { std::lock_guard lock(_materialsLock); result.appendMaterials(_materials); - if (_materials["0"].top().material) { - vertexColor = _materials["0"].top().material->getAlbedo(); + auto materials = _materials.find("0"); + if (materials != _materials.end()) { + vertexColor = materials->second.getSchemaBuffer().get()._albedo; } } if (auto mesh = geometryCache->meshFromShape(geometryShape, vertexColor)) { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index a33f023213..ece13fabe9 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -35,12 +35,14 @@ private: virtual void doRender(RenderArgs* args) override; virtual bool isTransparent() const override; - bool useMaterialPipeline() const; + bool useMaterialPipeline(const graphics::MultiMaterial& materials) const; Procedural _procedural; QString _lastUserData; entity::Shape _shape { entity::Sphere }; - std::shared_ptr _material; + std::shared_ptr _material { std::make_shared() }; + glm::u8vec3 _color; + float _alpha; glm::vec3 _position; glm::vec3 _dimensions; glm::quat _orientation; diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 7a7e211d0d..819cbf9dad 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -112,7 +112,6 @@ EntityItemPointer ShapeEntityItem::sphereFactory(const EntityItemID& entityID, c ShapeEntityItem::ShapeEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { _type = EntityTypes::Shape; _volumeMultiplier *= PI / 6.0f; - _material = std::make_shared(); } EntityItemProperties ShapeEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { @@ -215,7 +214,6 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit void ShapeEntityItem::setColor(const glm::u8vec3& value) { withWriteLock([&] { _color = value; - _material->setAlbedo(toGlm(_color)); }); } @@ -228,7 +226,6 @@ glm::u8vec3 ShapeEntityItem::getColor() const { void ShapeEntityItem::setAlpha(float alpha) { withWriteLock([&] { _alpha = alpha; - _material->setOpacity(alpha); }); } diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index 6e36e15a84..417f0c6173 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -99,8 +99,6 @@ public: virtual void computeShapeInfo(ShapeInfo& info) override; virtual ShapeType getShapeType() const override; - std::shared_ptr getMaterial() { return _material; } - protected: glm::u8vec3 _color; float _alpha { 1.0f }; @@ -110,8 +108,6 @@ protected: //! prior functionality where new or unsupported shapes are treated as //! ellipsoids. ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_ELLIPSOID }; - - std::shared_ptr _material; }; #endif // hifi_ShapeEntityItem_h diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 104674eddc..43b0daf407 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -40,13 +40,13 @@ namespace scriptable { * @typedef {object} Graphics.Material * @property {string} name * @property {string} model - * @property {number} opacity - * @property {number} roughness - * @property {number} metallic - * @property {number} scattering - * @property {boolean} unlit - * @propety {Vec3} emissive - * @propety {Vec3} albedo + * @property {number|string} opacity + * @property {number|string} roughness + * @property {number|string} metallic + * @property {number|string} scattering + * @property {boolean|string} unlit + * @propety {Vec3|string} emissive + * @propety {Vec3|string} albedo * @property {string} emissiveMap * @property {string} albedoMap * @property {string} opacityMap @@ -59,6 +59,11 @@ namespace scriptable { * @property {string} occlusionMap * @property {string} lightmapMap * @property {string} scatteringMap + * @property {string} texCoordTransform0 + * @property {string} texCoordTransform1 + * @property {string} lightmapParams + * @property {string} materialParams + * @property {boolean} defaultFallthrough */ class ScriptableMaterial { public: @@ -88,6 +93,9 @@ namespace scriptable { QString occlusionMap; QString lightmapMap; QString scatteringMap; + + bool defaultFallthrough; + std::unordered_map propertyFallthroughs; // not actually exposed to script }; /**jsdoc diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 67f79db6b7..3293d294d8 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -362,25 +362,64 @@ namespace scriptable { QScriptValue obj = engine->newObject(); obj.setProperty("name", material.name); obj.setProperty("model", material.model); - obj.setProperty("opacity", material.opacity); - obj.setProperty("roughness", material.roughness); - obj.setProperty("metallic", material.metallic); - obj.setProperty("scattering", material.scattering); - obj.setProperty("unlit", material.unlit); - obj.setProperty("emissive", vec3ColorToScriptValue(engine, material.emissive)); - obj.setProperty("albedo", vec3ColorToScriptValue(engine, material.albedo)); - obj.setProperty("emissiveMap", material.emissiveMap); - obj.setProperty("albedoMap", material.albedoMap); + + 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)); + + 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("metallicMap", material.metallicMap); - obj.setProperty("specularMap", material.specularMap); - obj.setProperty("roughnessMap", material.roughnessMap); - obj.setProperty("glossMap", material.glossMap); - obj.setProperty("normalMap", material.normalMap); - obj.setProperty("bumpMap", material.bumpMap); - obj.setProperty("occlusionMap", material.occlusionMap); - obj.setProperty("lightmapMap", material.lightmapMap); - obj.setProperty("scatteringMap", material.scatteringMap); + 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); + + // Only set one of each of these + if (material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT)) { + obj.setProperty("metallicMap", FALLTHROUGH); + } else if (!material.metallicMap.isEmpty()) { + obj.setProperty("metallicMap", material.metallicMap); + } else if (!material.specularMap.isEmpty()) { + obj.setProperty("specularMap", material.specularMap); + } + + if (material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT)) { + obj.setProperty("roughnessMap", FALLTHROUGH); + } else if (!material.roughnessMap.isEmpty()) { + obj.setProperty("roughnessMap", material.roughnessMap); + } else if (!material.glossMap.isEmpty()) { + obj.setProperty("glossMap", material.glossMap); + } + + if (material.propertyFallthroughs.at(graphics::MaterialKey::NORMAL_MAP_BIT)) { + obj.setProperty("normalMap", FALLTHROUGH); + } else if (!material.normalMap.isEmpty()) { + obj.setProperty("normalMap", material.normalMap); + } else if (!material.bumpMap.isEmpty()) { + obj.setProperty("bumpMap", material.bumpMap); + } + + // These need to be implemented, but set the fallthrough for now + if (material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM0)) { + obj.setProperty("texCoordTransform0", FALLTHROUGH); + } + if (material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM1)) { + obj.setProperty("texCoordTransform1", FALLTHROUGH); + } + if (material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) { + obj.setProperty("lightmapParams", FALLTHROUGH); + } + if (material.propertyFallthroughs.at(graphics::Material::MATERIAL_PARAMS)) { + obj.setProperty("materialParams", FALLTHROUGH); + } + + obj.setProperty("defaultFallthrough", material.defaultFallthrough); + return obj; } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index 51085561c3..4ff751782c 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -42,6 +42,9 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const lightmapMap = material.lightmapMap; scatteringMap = material.scatteringMap; + defaultFallthrough = material.defaultFallthrough; + propertyFallthroughs = material.propertyFallthroughs; + return *this; } @@ -54,7 +57,9 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint scattering(material->getScattering()), unlit(material->isUnlit()), emissive(material->getEmissive()), - albedo(material->getAlbedo()) + albedo(material->getAlbedo()), + defaultFallthrough(material->getDefaultFallthrough()), + propertyFallthroughs(material->getPropertyFallthroughs()) { auto map = material->getTextureMap(graphics::Material::MapChannel::EMISSIVE_MAP); if (map && map->getTextureSource()) { diff --git a/libraries/graphics/src/graphics/Material.cpp b/libraries/graphics/src/graphics/Material.cpp index be99cea681..7befb7e053 100755 --- a/libraries/graphics/src/graphics/Material.cpp +++ b/libraries/graphics/src/graphics/Material.cpp @@ -17,125 +17,125 @@ using namespace graphics; using namespace gpu; -Material::Material() : - _key(0), - _schemaBuffer(), - _textureMaps() -{ - // created from nothing: create the Buffer to store the properties - Schema schema; - _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema))); +const float Material::DEFAULT_EMISSIVE { 0.0f }; +const float Material::DEFAULT_OPACITY { 1.0f }; +const float Material::DEFAULT_ALBEDO { 0.5f }; +const float Material::DEFAULT_METALLIC { 0.0f }; +const float Material::DEFAULT_ROUGHNESS { 1.0f }; +const float Material::DEFAULT_SCATTERING { 0.0f }; + +Material::Material() { + for (int i = 0; i < NUM_TOTAL_FLAGS; i++) { + _propertyFallthroughs[i] = false; + } } Material::Material(const Material& material) : _name(material._name), + _model(material._model), _key(material._key), - _textureMaps(material._textureMaps) + _emissive(material._emissive), + _opacity(material._opacity), + _albedo(material._albedo), + _roughness(material._roughness), + _metallic(material._metallic), + _scattering(material._scattering), + _texcoordTransforms(material._texcoordTransforms), + _lightmapParams(material._lightmapParams), + _materialParams(material._materialParams), + _textureMaps(material._textureMaps), + _defaultFallthrough(material._defaultFallthrough), + _propertyFallthroughs(material._propertyFallthroughs) { - // copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer - Schema schema; - _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema))); - _schemaBuffer.edit() = material._schemaBuffer.get(); } -Material& Material::operator= (const Material& material) { +Material& Material::operator=(const Material& material) { QMutexLocker locker(&_textureMapsMutex); _name = material._name; + _model = material._model; + _key = material._key; + _emissive = material._emissive; + _opacity = material._opacity; + _albedo = material._albedo; + _roughness = material._roughness; + _metallic = material._metallic; + _scattering = material._scattering; + _texcoordTransforms = material._texcoordTransforms; + _lightmapParams = material._lightmapParams; + _materialParams = material._materialParams; + _textureMaps = material._textureMaps; - _key = (material._key); - _textureMaps = (material._textureMaps); - _hasCalculatedTextureInfo = false; - - // copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer - Schema schema; - _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema))); - _schemaBuffer.edit() = material._schemaBuffer.get(); + _defaultFallthrough = material._defaultFallthrough; + _propertyFallthroughs = material._propertyFallthroughs; return (*this); } -Material::~Material() { -} - -void Material::setEmissive(const Color& emissive, bool isSRGB) { - _key.setEmissive(glm::any(glm::greaterThan(emissive, Color(0.0f)))); - _schemaBuffer.edit()._key = (uint32) _key._flags.to_ulong(); - _schemaBuffer.edit()._emissive = (isSRGB ? ColorUtils::sRGBToLinearVec3(emissive) : emissive); +void Material::setEmissive(const glm::vec3& emissive, bool isSRGB) { + _key.setEmissive(glm::any(glm::greaterThan(emissive, glm::vec3(0.0f)))); + _emissive = (isSRGB ? ColorUtils::sRGBToLinearVec3(emissive) : emissive); } void Material::setOpacity(float opacity) { _key.setTranslucentFactor((opacity < 1.0f)); - _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); - _schemaBuffer.edit()._opacity = opacity; + _opacity = opacity; } void Material::setUnlit(bool value) { _key.setUnlit(value); - _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); } -void Material::setAlbedo(const Color& albedo, bool isSRGB) { - _key.setAlbedo(glm::any(glm::greaterThan(albedo, Color(0.0f)))); - _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); - _schemaBuffer.edit()._albedo = (isSRGB ? ColorUtils::sRGBToLinearVec3(albedo) : albedo); +void Material::setAlbedo(const glm::vec3& albedo, bool isSRGB) { + _key.setAlbedo(glm::any(glm::greaterThan(albedo, glm::vec3(0.0f)))); + _albedo = (isSRGB ? ColorUtils::sRGBToLinearVec3(albedo) : albedo); } void Material::setRoughness(float roughness) { roughness = std::min(1.0f, std::max(roughness, 0.0f)); - _key.setGlossy((roughness < 1.0f)); - _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); - _schemaBuffer.edit()._roughness = roughness; + _key.setGlossy(roughness < 1.0f); + _roughness = roughness; } void Material::setMetallic(float metallic) { metallic = glm::clamp(metallic, 0.0f, 1.0f); _key.setMetallic(metallic > 0.0f); - _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); - _schemaBuffer.edit()._metallic = metallic; + _metallic = metallic; } void Material::setScattering(float scattering) { scattering = glm::clamp(scattering, 0.0f, 1.0f); _key.setMetallic(scattering > 0.0f); - _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); - _schemaBuffer.edit()._scattering = scattering; + _scattering = scattering; } void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) { QMutexLocker locker(&_textureMapsMutex); if (textureMap) { - _key.setMapChannel(channel, (true)); + _key.setMapChannel(channel, true); _textureMaps[channel] = textureMap; } else { - _key.setMapChannel(channel, (false)); + _key.setMapChannel(channel, false); _textureMaps.erase(channel); } - _hasCalculatedTextureInfo = false; - - _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); if (channel == MaterialKey::ALBEDO_MAP) { resetOpacityMap(); - - // update the texcoord0 with albedo - _schemaBuffer.edit()._texcoordTransforms[0] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4()); + _texcoordTransforms[0] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4()); } if (channel == MaterialKey::OCCLUSION_MAP) { - _schemaBuffer.edit()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4()); + _texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4()); } if (channel == MaterialKey::LIGHTMAP_MAP) { // update the texcoord1 with lightmap - _schemaBuffer.edit()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4()); - _schemaBuffer.edit()._lightmapParams = (textureMap ? glm::vec2(textureMap->getLightmapOffsetScale()) : glm::vec2(0.0, 1.0)); + _texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4()); + _lightmapParams = (textureMap ? glm::vec2(textureMap->getLightmapOffsetScale()) : glm::vec2(0.0, 1.0)); } - _schemaBuffer.edit()._materialParams = (textureMap ? glm::vec2(textureMap->getMappingMode(), textureMap->getRepeat()) : glm::vec2(MaterialMappingMode::UV, 1.0)); - - _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); + _materialParams = (textureMap ? glm::vec2(textureMap->getMappingMode(), textureMap->getRepeat()) : glm::vec2(MaterialMappingMode::UV, 1.0)); } @@ -163,11 +163,8 @@ void Material::resetOpacityMap() const { } } } - - _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); } - const TextureMapPointer Material::getTextureMap(MapChannel channel) const { QMutexLocker locker(&_textureMapsMutex); @@ -179,40 +176,6 @@ const TextureMapPointer Material::getTextureMap(MapChannel channel) const { } } - -bool Material::calculateMaterialInfo() const { - if (!_hasCalculatedTextureInfo) { - QMutexLocker locker(&_textureMapsMutex); - - bool allTextures = true; // assume we got this... - _textureSize = 0; - _textureCount = 0; - - for (auto const &textureMapItem : _textureMaps) { - auto textureMap = textureMapItem.second; - if (textureMap) { - auto textureSoure = textureMap->getTextureSource(); - if (textureSoure) { - auto texture = textureSoure->getGPUTexture(); - if (texture) { - auto size = texture->getSize(); - _textureSize += size; - _textureCount++; - } else { - allTextures = false; - } - } else { - allTextures = false; - } - } else { - allTextures = false; - } - } - _hasCalculatedTextureInfo = allTextures; - } - return _hasCalculatedTextureInfo; -} - void Material::setTextureTransforms(const Transform& transform, MaterialMappingMode mode, bool repeat) { for (auto &textureMapItem : _textureMaps) { if (textureMapItem.second) { @@ -222,7 +185,32 @@ void Material::setTextureTransforms(const Transform& transform, MaterialMappingM } } for (int i = 0; i < NUM_TEXCOORD_TRANSFORMS; i++) { - _schemaBuffer.edit()._texcoordTransforms[i] = transform.getMatrix(); + _texcoordTransforms[i] = transform.getMatrix(); } - _schemaBuffer.edit()._materialParams = glm::vec2(mode, repeat); + _materialParams = glm::vec2(mode, repeat); } + +MultiMaterial::MultiMaterial() { + Schema schema; + _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema))); +} + +void MultiMaterial::calculateMaterialInfo() const { + if (!_hasCalculatedTextureInfo) { + bool allTextures = true; // assume we got this... + _textureSize = 0; + _textureCount = 0; + + auto textures = _textureTable->getTextures(); + for (auto const &texture : textures) { + if (texture && texture->isDefined()) { + auto size = texture->getSize(); + _textureSize += size; + _textureCount++; + } else { + allTextures = false; + } + } + _hasCalculatedTextureInfo = allTextures; + } +} \ No newline at end of file diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index 9711bd9000..fdddf3640a 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -176,7 +177,6 @@ public: bool isTexelOpaque() const { return isOpaque() && isOpacityMaskMap(); } }; - class MaterialFilter { public: MaterialKey::Flags _value{ 0 }; @@ -266,84 +266,44 @@ public: class Material { public: - typedef gpu::BufferView UniformBufferView; - - typedef glm::vec3 Color; - - // Texture Map Array Schema - static const int NUM_TEXCOORD_TRANSFORMS{ 2 }; - typedef MaterialKey::MapChannel MapChannel; typedef std::map TextureMaps; - typedef std::bitset MapFlags; Material(); Material(const Material& material); Material& operator= (const Material& material); - virtual ~Material(); const MaterialKey& getKey() const { return _key; } - void setEmissive(const Color& emissive, bool isSRGB = true); - Color getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get()._emissive) : _schemaBuffer.get()._emissive); } + static const float DEFAULT_EMISSIVE; + void setEmissive(const glm::vec3& emissive, bool isSRGB = true); + glm::vec3 getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_emissive) : _emissive); } + static const float DEFAULT_OPACITY; void setOpacity(float opacity); - float getOpacity() const { return _schemaBuffer.get()._opacity; } + float getOpacity() const { return _opacity; } void setUnlit(bool value); bool isUnlit() const { return _key.isUnlit(); } - void setAlbedo(const Color& albedo, bool isSRGB = true); - Color getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get()._albedo) : _schemaBuffer.get()._albedo); } + static const float DEFAULT_ALBEDO; + void setAlbedo(const glm::vec3& albedo, bool isSRGB = true); + glm::vec3 getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_albedo) : _albedo); } + static const float DEFAULT_METALLIC; void setMetallic(float metallic); - float getMetallic() const { return _schemaBuffer.get()._metallic; } + float getMetallic() const { return _metallic; } + static const float DEFAULT_ROUGHNESS; void setRoughness(float roughness); - float getRoughness() const { return _schemaBuffer.get()._roughness; } + float getRoughness() const { return _roughness; } + static const float DEFAULT_SCATTERING; void setScattering(float scattering); - float getScattering() const { return _schemaBuffer.get()._scattering; } - - // Schema to access the attribute values of the material - class Schema { - public: - glm::vec3 _emissive { 0.0f }; // No Emissive - float _opacity { 1.0f }; // Opacity = 1 => Not Transparent - - glm::vec3 _albedo { 0.5f }; // Grey albedo => isAlbedo - float _roughness { 1.0f }; // Roughness = 1 => Not Glossy - - float _metallic { 0.0f }; // Not Metallic - float _scattering { 0.0f }; // Scattering info -#if defined(__clang__) - __attribute__((unused)) -#endif - glm::vec2 _spare { 0.0f }; // Padding - - uint32_t _key { 0 }; // a copy of the materialKey -#if defined(__clang__) - __attribute__((unused)) -#endif - glm::vec3 _spare2 { 0.0f }; - - // for alignment beauty, Material size == Mat4x4 - - // Texture Coord Transform Array - glm::mat4 _texcoordTransforms[NUM_TEXCOORD_TRANSFORMS]; - - glm::vec2 _lightmapParams { 0.0, 1.0 }; - - // x: material mode (0 for UV, 1 for PROJECTED) - // y: 1 for texture repeat, 0 for discard outside of 0 - 1 - glm::vec2 _materialParams { 0.0, 1.0 }; - - Schema() {} - }; - - const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; } + float getScattering() const { return _scattering; } // The texture map to channel association + static const int NUM_TEXCOORD_TRANSFORMS { 2 }; void setTextureMap(MapChannel channel, const TextureMapPointer& textureMap); const TextureMaps& getTextureMaps() const { return _textureMaps; } // FIXME - not thread safe... const TextureMapPointer getTextureMap(MapChannel channel) const; @@ -355,10 +315,6 @@ public: // conversion from legacy material properties to PBR equivalent static float shininessToRoughness(float shininess) { return 1.0f - shininess / 100.0f; } - int getTextureCount() const { calculateMaterialInfo(); return _textureCount; } - size_t getTextureSize() const { calculateMaterialInfo(); return _textureSize; } - bool hasTextureInfo() const { return _hasCalculatedTextureInfo; } - void setTextureTransforms(const Transform& transform, MaterialMappingMode mode, bool repeat); const std::string& getName() const { return _name; } @@ -366,28 +322,50 @@ public: const std::string& getModel() const { return _model; } void setModel(const std::string& model) { _model = model; } - const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; } + glm::mat4 getTexCoordTransform(uint i) const { return _texcoordTransforms[i]; } + glm::vec2 getLightmapParams() const { return _lightmapParams; } + glm::vec2 getMaterialParams() const { return _materialParams; } + + bool getDefaultFallthrough() const { return _defaultFallthrough; } + void setDefaultFallthrough(bool defaultFallthrough) { _defaultFallthrough = defaultFallthrough; } + + enum ExtraFlagBit { + TEXCOORDTRANSFORM0 = MaterialKey::NUM_FLAGS, + TEXCOORDTRANSFORM1, + LIGHTMAP_PARAMS, + MATERIAL_PARAMS, + + NUM_TOTAL_FLAGS + }; + std::unordered_map getPropertyFallthroughs() { return _propertyFallthroughs; } + bool getPropertyFallthrough(uint property) { return _propertyFallthroughs[property]; } + void setPropertyDoesFallthrough(uint property) { _propertyFallthroughs[property] = true; } protected: std::string _name { "" }; private: - mutable MaterialKey _key; - mutable UniformBufferView _schemaBuffer; - mutable gpu::TextureTablePointer _textureTable{ std::make_shared() }; + std::string _model { "hifi_pbr" }; + mutable MaterialKey _key { 0 }; + // Material properties + glm::vec3 _emissive { DEFAULT_EMISSIVE }; + float _opacity { DEFAULT_OPACITY }; + glm::vec3 _albedo { DEFAULT_ALBEDO }; + float _roughness { DEFAULT_ROUGHNESS }; + float _metallic { DEFAULT_METALLIC }; + float _scattering { DEFAULT_SCATTERING }; + std::array _texcoordTransforms; + glm::vec2 _lightmapParams { 0.0, 1.0 }; + glm::vec2 _materialParams { 0.0, 1.0 }; TextureMaps _textureMaps; + bool _defaultFallthrough { false }; + std::unordered_map _propertyFallthroughs { NUM_TOTAL_FLAGS }; + mutable QMutex _textureMapsMutex { QMutex::Recursive }; - mutable size_t _textureSize { 0 }; - mutable int _textureCount { 0 }; - mutable bool _hasCalculatedTextureInfo { false }; - bool calculateMaterialInfo() const; - - std::string _model { "hifi_pbr" }; - }; -typedef std::shared_ptr< Material > MaterialPointer; +typedef std::shared_ptr MaterialPointer; class MaterialLayer { public: @@ -403,9 +381,18 @@ public: return left.priority < right.priority; } }; +typedef std::priority_queue, MaterialLayerCompare> MaterialLayerQueue; -class MultiMaterial : public std::priority_queue, MaterialLayerCompare> { +class MultiMaterial : public MaterialLayerQueue { public: + MultiMaterial(); + + void push(const MaterialLayer& value) { + MaterialLayerQueue::push(value); + _hasCalculatedTextureInfo = false; + _needsUpdate = true; + } + bool remove(const MaterialPointer& value) { auto it = c.begin(); while (it != c.end()) { @@ -417,11 +404,78 @@ public: if (it != c.end()) { c.erase(it); std::make_heap(c.begin(), c.end(), comp); + _hasCalculatedTextureInfo = false; + _needsUpdate = true; return true; } else { return false; } } + + // Schema to access the attribute values of the material + class Schema { + public: + glm::vec3 _emissive { Material::DEFAULT_EMISSIVE }; // No Emissive + float _opacity { Material::DEFAULT_OPACITY }; // Opacity = 1 => Not Transparent + + glm::vec3 _albedo { Material::DEFAULT_ALBEDO }; // Grey albedo => isAlbedo + float _roughness { Material::DEFAULT_ROUGHNESS }; // Roughness = 1 => Not Glossy + + float _metallic { Material::DEFAULT_METALLIC }; // Not Metallic + float _scattering { Material::DEFAULT_SCATTERING }; // Scattering info +#if defined(__clang__) + __attribute__((unused)) +#endif + glm::vec2 _spare { 0.0f }; // Padding + + uint32_t _key { 0 }; // a copy of the materialKey +#if defined(__clang__) + __attribute__((unused)) +#endif + glm::vec3 _spare2 { 0.0f }; + + // for alignment beauty, Material size == Mat4x4 + + // Texture Coord Transform Array + glm::mat4 _texcoordTransforms[Material::NUM_TEXCOORD_TRANSFORMS]; + + glm::vec2 _lightmapParams { 0.0, 1.0 }; + + // x: material mode (0 for UV, 1 for PROJECTED) + // y: 1 for texture repeat, 0 for discard outside of 0 - 1 + glm::vec2 _materialParams { 0.0, 1.0 }; + + Schema() { + for (auto& transform : _texcoordTransforms) { + transform = glm::mat4(); + } + } + }; + + gpu::BufferView& getSchemaBuffer() { return _schemaBuffer; } + graphics::MaterialKey getMaterialKey() const { return graphics::MaterialKey(_schemaBuffer.get()._key); } + const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; } + + bool needsUpdate() const { return _needsUpdate; } + void setNeedsUpdate(bool needsUpdate) { _needsUpdate = needsUpdate; } + + void setTexturesLoading(bool value) { _texturesLoading = value; } + bool areTexturesLoading() const { return _texturesLoading; } + + int getTextureCount() const { calculateMaterialInfo(); return _textureCount; } + size_t getTextureSize() const { calculateMaterialInfo(); return _textureSize; } + bool hasTextureInfo() const { return _hasCalculatedTextureInfo; } + +private: + gpu::BufferView _schemaBuffer; + gpu::TextureTablePointer _textureTable { std::make_shared() }; + bool _needsUpdate { false }; + bool _texturesLoading { false }; + + mutable size_t _textureSize { 0 }; + mutable int _textureCount { 0 }; + mutable bool _hasCalculatedTextureInfo { false }; + void calculateMaterialInfo() const; }; }; diff --git a/libraries/model-networking/src/model-networking/MaterialCache.cpp b/libraries/model-networking/src/model-networking/MaterialCache.cpp index e6e3b0e812..b6550a5e9e 100644 --- a/libraries/model-networking/src/model-networking/MaterialCache.cpp +++ b/libraries/model-networking/src/model-networking/MaterialCache.cpp @@ -111,146 +111,300 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater /**jsdoc * A material such as may be used by a {@link Entities.EntityType|Material} entity. * @typedef {object} Material - * @property {string} name="" - A name for the material. - * @property {string} model="hifi_pbr" - Currently not used. - * @property {Color|RGBS} emissive - The emissive color, i.e., the color that the material emits. A {@link Color} value - * is treated as sRGB. A {@link RGBS} value can be either RGB or sRGB. - * @property {number} opacity=1.0 - The opacity, 0.01.0. - * @property {boolean} unlit=false - If true, the material is not lit. - * @property {Color|RGBS} albedo - The albedo color. A {@link Color} value is treated as sRGB. A {@link RGBS} value can - * be either RGB or sRGB. - * @property {number} roughness - The roughness, 0.01.0. - * @property {number} metallic - The metallicness, 0.01.0. - * @property {number} scattering - The scattering, 0.01.0. - * @property {string} emissiveMap - URL of emissive texture image. - * @property {string} albedoMap - URL of albedo texture image. + * @property {string} model="hifi_pbr" - Different material models support different properties and rendering modes. + * Supported models are: "hifi_pbr" + * @property {string} name="" - A name for the material. Supported by all material models. + * @property {Color|RGBS|string} emissive - The emissive color, i.e., the color that the material emits. A {@link Color} value + * is treated as sRGB. A {@link RGBS} value can be either RGB or sRGB. Set to "fallthrough" to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {number|string} opacity=1.0 - The opacity, 0.01.0. Set to "fallthrough" to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {boolean|string} unlit=false - If true, the material is not lit. Set to "fallthrough" to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {Color|RGBS|string} albedo - The albedo color. A {@link Color} value is treated as sRGB. A {@link RGBS} value can + * be either RGB or sRGB. Set to "fallthrough" to fallthrough to the material below. Set to "fallthrough" to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {number|string} roughness - The roughness, 0.01.0. Set to "fallthrough" to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {number|string} metallic - The metallicness, 0.01.0. Set to "fallthrough" to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {number|string} scattering - The scattering, 0.01.0. Set to "fallthrough" to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {string} emissiveMap - URL of emissive texture image. Set to "fallthrough" to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {string} albedoMap - URL of albedo texture image. Set to "fallthrough" to fallthrough to + * the material below. "hifi_pbr" model only. * @property {string} opacityMap - URL of opacity texture image. Set value the same as the albedoMap value for - * transparency. - * @property {string} roughnessMap - URL of roughness texture image. Can use this or glossMap, but not both. - * @property {string} glossMap - URL of gloss texture image. Can use this or roughnessMap, but not both. - * @property {string} metallicMap - URL of metallic texture image. Can use this or specularMap, but not both. - * @property {string} specularMap - URL of specular texture image. Can use this or metallicMap, but not both. - * @property {string} normalMap - URL of normal texture image. Can use this or bumpMap, but not both. - * @property {string} bumpMap - URL of bump texture image. Can use this or normalMap, but not both. - * @property {string} occlusionMap - URL of occlusion texture image. + * transparency. "hifi_pbr" model only. + * @property {string} roughnessMap - URL of roughness texture image. Can use this or glossMap, but not both. Set to "fallthrough" + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} glossMap - URL of gloss texture image. Can use this or roughnessMap, but not both. Set to "fallthrough" + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} metallicMap - URL of metallic texture image. Can use this or specularMap, but not both. Set to "fallthrough" + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} specularMap - URL of specular texture image. Can use this or metallicMap, but not both. Set to "fallthrough" + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} normalMap - URL of normal texture image. Can use this or bumpMap, but not both. Set to "fallthrough" + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} bumpMap - URL of bump texture image. Can use this or normalMap, but not both. Set to "fallthrough" + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} occlusionMap - URL of occlusion texture image. Set to "fallthrough" to fallthrough to the material below. "hifi_pbr" model only. * @property {string} scatteringMap - URL of scattering texture image. Only used if normalMap or - * bumpMap is specified. - * @property {string} lightMap - URL of light map texture image. Currently not used. + * bumpMap is specified. Set to "fallthrough" to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} lightMap - URL of light map texture image. Currently not used.. Set to "fallthrough" + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} texCoordTransform0 - The transform to use for all of the maps besides occlusionMap and lightMap. Currently unused. Set to + * "fallthrough" to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} texCoordTransform1 - The transform to use for occlusionMap and lightMap. Currently unused. Set to "fallthrough" + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} lightmapParams - Parameters for controlling how lightMap is used. Currently unused. Set to "fallthrough" + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} materialParams - Parameters for controlling the material projection and repition. Currently unused. Set to "fallthrough" + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {bool} defaultFallthrough=false - If true, all properties will fallthrough to the material below unless they are set. If + * false, they will respect the individual properties' fallthrough state. "hifi_pbr" model only. */ // Note: See MaterialEntityItem.h for default values used in practice. std::pair> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl) { std::string name = ""; std::shared_ptr material = std::make_shared(); - for (auto& key : materialJSON.keys()) { - if (key == "name") { - auto nameJSON = materialJSON.value(key); - if (nameJSON.isString()) { - name = nameJSON.toString().toStdString(); - } - } else if (key == "model") { - auto modelJSON = materialJSON.value(key); - if (modelJSON.isString()) { - material->setModel(modelJSON.toString().toStdString()); - } - } else if (key == "emissive") { - glm::vec3 color; - bool isSRGB; - bool valid = parseJSONColor(materialJSON.value(key), color, isSRGB); - if (valid) { - material->setEmissive(color, isSRGB); - } - } else if (key == "opacity") { - auto value = materialJSON.value(key); - if (value.isDouble()) { - material->setOpacity(value.toDouble()); - } - } else if (key == "unlit") { - auto value = materialJSON.value(key); - if (value.isBool()) { - material->setUnlit(value.toBool()); - } - } else if (key == "albedo") { - glm::vec3 color; - bool isSRGB; - bool valid = parseJSONColor(materialJSON.value(key), color, isSRGB); - if (valid) { - material->setAlbedo(color, isSRGB); - } - } else if (key == "roughness") { - auto value = materialJSON.value(key); - if (value.isDouble()) { - material->setRoughness(value.toDouble()); - } - } else if (key == "metallic") { - auto value = materialJSON.value(key); - if (value.isDouble()) { - material->setMetallic(value.toDouble()); - } - } else if (key == "scattering") { - auto value = materialJSON.value(key); - if (value.isDouble()) { - material->setScattering(value.toDouble()); - } - } else if (key == "emissiveMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - material->setEmissiveMap(baseUrl.resolved(value.toString())); - } - } else if (key == "albedoMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - QString urlString = value.toString(); - bool useAlphaChannel = false; - auto opacityMap = materialJSON.find("opacityMap"); - if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == urlString) { - useAlphaChannel = true; + + const std::string HIFI_PBR = "hifi_pbr"; + std::string modelString = HIFI_PBR; + auto modelJSONIter = materialJSON.find("model"); + if (modelJSONIter != materialJSON.end() && modelJSONIter.value().isString()) { + modelString = modelJSONIter.value().toString().toStdString(); + material->setModel(modelString); + } + + if (modelString == HIFI_PBR) { + const QString FALLTHROUGH("fallthrough"); + for (auto& key : materialJSON.keys()) { + if (key == "name") { + auto nameJSON = materialJSON.value(key); + if (nameJSON.isString()) { + name = nameJSON.toString().toStdString(); + } + } else if (key == "model") { + auto modelJSON = materialJSON.value(key); + if (modelJSON.isString()) { + material->setModel(modelJSON.toString().toStdString()); + } + } else if (key == "emissive") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::EMISSIVE_VAL_BIT); + } else { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(value, color, isSRGB); + if (valid) { + material->setEmissive(color, isSRGB); + } + } + } else if (key == "opacity") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OPACITY_VAL_BIT); + } else if (value.isDouble()) { + material->setOpacity(value.toDouble()); + } + } else if (key == "unlit") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::UNLIT_VAL_BIT); + } else if (value.isBool()) { + material->setUnlit(value.toBool()); + } + } else if (key == "albedo") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ALBEDO_VAL_BIT); + } else { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(value, color, isSRGB); + if (valid) { + material->setAlbedo(color, isSRGB); + } + } + } else if (key == "roughness") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::GLOSSY_VAL_BIT); + } else if (value.isDouble()) { + material->setRoughness(value.toDouble()); + } + } else if (key == "metallic") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_VAL_BIT); + } else if (value.isDouble()) { + material->setMetallic(value.toDouble()); + } + } else if (key == "scattering") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_VAL_BIT); + } else if (value.isDouble()) { + material->setScattering(value.toDouble()); + } + } else if (key == "emissiveMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::EMISSIVE_MAP_BIT); + } else { + material->setEmissiveMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "albedoMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + QString valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ALBEDO_MAP_BIT); + } else { + bool useAlphaChannel = false; + auto opacityMap = materialJSON.find("opacityMap"); + if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == valueString) { + useAlphaChannel = true; + } + material->setAlbedoMap(baseUrl.resolved(valueString), useAlphaChannel); + } + } + } else if (key == "roughnessMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT); + } else { + material->setRoughnessMap(baseUrl.resolved(valueString), false); + } + } + } else if (key == "glossMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT); + } else { + material->setRoughnessMap(baseUrl.resolved(valueString), true); + } + } + } else if (key == "metallicMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT); + } else { + material->setMetallicMap(baseUrl.resolved(valueString), false); + } + } + } else if (key == "specularMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT); + } else { + material->setMetallicMap(baseUrl.resolved(valueString), true); + } + } + } else if (key == "normalMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::NORMAL_MAP_BIT); + } else { + material->setNormalMap(baseUrl.resolved(valueString), false); + } + } + } else if (key == "bumpMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::NORMAL_MAP_BIT); + } else { + material->setNormalMap(baseUrl.resolved(valueString), true); + } + } + } else if (key == "occlusionMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OCCLUSION_MAP_BIT); + } else { + material->setOcclusionMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "scatteringMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_MAP_BIT); + } else { + material->setScatteringMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "lightMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::LIGHTMAP_MAP_BIT); + } else { + material->setLightmapMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "texCoordTransform0") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::TEXCOORDTRANSFORM0); + } + } + // TODO: implement texCoordTransform0 + } else if (key == "texCoordTransform1") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::TEXCOORDTRANSFORM1); + } + } + // TODO: implement texCoordTransform1 + } else if (key == "lightmapParams") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::LIGHTMAP_PARAMS); + } + } + // TODO: implement lightmapParams + } else if (key == "materialParams") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::MATERIAL_PARAMS); + } + } + // TODO: implement materialParams + } else if (key == "defaultFallthrough") { + auto value = materialJSON.value(key); + if (value.isBool()) { + material->setDefaultFallthrough(value.toBool()); } - material->setAlbedoMap(baseUrl.resolved(urlString), useAlphaChannel); - } - } else if (key == "roughnessMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - material->setRoughnessMap(baseUrl.resolved(value.toString()), false); - } - } else if (key == "glossMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - material->setRoughnessMap(baseUrl.resolved(value.toString()), true); - } - } else if (key == "metallicMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - material->setMetallicMap(baseUrl.resolved(value.toString()), false); - } - } else if (key == "specularMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - material->setMetallicMap(baseUrl.resolved(value.toString()), true); - } - } else if (key == "normalMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - material->setNormalMap(baseUrl.resolved(value.toString()), false); - } - } else if (key == "bumpMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - material->setNormalMap(baseUrl.resolved(value.toString()), true); - } - } else if (key == "occlusionMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - material->setOcclusionMap(baseUrl.resolved(value.toString())); - } - } else if (key == "scatteringMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - material->setScatteringMap(baseUrl.resolved(value.toString())); - } - } else if (key == "lightMap") { - auto value = materialJSON.value(key); - if (value.isString()) { - material->setLightmapMap(baseUrl.resolved(value.toString())); } } } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 7a52ad77da..bb2e784807 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -49,8 +49,6 @@ template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderAr } } -const graphics::MaterialPointer MeshPartPayload::DEFAULT_MATERIAL = std::make_shared(); - MeshPartPayload::MeshPartPayload(const std::shared_ptr& mesh, int partIndex, graphics::MaterialPointer material) { updateMeshPart(mesh, partIndex); addMaterial(graphics::MaterialLayer(material, 0)); @@ -85,11 +83,13 @@ void MeshPartPayload::updateKey(const render::ItemKey& key) { ItemKey::Builder builder(key); builder.withTypeShape(); - if (topMaterialExists()) { - auto matKey = _drawMaterials.top().material->getKey(); - if (matKey.isTranslucent()) { - builder.withTransparent(); - } + if (_drawMaterials.needsUpdate()) { + RenderPipelines::updateMultiMaterial(_drawMaterials); + } + + auto matKey = _drawMaterials.getMaterialKey(); + if (matKey.isTranslucent()) { + builder.withTransparent(); } _itemKey = builder.build(); @@ -104,10 +104,7 @@ Item::Bound MeshPartPayload::getBound() const { } ShapeKey MeshPartPayload::getShapeKey() const { - graphics::MaterialKey drawMaterialKey; - if (topMaterialExists()) { - drawMaterialKey = _drawMaterials.top().material->getKey(); - } + graphics::MaterialKey drawMaterialKey = _drawMaterials.getMaterialKey(); ShapeKey::Builder builder; builder.withMaterial(); @@ -158,7 +155,7 @@ void MeshPartPayload::render(RenderArgs* args) { // apply material properties if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { - RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing); + RenderPipelines::bindMaterials(_drawMaterials, batch, args->_enableTexturing); args->_details._materialSwitches++; } @@ -332,11 +329,13 @@ void ModelMeshPartPayload::updateKey(const render::ItemKey& key) { builder.withDeformed(); } - if (topMaterialExists()) { - auto matKey = _drawMaterials.top().material->getKey(); - if (matKey.isTranslucent()) { - builder.withTransparent(); - } + if (_drawMaterials.needsUpdate()) { + RenderPipelines::updateMultiMaterial(_drawMaterials); + } + + auto matKey = _drawMaterials.getMaterialKey(); + if (matKey.isTranslucent()) { + builder.withTransparent(); } _itemKey = builder.build(); @@ -348,11 +347,12 @@ void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, PrimitiveMode pr return; } - graphics::MaterialKey drawMaterialKey; - if (topMaterialExists()) { - drawMaterialKey = _drawMaterials.top().material->getKey(); + if (_drawMaterials.needsUpdate()) { + RenderPipelines::updateMultiMaterial(_drawMaterials); } + graphics::MaterialKey drawMaterialKey = _drawMaterials.getMaterialKey(); + bool isTranslucent = drawMaterialKey.isTranslucent(); bool hasTangents = drawMaterialKey.isNormalMap() && _hasTangents; bool hasLightmap = drawMaterialKey.isLightmapMap(); @@ -435,7 +435,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { // apply material properties if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { - RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing); + RenderPipelines::bindMaterials(_drawMaterials, batch, args->_enableTexturing); args->_details._materialSwitches++; } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 3b0590b4a9..deae91dda9 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -66,18 +66,15 @@ public: graphics::Mesh::Part _drawPart; size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; } - size_t getMaterialTextureSize() { return topMaterialExists() ? _drawMaterials.top().material->getTextureSize() : 0; } - int getMaterialTextureCount() { return topMaterialExists() ? _drawMaterials.top().material->getTextureCount() : 0; } - bool hasTextureInfo() const { return topMaterialExists() ? _drawMaterials.top().material->hasTextureInfo() : false; } + size_t getMaterialTextureSize() { return _drawMaterials.getTextureSize(); } + int getMaterialTextureCount() { return _drawMaterials.getTextureCount(); } + bool hasTextureInfo() const { return _drawMaterials.hasTextureInfo(); } void addMaterial(graphics::MaterialLayer material); void removeMaterial(graphics::MaterialPointer material); protected: - static const graphics::MaterialPointer DEFAULT_MATERIAL; render::ItemKey _itemKey{ render::ItemKey::Builder::opaqueShape().build() }; - - bool topMaterialExists() const { return !_drawMaterials.empty() && _drawMaterials.top().material; } }; namespace render { diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index cd685a54a1..07dc683719 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -308,24 +308,33 @@ void addPlumberPipeline(ShapePlumber& plumber, void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* args) { // Set a default albedo map - batch.setResourceTexture(gr::Texture::MaterialAlbedo, - DependencyManager::get()->getWhiteTexture()); + batch.setResourceTexture(gr::Texture::MaterialAlbedo, DependencyManager::get()->getWhiteTexture()); // Set a default material if (pipeline.locations->materialBufferUnit) { // Create a default schema - static bool isMaterialSet = false; - static graphics::Material material; - if (!isMaterialSet) { - material.setAlbedo(vec3(1.0f)); - material.setOpacity(1.0f); - material.setMetallic(0.1f); - material.setRoughness(0.9f); - isMaterialSet = true; - } + static gpu::BufferView schemaBuffer; + static std::once_flag once; + std::call_once(once, [] { + graphics::MultiMaterial::Schema schema; + graphics::MaterialKey schemaKey; - // Set a default schema - batch.setUniformBuffer(gr::Buffer::Material, material.getSchemaBuffer()); + schema._albedo = vec3(1.0f); + schema._opacity = 1.0f; + schema._metallic = 0.1f; + schema._roughness = 0.9f; + + schemaKey.setAlbedo(true); + schemaKey.setTranslucentFactor(false); + schemaKey.setMetallic(true); + schemaKey.setGlossy(true); + schema._key = (uint32_t)schemaKey._flags.to_ulong(); + + auto schemaSize = sizeof(graphics::MultiMaterial::Schema); + schemaBuffer = gpu::BufferView(std::make_shared(schemaSize, (const gpu::Byte*) &schema, schemaSize)); + }); + + batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer); } } @@ -364,103 +373,400 @@ void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state, con gpu::Shader::createProgram(deformed_model_shadow_fade_dq), state, extraBatchSetter, itemSetter); } -// FIXME find a better way to setup the default textures -void RenderPipelines::bindMaterial(const graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures) { - if (!material) { +void RenderPipelines::bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures) { + graphics::MultiMaterial multiMaterial; + multiMaterial.push(graphics::MaterialLayer(material, 0)); + bindMaterials(multiMaterial, batch, enableTextures); +} + +void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial) { + auto& schemaBuffer = multiMaterial.getSchemaBuffer(); + + if (multiMaterial.size() == 0) { + schemaBuffer.edit() = graphics::MultiMaterial::Schema(); return; } + auto& drawMaterialTextures = multiMaterial.getTextureTable(); + multiMaterial.setTexturesLoading(false); + + // The total list of things we need to look for + static std::set allFlags; + static std::once_flag once; + std::call_once(once, [] { + for (int i = 0; i < graphics::Material::NUM_TOTAL_FLAGS; i++) { + // The opacity mask/map are derived from the albedo map + if (i != graphics::MaterialKey::OPACITY_MASK_MAP_BIT && + i != graphics::MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT) { + allFlags.insert(i); + } + } + }); + + graphics::MultiMaterial materials = multiMaterial; + graphics::MultiMaterial::Schema schema; + graphics::MaterialKey schemaKey; + + std::set flagsToCheck = allFlags; + std::set flagsToSetDefault; + + while (!materials.empty()) { + auto material = materials.top().material; + if (!material) { + break; + } + materials.pop(); + + bool defaultFallthrough = material->getDefaultFallthrough(); + const auto& materialKey = material->getKey(); + const auto& textureMaps = material->getTextureMaps(); + + auto it = flagsToCheck.begin(); + while (it != flagsToCheck.end()) { + auto flag = *it; + bool fallthrough = defaultFallthrough || material->getPropertyFallthrough(flag); + + bool wasSet = false; + bool forceDefault = false; + switch (flag) { + case graphics::MaterialKey::EMISSIVE_VAL_BIT: + if (materialKey.isEmissive()) { + schema._emissive = material->getEmissive(false); + schemaKey.setEmissive(true); + wasSet = true; + } + break; + case graphics::MaterialKey::UNLIT_VAL_BIT: + if (materialKey.isUnlit()) { + schemaKey.setUnlit(true); + wasSet = true; + } + break; + case graphics::MaterialKey::ALBEDO_VAL_BIT: + if (materialKey.isAlbedo()) { + schema._albedo = material->getAlbedo(false); + schemaKey.setAlbedo(true); + wasSet = true; + } + break; + case graphics::MaterialKey::METALLIC_VAL_BIT: + if (materialKey.isMetallic()) { + schema._metallic = material->getMetallic(); + schemaKey.setMetallic(true); + wasSet = true; + } + break; + case graphics::MaterialKey::GLOSSY_VAL_BIT: + if (materialKey.isRough() || materialKey.isGlossy()) { + schema._roughness = material->getRoughness(); + schemaKey.setGlossy(materialKey.isGlossy()); + wasSet = true; + } + break; + case graphics::MaterialKey::OPACITY_VAL_BIT: + if (materialKey.isTranslucentFactor()) { + schema._opacity = material->getOpacity(); + schemaKey.setTranslucentFactor(true); + wasSet = true; + } + break; + case graphics::MaterialKey::SCATTERING_VAL_BIT: + if (materialKey.isScattering()) { + schema._scattering = material->getScattering(); + schemaKey.setScattering(true); + wasSet = true; + } + break; + case graphics::MaterialKey::ALBEDO_MAP_BIT: + if (materialKey.isAlbedoMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setAlbedoMap(true); + schemaKey.setOpacityMaskMap(materialKey.isOpacityMaskMap()); + schemaKey.setTranslucentMap(materialKey.isTranslucentMap()); + } + break; + case graphics::MaterialKey::METALLIC_MAP_BIT: + if (materialKey.isMetallicMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, itr->second->getTextureView()); + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setMetallicMap(true); + } + break; + case graphics::MaterialKey::ROUGHNESS_MAP_BIT: + if (materialKey.isRoughnessMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, itr->second->getTextureView()); + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setRoughnessMap(true); + } + break; + case graphics::MaterialKey::NORMAL_MAP_BIT: + if (materialKey.isNormalMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView()); + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setNormalMap(true); + } + break; + case graphics::MaterialKey::OCCLUSION_MAP_BIT: + if (materialKey.isOcclusionMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, itr->second->getTextureView()); + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setOcclusionMap(true); + } + break; + case graphics::MaterialKey::SCATTERING_MAP_BIT: + if (materialKey.isScatteringMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, itr->second->getTextureView()); + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setScattering(true); + } + break; + case graphics::MaterialKey::EMISSIVE_MAP_BIT: + // Lightmap takes precendence over emissive map for legacy reasons + if (materialKey.isEmissiveMap() && !materialKey.isLightmapMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setEmissiveMap(true); + } else if (materialKey.isLightmapMap()) { + // We'll set this later when we check the lightmap + wasSet = true; + } + break; + case graphics::MaterialKey::LIGHTMAP_MAP_BIT: + if (materialKey.isLightmapMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setLightmapMap(true); + } + break; + case graphics::Material::TEXCOORDTRANSFORM0: + if (!fallthrough) { + schema._texcoordTransforms[0] = material->getTexCoordTransform(0); + wasSet = true; + } + break; + case graphics::Material::TEXCOORDTRANSFORM1: + if (!fallthrough) { + schema._texcoordTransforms[1] = material->getTexCoordTransform(1); + wasSet = true; + } + break; + case graphics::Material::LIGHTMAP_PARAMS: + if (!fallthrough) { + schema._lightmapParams = material->getLightmapParams(); + wasSet = true; + } + break; + case graphics::Material::MATERIAL_PARAMS: + if (!fallthrough) { + schema._materialParams = material->getMaterialParams(); + wasSet = true; + } + break; + default: + break; + } + + if (wasSet) { + flagsToCheck.erase(it++); + } else if (forceDefault || !fallthrough) { + flagsToSetDefault.insert(flag); + flagsToCheck.erase(it++); + } else { + ++it; + } + } + + if (flagsToCheck.empty()) { + break; + } + } + + for (auto flagBit : flagsToCheck) { + flagsToSetDefault.insert(flagBit); + } + + auto textureCache = DependencyManager::get(); + // Handle defaults + for (auto flag : flagsToSetDefault) { + switch (flag) { + case graphics::MaterialKey::EMISSIVE_VAL_BIT: + case graphics::MaterialKey::UNLIT_VAL_BIT: + case graphics::MaterialKey::ALBEDO_VAL_BIT: + case graphics::MaterialKey::METALLIC_VAL_BIT: + case graphics::MaterialKey::GLOSSY_VAL_BIT: + case graphics::MaterialKey::OPACITY_VAL_BIT: + case graphics::MaterialKey::SCATTERING_VAL_BIT: + case graphics::Material::TEXCOORDTRANSFORM0: + case graphics::Material::TEXCOORDTRANSFORM1: + case graphics::Material::LIGHTMAP_PARAMS: + case graphics::Material::MATERIAL_PARAMS: + // these are initialized to the correct default values in Schema() + break; + case graphics::MaterialKey::ALBEDO_MAP_BIT: + if (schemaKey.isAlbedoMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); + } + break; + case graphics::MaterialKey::METALLIC_MAP_BIT: + if (schemaKey.isMetallicMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, textureCache->getBlackTexture()); + } + break; + case graphics::MaterialKey::ROUGHNESS_MAP_BIT: + if (schemaKey.isRoughnessMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, textureCache->getWhiteTexture()); + } + break; + case graphics::MaterialKey::NORMAL_MAP_BIT: + if (schemaKey.isNormalMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, textureCache->getBlueTexture()); + } + break; + case graphics::MaterialKey::OCCLUSION_MAP_BIT: + if (schemaKey.isOcclusionMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, textureCache->getWhiteTexture()); + } + break; + case graphics::MaterialKey::SCATTERING_MAP_BIT: + if (schemaKey.isScatteringMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, textureCache->getWhiteTexture()); + } + break; + case graphics::MaterialKey::EMISSIVE_MAP_BIT: + if (schemaKey.isEmissiveMap() && !schemaKey.isLightmapMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + } + break; + case graphics::MaterialKey::LIGHTMAP_MAP_BIT: + if (schemaKey.isLightmapMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); + } + break; + default: + break; + } + } + + schema._key = (uint32_t)schemaKey._flags.to_ulong(); + schemaBuffer.edit() = schema; + multiMaterial.setNeedsUpdate(false); +} + +void RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures) { + if (multiMaterial.size() == 0) { + return; + } + + if (multiMaterial.needsUpdate() || multiMaterial.areTexturesLoading()) { + updateMultiMaterial(multiMaterial); + } + auto textureCache = DependencyManager::get(); - batch.setUniformBuffer(gr::Buffer::Material, material->getSchemaBuffer()); + static gpu::TextureTablePointer defaultMaterialTextures = std::make_shared(); + static std::once_flag once; + std::call_once(once, [textureCache] { + defaultMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); + defaultMaterialTextures->setTexture(gr::Texture::MaterialMetallic, textureCache->getBlackTexture()); + defaultMaterialTextures->setTexture(gr::Texture::MaterialRoughness, textureCache->getWhiteTexture()); + defaultMaterialTextures->setTexture(gr::Texture::MaterialNormal, textureCache->getBlueTexture()); + defaultMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, textureCache->getWhiteTexture()); + defaultMaterialTextures->setTexture(gr::Texture::MaterialScattering, textureCache->getWhiteTexture()); + // MaterialEmissiveLightmap has to be set later + }); - const auto& materialKey = material->getKey(); - const auto& textureMaps = material->getTextureMaps(); - - int numUnlit = 0; - if (materialKey.isUnlit()) { - numUnlit++; - } - - const auto& drawMaterialTextures = material->getTextureTable(); - - // Albedo - if (materialKey.isAlbedoMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP); - if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); - } else { - drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); + auto& schemaBuffer = multiMaterial.getSchemaBuffer(); + batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer); + if (enableTextures) { + batch.setResourceTextureTable(multiMaterial.getTextureTable()); + } else { + auto key = multiMaterial.getMaterialKey(); + if (key.isLightmapMap()) { + defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); + } else if (key.isEmissiveMap()) { + defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); } + batch.setResourceTextureTable(defaultMaterialTextures); } - - // Roughness map - if (materialKey.isRoughnessMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP); - if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, itr->second->getTextureView()); - } else { - drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, textureCache->getWhiteTexture()); - } - } - - // Normal map - if (materialKey.isNormalMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); - if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView()); - } else { - drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, textureCache->getBlueTexture()); - } - } - - // Metallic map - if (materialKey.isMetallicMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP); - if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, itr->second->getTextureView()); - } else { - drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, textureCache->getBlackTexture()); - } - } - - // Occlusion map - if (materialKey.isOcclusionMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP); - if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, itr->second->getTextureView()); - } else { - drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, textureCache->getWhiteTexture()); - } - } - - // Scattering map - if (materialKey.isScatteringMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP); - if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, itr->second->getTextureView()); - } else { - drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, textureCache->getWhiteTexture()); - } - } - - // Emissive / Lightmap - if (materialKey.isLightmapMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP); - - if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); - } else { - drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); - } - } else if (materialKey.isEmissiveMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); - if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); - } else { - drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); - } - } - - batch.setResourceTextureTable(material->getTextureTable()); } diff --git a/libraries/render-utils/src/RenderPipelines.h b/libraries/render-utils/src/RenderPipelines.h index b7d22bc72d..0f3d1160ef 100644 --- a/libraries/render-utils/src/RenderPipelines.h +++ b/libraries/render-utils/src/RenderPipelines.h @@ -15,7 +15,9 @@ class RenderPipelines { public: - static void bindMaterial(const graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures); + static void bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures); + static void updateMultiMaterial(graphics::MultiMaterial& multiMaterial); + static void bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures); };