diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index a705b61cd3..4dd6caf55b 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -228,7 +228,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { gpu::Batch& batch = *args->_batch; - std::shared_ptr<graphics::Material> mat; + graphics::MultiMaterial materials; auto geometryCache = DependencyManager::get<GeometryCache>(); GeometryCache::Shape geometryShape; bool proceduralRender = false; @@ -236,9 +236,11 @@ 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()); + materials = _materials["0"]; + auto topMat = materials.top().material; + if (topMat) { + // FIXME: fallthrough to get proper albedo and opacity? + outColor = glm::vec4(topMat->getAlbedo(), topMat->getOpacity()); if (_procedural.isReady()) { outColor = _procedural.getColor(outColor); outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f; @@ -248,10 +250,6 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { } }); - if (!mat) { - return; - } - if (proceduralRender) { if (render::ShapeKey(args->_globalShapeKey).isWireframe()) { geometryCache->renderWireShape(batch, geometryShape, outColor); @@ -269,7 +267,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++; } diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 104674eddc..78d34a9dee 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,7 @@ namespace scriptable { * @property {string} occlusionMap * @property {string} lightmapMap * @property {string} scatteringMap + * @property {boolean} defaultFallthrough */ class ScriptableMaterial { public: @@ -88,6 +89,9 @@ namespace scriptable { QString occlusionMap; QString lightmapMap; QString scatteringMap; + + bool defaultFallthrough; + std::unordered_map<graphics::MaterialKey::FlagBit, bool> 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..f32c4f2e01 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -362,25 +362,44 @@ 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.metallicMap.isEmpty()) { + obj.setProperty("metallicMap", material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT) ? FALLTHROUGH : material.metallicMap); + } else { + obj.setProperty("specularMap", material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT) ? FALLTHROUGH : material.specularMap); + } + + if (!material.roughnessMap.isEmpty()) { + obj.setProperty("roughnessMap", material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT) ? FALLTHROUGH : material.roughnessMap); + } else { + obj.setProperty("glossMap", material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT) ? FALLTHROUGH : material.glossMap); + } + + if (!material.normalMap.isEmpty()) { + obj.setProperty("normalMap", material.propertyFallthroughs.at(graphics::MaterialKey::NORMAL_MAP_BIT) ? FALLTHROUGH : material.normalMap); + } else { + obj.setProperty("bumpMap", material.propertyFallthroughs.at(graphics::MaterialKey::NORMAL_MAP_BIT) ? FALLTHROUGH : material.bumpMap); + } + + 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..b6b759ec86 100755 --- a/libraries/graphics/src/graphics/Material.cpp +++ b/libraries/graphics/src/graphics/Material.cpp @@ -17,125 +17,126 @@ 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<gpu::Buffer>(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 < graphics::MaterialKey::NUM_FLAGS; i++) { + _propertyFallthroughs[graphics::MaterialKey::FlagBit(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<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema))); - _schemaBuffer.edit<Schema>() = material._schemaBuffer.get<Schema>(); } -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<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema))); - _schemaBuffer.edit<Schema>() = material._schemaBuffer.get<Schema>(); + _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<Schema>()._key = (uint32) _key._flags.to_ulong(); - _schemaBuffer.edit<Schema>()._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<Schema>()._key = (uint32)_key._flags.to_ulong(); - _schemaBuffer.edit<Schema>()._opacity = opacity; + _opacity = opacity; } void Material::setUnlit(bool value) { _key.setUnlit(value); - _schemaBuffer.edit<Schema>()._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<Schema>()._key = (uint32)_key._flags.to_ulong(); - _schemaBuffer.edit<Schema>()._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<Schema>()._key = (uint32)_key._flags.to_ulong(); - _schemaBuffer.edit<Schema>()._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<Schema>()._key = (uint32)_key._flags.to_ulong(); - _schemaBuffer.edit<Schema>()._metallic = metallic; + _metallic = metallic; } void Material::setScattering(float scattering) { scattering = glm::clamp(scattering, 0.0f, 1.0f); _key.setMetallic(scattering > 0.0f); - _schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong(); - _schemaBuffer.edit<Schema>()._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<Schema>()._key = (uint32)_key._flags.to_ulong(); - if (channel == MaterialKey::ALBEDO_MAP) { resetOpacityMap(); - - // update the texcoord0 with albedo - _schemaBuffer.edit<Schema>()._texcoordTransforms[0] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4()); + _texcoordTransforms[0] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4()); } if (channel == MaterialKey::OCCLUSION_MAP) { - _schemaBuffer.edit<Schema>()._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<Schema>()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4()); - _schemaBuffer.edit<Schema>()._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<Schema>()._materialParams = (textureMap ? glm::vec2(textureMap->getMappingMode(), textureMap->getRepeat()) : glm::vec2(MaterialMappingMode::UV, 1.0)); - - _schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong(); + _materialParams = (textureMap ? glm::vec2(textureMap->getMappingMode(), textureMap->getRepeat()) : glm::vec2(MaterialMappingMode::UV, 1.0)); } @@ -163,11 +164,8 @@ void Material::resetOpacityMap() const { } } } - - _schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong(); } - const TextureMapPointer Material::getTextureMap(MapChannel channel) const { QMutexLocker locker(&_textureMapsMutex); @@ -179,7 +177,6 @@ const TextureMapPointer Material::getTextureMap(MapChannel channel) const { } } - bool Material::calculateMaterialInfo() const { if (!_hasCalculatedTextureInfo) { QMutexLocker locker(&_textureMapsMutex); @@ -222,7 +219,12 @@ void Material::setTextureTransforms(const Transform& transform, MaterialMappingM } } for (int i = 0; i < NUM_TEXCOORD_TRANSFORMS; i++) { - _schemaBuffer.edit<Schema>()._texcoordTransforms[i] = transform.getMatrix(); + _texcoordTransforms[i] = transform.getMatrix(); } - _schemaBuffer.edit<Schema>()._materialParams = glm::vec2(mode, repeat); + _materialParams = glm::vec2(mode, repeat); } + +MultiMaterial::MultiMaterial() { + Schema schema; + _schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema))); +} \ No newline at end of file diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index 9711bd9000..91453bb259 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -14,7 +14,7 @@ #include <QMutex> #include <bitset> -#include <map> +#include <unordered_map> #include <queue> #include <ColorUtils.h> @@ -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<MapChannel, TextureMapPointer> TextureMaps; - typedef std::bitset<MaterialKey::NUM_MAP_CHANNELS> MapFlags; + typedef std::unordered_map<MapChannel, TextureMapPointer> TextureMaps; 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<Schema>()._emissive) : _schemaBuffer.get<Schema>()._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<Schema>()._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<Schema>()._albedo) : _schemaBuffer.get<Schema>()._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<Schema>()._metallic; } + float getMetallic() const { return _metallic; } + static const float DEFAULT_ROUGHNESS; void setRoughness(float roughness); - float getRoughness() const { return _schemaBuffer.get<Schema>()._roughness; } + float getRoughness() const { return _roughness; } + static const float DEFAULT_SCATTERING; void setScattering(float scattering); - float getScattering() const { return _schemaBuffer.get<Schema>()._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; @@ -366,18 +326,34 @@ public: const std::string& getModel() const { return _model; } void setModel(const std::string& model) { _model = model; } - const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; } + bool getDefaultFallthrough() const { return _defaultFallthrough; } + void setDefaultFallthrough(bool defaultFallthrough) { _defaultFallthrough = defaultFallthrough; } + + std::unordered_map<MaterialKey::FlagBit, bool> getPropertyFallthroughs() { return _propertyFallthroughs; } + bool getPropertyFallthrough(MaterialKey::FlagBit property) { return _propertyFallthroughs[property]; } + void setPropertyDoesFallthrough(MaterialKey::FlagBit property) { _propertyFallthroughs[property] = true; } protected: std::string _name { "" }; private: - mutable MaterialKey _key; - mutable UniformBufferView _schemaBuffer; - mutable gpu::TextureTablePointer _textureTable{ std::make_shared<gpu::TextureTable>() }; + 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<glm::mat4, NUM_TEXCOORD_TRANSFORMS> _texcoordTransforms; + glm::vec2 _lightmapParams { 0.0, 1.0 }; + glm::vec2 _materialParams { 0.0, 1.0 }; TextureMaps _textureMaps; + bool _defaultFallthrough { false }; + std::unordered_map<MaterialKey::FlagBit, bool> _propertyFallthroughs; + mutable QMutex _textureMapsMutex { QMutex::Recursive }; mutable size_t _textureSize { 0 }; mutable int _textureCount { 0 }; @@ -385,7 +361,6 @@ private: bool calculateMaterialInfo() const; std::string _model { "hifi_pbr" }; - }; typedef std::shared_ptr< Material > MaterialPointer; @@ -406,6 +381,8 @@ public: class MultiMaterial : public std::priority_queue<MaterialLayer, std::vector<MaterialLayer>, MaterialLayerCompare> { public: + MultiMaterial(); + bool remove(const MaterialPointer& value) { auto it = c.begin(); while (it != c.end()) { @@ -422,6 +399,49 @@ public: 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() {} + }; + + gpu::BufferView& getSchemaBuffer() { return _schemaBuffer; } + const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; } + +private: + gpu::BufferView _schemaBuffer; + gpu::TextureTablePointer _textureTable { std::make_shared<gpu::TextureTable>() }; }; }; diff --git a/libraries/model-networking/src/model-networking/MaterialCache.cpp b/libraries/model-networking/src/model-networking/MaterialCache.cpp index e6e3b0e812..ecaaf62fa7 100644 --- a/libraries/model-networking/src/model-networking/MaterialCache.cpp +++ b/libraries/model-networking/src/model-networking/MaterialCache.cpp @@ -111,146 +111,253 @@ 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" - <em>Currently not used.</em> - * @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, <code>0.0</code> – <code>1.0</code>. - * @property {boolean} unlit=false - If <code>true</code>, 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, <code>0.0</code> – <code>1.0</code>. - * @property {number} metallic - The metallicness, <code>0.0</code> – <code>1.0</code>. - * @property {number} scattering - The scattering, <code>0.0</code> – <code>1.0</code>. - * @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 <code>"fallthrough"</code> to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {number|string} opacity=1.0 - The opacity, <code>0.0</code> – <code>1.0</code>. Set to <code>"fallthrough"</code> to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {boolean|string} unlit=false - If <code>true</code>, the material is not lit. Set to <code>"fallthrough"</code> 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 <code>"fallthrough"</code> to fallthrough to the material below. Set to <code>"fallthrough"</code> to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {number|string} roughness - The roughness, <code>0.0</code> – <code>1.0</code>. Set to <code>"fallthrough"</code> to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {number|string} metallic - The metallicness, <code>0.0</code> – <code>1.0</code>. Set to <code>"fallthrough"</code> to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {number|string} scattering - The scattering, <code>0.0</code> – <code>1.0</code>. Set to <code>"fallthrough"</code> to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {string} emissiveMap - URL of emissive texture image. Set to <code>"fallthrough"</code> to fallthrough to + * the material below. "hifi_pbr" model only. + * @property {string} albedoMap - URL of albedo texture image. Set to <code>"fallthrough"</code> to fallthrough to + * the material below. "hifi_pbr" model only. * @property {string} opacityMap - URL of opacity texture image. Set value the same as the <code>albedoMap</code> value for - * transparency. - * @property {string} roughnessMap - URL of roughness texture image. Can use this or <code>glossMap</code>, but not both. - * @property {string} glossMap - URL of gloss texture image. Can use this or <code>roughnessMap</code>, but not both. - * @property {string} metallicMap - URL of metallic texture image. Can use this or <code>specularMap</code>, but not both. - * @property {string} specularMap - URL of specular texture image. Can use this or <code>metallicMap</code>, but not both. - * @property {string} normalMap - URL of normal texture image. Can use this or <code>bumpMap</code>, but not both. - * @property {string} bumpMap - URL of bump texture image. Can use this or <code>normalMap</code>, 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 <code>glossMap</code>, but not both. Set to <code>"fallthrough"</code> + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} glossMap - URL of gloss texture image. Can use this or <code>roughnessMap</code>, but not both. Set to <code>"fallthrough"</code> + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} metallicMap - URL of metallic texture image. Can use this or <code>specularMap</code>, but not both. Set to <code>"fallthrough"</code> + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} specularMap - URL of specular texture image. Can use this or <code>metallicMap</code>, but not both. Set to <code>"fallthrough"</code> + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} normalMap - URL of normal texture image. Can use this or <code>bumpMap</code>, but not both. Set to <code>"fallthrough"</code> + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} bumpMap - URL of bump texture image. Can use this or <code>normalMap</code>, but not both. Set to <code>"fallthrough"</code> + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} occlusionMap - URL of occlusion texture image. Set to <code>"fallthrough"</code> to fallthrough to the material below. "hifi_pbr" model only. * @property {string} scatteringMap - URL of scattering texture image. Only used if <code>normalMap</code> or - * <code>bumpMap</code> is specified. - * @property {string} lightMap - URL of light map texture image. <em>Currently not used.</em> + * <code>bumpMap</code> is specified. Set to <code>"fallthrough"</code> to fallthrough to the material below. "hifi_pbr" model only. + * @property {string} lightMap - URL of light map texture image. <em>Currently not used.</em>. Set to <code>"fallthrough"</code> + * to fallthrough to the material below. "hifi_pbr" model only. + * @property {bool} defaultFallthrough=false - If <code>true</code>, all properties will fallthrough to the material below unless they are set. If + * <code>false</code>, they will respect the individual properties' fallthrough state. "hifi_pbr" model only. */ // Note: See MaterialEntityItem.h for default values used in practice. std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl) { std::string name = ""; std::shared_ptr<NetworkMaterial> material = std::make_shared<NetworkMaterial>(); - 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; + std::string modelString = "hifi_pbr"; + auto modelJSON = materialJSON.value("model"); + if (modelJSON.isString()) { + modelString = modelJSON.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 == "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 ca2e56862d..489fc5c331 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<graphics::Material>(); - MeshPartPayload::MeshPartPayload(const std::shared_ptr<const graphics::Mesh>& mesh, int partIndex, graphics::MaterialPointer material) { updateMeshPart(mesh, partIndex); addMaterial(graphics::MaterialLayer(material, 0)); @@ -158,7 +156,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++; } @@ -434,7 +432,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 29c0091f11..03145c981b 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -74,7 +74,6 @@ public: 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; } diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index cd685a54a1..1af47b4321 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<TextureCache>()->getWhiteTexture()); + batch.setResourceTexture(gr::Texture::MaterialAlbedo, DependencyManager::get<TextureCache>()->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<gpu::Buffer>(schemaSize, (const gpu::Byte*) &schema, schemaSize)); + }); + + batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer); } } @@ -364,103 +373,322 @@ void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state, con gpu::Shader::createProgram(deformed_model_shadow_fade_dq), state, extraBatchSetter, itemSetter); } +void RenderPipelines::bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures) { + graphics::MultiMaterial multiMaterial; + multiMaterial.push(graphics::MaterialLayer(material, 0)); + bindMaterials(multiMaterial, batch, enableTextures); +} + // 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::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures) { + if (multiMaterial.size() == 0) { return; } auto textureCache = DependencyManager::get<TextureCache>(); + auto& drawMaterialTextures = multiMaterial.getTextureTable(); + auto& schemaBuffer = multiMaterial.getSchemaBuffer(); - batch.setUniformBuffer(gr::Buffer::Material, material->getSchemaBuffer()); + // The total list of things we need to look for + static std::set<graphics::MaterialKey::FlagBit> allFlagBits; + static std::once_flag once; + std::call_once(once, [] { + for (int i = 0; i < graphics::MaterialKey::NUM_FLAGS; i++) { + auto flagBit = graphics::MaterialKey::FlagBit(i); + // The opacity mask/map are derived from the albedo map + if (flagBit != graphics::MaterialKey::OPACITY_MASK_MAP_BIT && + flagBit != graphics::MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT) { + allFlagBits.insert(flagBit); + } + } + }); - const auto& materialKey = material->getKey(); - const auto& textureMaps = material->getTextureMaps(); + graphics::MultiMaterial materials = multiMaterial; + graphics::MultiMaterial::Schema schema; + graphics::MaterialKey schemaKey; - int numUnlit = 0; - if (materialKey.isUnlit()) { - numUnlit++; + std::set<graphics::MaterialKey::FlagBit> flagBitsToCheck = allFlagBits; + std::set<graphics::MaterialKey::FlagBit> flagBitsToSetDefault; + + auto material = materials.top().material; + while (material) { + bool defaultFallthrough = material->getDefaultFallthrough(); + const auto& materialKey = material->getKey(); + const auto& textureMaps = material->getTextureMaps(); + + auto it = flagBitsToCheck.begin(); + while (it != flagBitsToCheck.end()) { + auto flagBit = *it; + bool wasSet = false; + bool forceDefault = false; + switch (flagBit) { + 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()) { + if (!enableTextures) { + forceDefault = true; + } else { + auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); + wasSet = 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()) { + if (!enableTextures) { + forceDefault = true; + } else { + auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, itr->second->getTextureView()); + wasSet = true; + } else { + forceDefault = true; + } + } + schemaKey.setMetallicMap(true); + } + break; + case graphics::MaterialKey::ROUGHNESS_MAP_BIT: + if (materialKey.isRoughnessMap()) { + if (!enableTextures) { + forceDefault = true; + } else { + auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, itr->second->getTextureView()); + wasSet = true; + } else { + forceDefault = true; + } + } + schemaKey.setRoughnessMap(true); + } + break; + case graphics::MaterialKey::NORMAL_MAP_BIT: + if (materialKey.isNormalMap()) { + if (!enableTextures) { + forceDefault = true; + } else { + auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView()); + wasSet = true; + } else { + forceDefault = true; + } + } + schemaKey.setNormalMap(true); + } + break; + case graphics::MaterialKey::OCCLUSION_MAP_BIT: + if (materialKey.isOcclusionMap()) { + if (!enableTextures) { + forceDefault = true; + } else { + auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, itr->second->getTextureView()); + wasSet = true; + } else { + forceDefault = true; + } + } + schemaKey.setOcclusionMap(true); + } + break; + case graphics::MaterialKey::SCATTERING_MAP_BIT: + if (materialKey.isScatteringMap()) { + if (!enableTextures) { + forceDefault = true; + } else { + auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, itr->second->getTextureView()); + wasSet = 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()) { + if (!enableTextures) { + forceDefault = true; + } else { + auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + wasSet = 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()) { + if (!enableTextures) { + forceDefault = true; + } else { + auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP); + if (itr != textureMaps.end() && itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + wasSet = true; + } else { + forceDefault = true; + } + } + schemaKey.setLightmapMap(true); + } + break; + default: + break; + } + + if (wasSet) { + flagBitsToCheck.erase(it++); + } else if (forceDefault || !defaultFallthrough || !material->getPropertyFallthrough(flagBit)) { + flagBitsToSetDefault.insert(flagBit); + flagBitsToCheck.erase(it++); + } else { + ++it; + } + } + + if (flagBitsToCheck.empty()) { + break; + } + + materials.pop(); + material = materials.top().material; } - const auto& drawMaterialTextures = material->getTextureTable(); + for (auto flagBit : flagBitsToCheck) { + flagBitsToSetDefault.insert(flagBit); + } - // 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()); + // Handle defaults + for (auto flagBit : flagBitsToSetDefault) { + switch (flagBit) { + 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: + // 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; } } - // 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()); + schema._key = (uint32_t)schemaKey._flags.to_ulong(); + schemaBuffer.edit<graphics::MultiMaterial::Schema>() = schema; + batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer); + batch.setResourceTextureTable(drawMaterialTextures); } diff --git a/libraries/render-utils/src/RenderPipelines.h b/libraries/render-utils/src/RenderPipelines.h index b7d22bc72d..49abe719c8 100644 --- a/libraries/render-utils/src/RenderPipelines.h +++ b/libraries/render-utils/src/RenderPipelines.h @@ -15,7 +15,8 @@ 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 bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures); };