diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 212baa6634..db8a61312a 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -4,6 +4,7 @@ // // Created by Brad Hefta-Gaub on 12/6/13. // Copyright 2013 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -196,6 +197,8 @@ ItemKey EntityRenderer::getKey() { builder.withInvisible(); } + updateItemKeyBuilderFromMaterials(builder); + return builder; } @@ -221,6 +224,20 @@ bool EntityRenderer::passesZoneOcclusionTest(const std::unordered_set& co return true; } +HighlightStyle EntityRenderer::getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const { + std::lock_guard lock(_materialsLock); + auto materials = _materials.find("0"); + if (materials != _materials.end()) { + glm::vec3 position; + withReadLock([&] { + position = _renderTransform.getTranslation(); + }); + return HighlightStyle::calculateOutlineStyle(materials->second.getOutlineWidthMode(), materials->second.getOutlineWidth(), + materials->second.getOutline(), position, viewFrustum, height); + } + return HighlightStyle(); +} + void EntityRenderer::render(RenderArgs* args) { if (!isValidRenderItem()) { return; @@ -510,7 +527,7 @@ EntityRenderer::Pipeline EntityRenderer::getPipelineType(const graphics::MultiMa } graphics::MaterialKey drawMaterialKey = materials.getMaterialKey(); - if (drawMaterialKey.isEmissive() || drawMaterialKey.isMetallic() || drawMaterialKey.isScattering()) { + if (materials.isMToon() || drawMaterialKey.isEmissive() || drawMaterialKey.isMetallic() || drawMaterialKey.isScattering()) { return Pipeline::MATERIAL; } @@ -630,6 +647,26 @@ Item::Bound EntityRenderer::getMaterialBound(RenderArgs* args) { return EntityRenderer::getBound(args); } +void EntityRenderer::updateItemKeyBuilderFromMaterials(ItemKey::Builder& builder) { + MaterialMap::iterator materials; + { + std::lock_guard lock(_materialsLock); + materials = _materials.find("0"); + + if (materials != _materials.end()) { + if (materials->second.shouldUpdate()) { + RenderPipelines::updateMultiMaterial(materials->second); + } + } else { + return; + } + } + + if (materials->second.hasOutline()) { + builder.withOutline(); + } +} + void EntityRenderer::updateShapeKeyBuilderFromMaterials(ShapeKey::Builder& builder) { MaterialMap::iterator materials; { @@ -656,7 +693,7 @@ void EntityRenderer::updateShapeKeyBuilderFromMaterials(ShapeKey::Builder& build builder.withCullFaceMode(materials->second.getCullFaceMode()); graphics::MaterialKey drawMaterialKey = materials->second.getMaterialKey(); - if (drawMaterialKey.isUnlit()) { + if (!materials->second.isMToon() && drawMaterialKey.isUnlit()) { builder.withUnlit(); } @@ -666,8 +703,12 @@ void EntityRenderer::updateShapeKeyBuilderFromMaterials(ShapeKey::Builder& build if (drawMaterialKey.isNormalMap()) { builder.withTangents(); } - if (drawMaterialKey.isLightMap()) { - builder.withLightMap(); + if (!materials->second.isMToon()) { + if (drawMaterialKey.isLightMap()) { + builder.withLightMap(); + } + } else { + builder.withMToon(); } } else if (pipelineType == Pipeline::PROCEDURAL) { builder.withOwnPipeline(); diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 3caeef0713..86ef9dfb54 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -4,6 +4,7 @@ // // Created by Brad Hefta-Gaub on 12/6/13. // Copyright 2013 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -13,6 +14,8 @@ #define hifi_RenderableEntityItem_h #include +#include + #include #include #include "AbstractViewStateInterface.h" @@ -74,6 +77,7 @@ public: virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override; virtual Item::Bound getBound(RenderArgs* args) override; bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const override; + virtual HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const override; protected: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } @@ -131,6 +135,7 @@ protected: void updateMaterials(bool baseMaterialChanged = false); bool materialsTransparent() const; Item::Bound getMaterialBound(RenderArgs* args); + void updateItemKeyBuilderFromMaterials(ItemKey::Builder& builder); void updateShapeKeyBuilderFromMaterials(ShapeKey::Builder& builder); Item::Bound _bound; diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index 9592a3e57f..13ddcffe6f 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -130,8 +130,7 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { materials = _materials["0"]; } - auto& schema = materials.getSchemaBuffer().get(); - glm::vec4 color = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity); + glm::vec4 color = materials.getColor(); color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created); if (!_texture || !_texture->isLoaded() || color.a == 0.0f) { diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index b8f829f4ba..b086f42d72 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -1,6 +1,7 @@ // // Created by Sam Gondelman on 1/18/2018 // Copyright 2018 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -219,7 +220,7 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo ItemKey MaterialEntityRenderer::getKey() { auto builder = ItemKey::Builder().withTypeShape().withTagBits(getTagMask()).withLayer(getHifiRenderLayer()); - if (!_visible) { + if (!_visible || !_parentID.isNull()) { builder.withInvisible(); } @@ -229,6 +230,10 @@ ItemKey MaterialEntityRenderer::getKey() { if (matKey.isTranslucent()) { builder.withTransparent(); } + + if (drawMaterial->getOutlineWidthMode() != NetworkMToonMaterial::OutlineWidthMode::OUTLINE_NONE && drawMaterial->getOutlineWidth() > 0.0f) { + builder.withOutline(); + } } return builder.build(); @@ -258,11 +263,16 @@ ShapeKey MaterialEntityRenderer::getShapeKey() { if (drawMaterialKey.isNormalMap()) { builder.withTangents(); } - if (drawMaterialKey.isLightMap()) { - builder.withLightMap(); - } - if (drawMaterialKey.isUnlit()) { - builder.withUnlit(); + + if (drawMaterial && drawMaterial->isMToon()) { + builder.withMToon(); + } else { + if (drawMaterialKey.isLightMap()) { + builder.withLightMap(); + } + if (drawMaterialKey.isUnlit()) { + builder.withUnlit(); + } } } @@ -273,6 +283,18 @@ ShapeKey MaterialEntityRenderer::getShapeKey() { return builder.build(); } +HighlightStyle MaterialEntityRenderer::getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const { + if (const auto drawMaterial = getMaterial()) { + glm::vec3 position; + withReadLock([&] { + position = _renderTransform.getTranslation(); + }); + return HighlightStyle::calculateOutlineStyle(drawMaterial->getOutlineWidthMode(), drawMaterial->getOutlineWidth(), + drawMaterial->getOutline(), position, viewFrustum, height); + } + return HighlightStyle(); +} + void MaterialEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("RenderableMaterialEntityItem::render"); Q_ASSERT(args->_batch); diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h index 25403e8a2b..ad1f9771a5 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h @@ -1,6 +1,7 @@ // // Created by Sam Gondelman on 1/18/2018 // Copyright 2018 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -32,6 +33,7 @@ private: virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; virtual void doRender(RenderArgs* args) override; + virtual HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const override; ItemKey getKey() override; ShapeKey getShapeKey() override; diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index a6fee03311..1355885625 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -99,8 +99,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { materials = _materials["0"]; } - auto& schema = materials.getSchemaBuffer().get(); - glm::vec4 outColor = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity); + glm::vec4 outColor = materials.getColor(); outColor = EntityRenderer::calculatePulseColor(outColor, _pulseProperties, _created); if (outColor.a == 0.0f) { @@ -178,7 +177,7 @@ scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel() { result.appendMaterials(_materials); auto materials = _materials.find("0"); if (materials != _materials.end()) { - vertexColor = ColorUtils::tosRGBVec3(materials->second.getSchemaBuffer().get()._albedo); + vertexColor = materials->second.getColor(); } } if (auto mesh = geometryCache->meshFromShape(geometryShape, vertexColor)) { diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 2858e12afd..5b790d6e60 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -147,8 +147,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { materials = _materials["0"]; } - auto& schema = materials.getSchemaBuffer().get(); - glm::vec4 backgroundColor = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity); + glm::vec4 backgroundColor = materials.getColor(); backgroundColor = EntityRenderer::calculatePulseColor(backgroundColor, _pulseProperties, _created); if (backgroundColor.a <= 0.0f) { diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 847937ba4f..d120e15892 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -82,13 +82,33 @@ namespace scriptable { QString procedural; + glm::vec3 shade; + QString shadeMap; + float shadingShift; + QString shadingShiftMap; + float shadingToony; + glm::vec3 matcap; + QString matcapMap; + glm::vec3 parametricRim; + float parametricRimFresnelPower; + float parametricRimLift; + QString rimMap; + float rimLightingMix; + QString outlineWidthMode; + float outlineWidth; + glm::vec3 outline; + QString uvAnimationMaskMap; + float uvAnimationScrollXSpeed; + float uvAnimationScrollYSpeed; + float uvAnimationRotationSpeed; + graphics::MaterialKey key { 0 }; }; /*@jsdoc * A material layer. * @typedef {object} Graphics.MaterialLayer - * @property {Graphics.Material} material - The layer's material. + * @property {Entities.Material} material - The layer's material. * @property {number} priority - The priority of the layer. If multiple materials are applied to a mesh part, only the * layer with the highest priority is applied, with materials of the same priority randomly assigned. */ diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 0dd5b95532..b44aa9149a 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -369,120 +369,6 @@ namespace scriptable { return true; } - /*@jsdoc - * A material in a {@link GraphicsModel}. - * @typedef {object} Graphics.Material - * @property {string} name - The name of the material. - * @property {string} model - Different material models support different properties and rendering modes. Supported models - * are: "hifi_pbr" and "hifi_shader_simple". - * @property {Vec3|string} [albedo] - The albedo color. Component values are in the range 0.0 – - * 1.0. - * If "fallthrough" then it falls through to the material below. - * @property {number|string} [opacity] - The opacity, range 0.01.0. - * If "fallthrough" then it falls through to the material below. - * - * @property {number|string} [opacityCutoff] - The opacity cutoff threshold used to determine the opaque texels of the - * opacityMap when opacityMapMode is "OPACITY_MAP_MASK". Range 0.0 - * – 1.0. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {number|string} [roughness] - The roughness, range 0.01.0. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {number|string} [metallic] - The metallicness, range 0.01.0. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {number|string} [scattering] - The scattering, range 0.01.0. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {boolean|string} [unlit] - true if the material is unaffected by lighting, false if it - * it is lit by the key light and local lights. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {Vec3|string} [emissive] - The emissive color, i.e., the color that the material emits. Component values are - * in the range 0.01.0. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [albedoMap] - The URL of the albedo texture image. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [opacityMap] - The URL of the opacity texture image. - * "hifi_pbr" model only. - * @property {string} [opacityMapMode] - The mode defining the interpretation of the opacity map. Values can be: - *
    - *
  • "OPACITY_MAP_OPAQUE" for ignoring the opacity map information.
  • - *
  • "OPACITY_MAP_MASK" for using the opacityMap as a mask, where only the texel greater - * than opacityCutoff are visible and rendered opaque.
  • - *
  • "OPACITY_MAP_BLEND" for using the opacityMap for alpha blending the material surface - * with the background.
  • - *
- * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [occlusionMap] - The URL of the occlusion texture image. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [lightMap] - The URL of the light map texture image. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [lightmapParams] - Parameters for controlling how lightMap is used. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - *

Currently not used.

- * @property {string} [scatteringMap] - The URL of the scattering texture image. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [emissiveMap] - The URL of the emissive texture image. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {string} [metallicMap] - The URL of the metallic texture image. - * If "fallthrough" then it and specularMap fall through to the material below. - * Only use one of metallicMap and specularMap. - * "hifi_pbr" model only. - * @property {string} [specularMap] - The URL of the specular texture image. - * Only use one of metallicMap and specularMap. - * "hifi_pbr" model only. - * @property {string} [roughnessMap] - The URL of the roughness texture image. - * If "fallthrough" then it and glossMap fall through to the material below. - * Only use one of roughnessMap and glossMap. - * "hifi_pbr" model only. - * @property {string} [glossMap] - The URL of the gloss texture image. - * Only use one of roughnessMap and glossMap. - * "hifi_pbr" model only. - * @property {string} [normalMap] - The URL of the normal texture image. - * If "fallthrough" then it and bumpMap fall through to the material below. - * Only use one of normalMap and bumpMap. - * "hifi_pbr" model only. - * @property {string} [bumpMap] - The URL of the bump texture image. - * Only use one of normalMap and bumpMap. - * "hifi_pbr" model only. - * @property {string} [materialParams] - Parameters for controlling the material projection and repetition. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - *

Currently not used.

- * @property {string} [cullFaceMode="CULL_BACK"] - Specifies Which faces of the geometry to render. Values can be: - *
    - *
  • "CULL_NONE" to render both sides of the geometry.
  • - *
  • "CULL_FRONT" to cull the front faces of the geometry.
  • - *
  • "CULL_BACK" (the default) to cull the back faces of the geometry.
  • - *
- * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {Mat4|string} [texCoordTransform0] - The transform to use for all of the maps apart from - * occlusionMap and lightMap. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * @property {Mat4|string} [texCoordTransform1] - The transform to use for occlusionMap and - * lightMap. - * If "fallthrough" then it falls through to the material below. - * "hifi_pbr" model only. - * - * @property {string} procedural - The definition of a procedural shader material. - * "hifi_shader_simple" model only. - *

Currently not used.

- * - * @property {boolean} defaultFallthrough - true if all properties fall through to the material below unless - * they are set, false if properties respect their individual fall-through settings. - */ ScriptValue scriptableMaterialToScriptValue(ScriptEngine* engine, const scriptable::ScriptableMaterial &material) { ScriptValue obj = engine->newObject(); obj.setProperty("name", material.name); @@ -503,7 +389,7 @@ namespace scriptable { obj.setProperty("albedo", vec3ColorToScriptValue(engine, material.albedo)); } - if (material.model.toStdString() == graphics::Material::HIFI_PBR) { + if (material.model.toStdString() == graphics::Material::HIFI_PBR || material.model.toStdString() == graphics::Material::VRM_MTOON) { if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_CUTOFF_VAL_BIT)) { obj.setProperty("opacityCutoff", FALLTHROUGH); } else if (material.key.isOpacityCutoff()) { @@ -516,30 +402,6 @@ namespace scriptable { obj.setProperty("opacityMapMode", material.opacityMapMode); } - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT)) { - obj.setProperty("roughness", FALLTHROUGH); - } else if (material.key.isGlossy()) { - obj.setProperty("roughness", material.roughness); - } - - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT)) { - obj.setProperty("metallic", FALLTHROUGH); - } else if (material.key.isMetallic()) { - obj.setProperty("metallic", material.metallic); - } - - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT)) { - obj.setProperty("scattering", FALLTHROUGH); - } else if (material.key.isScattering()) { - obj.setProperty("scattering", material.scattering); - } - - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT)) { - obj.setProperty("unlit", FALLTHROUGH); - } else if (material.key.isUnlit()) { - obj.setProperty("unlit", material.unlit); - } - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_VAL_BIT)) { obj.setProperty("emissive", FALLTHROUGH); } else if (material.key.isEmissive()) { @@ -562,41 +424,6 @@ namespace scriptable { obj.setProperty("opacityMap", material.opacityMap); } - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT)) { - obj.setProperty("occlusionMap", FALLTHROUGH); - } else if (!material.occlusionMap.isEmpty()) { - obj.setProperty("occlusionMap", material.occlusionMap); - } - - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::LIGHT_MAP_BIT)) { - obj.setProperty("lightMap", FALLTHROUGH); - } else if (!material.lightMap.isEmpty()) { - obj.setProperty("lightMap", material.lightMap); - } - - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT)) { - obj.setProperty("scatteringMap", FALLTHROUGH); - } else if (!material.scatteringMap.isEmpty()) { - obj.setProperty("scatteringMap", material.scatteringMap); - } - - // Only set one of each of these - if (hasPropertyFallthroughs && 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 (hasPropertyFallthroughs && 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 (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::NORMAL_MAP_BIT)) { obj.setProperty("normalMap", FALLTHROUGH); } else if (!material.normalMap.isEmpty()) { @@ -616,10 +443,7 @@ namespace scriptable { obj.setProperty("texCoordTransform1", mat4toScriptValue(engine, material.texCoordTransforms[1])); } - // These need to be implemented, but set the fallthrough for now - if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) { - obj.setProperty("lightmapParams", FALLTHROUGH); - } + // This needs to be implemented, but set the fallthrough for now if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::MATERIAL_PARAMS)) { obj.setProperty("materialParams", FALLTHROUGH); } @@ -629,6 +453,179 @@ namespace scriptable { } else if (!material.cullFaceMode.isEmpty()) { obj.setProperty("cullFaceMode", material.cullFaceMode); } + + if (material.model.toStdString() == graphics::Material::HIFI_PBR) { + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT)) { + obj.setProperty("roughness", FALLTHROUGH); + } else if (material.key.isGlossy()) { + obj.setProperty("roughness", material.roughness); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT)) { + obj.setProperty("metallic", FALLTHROUGH); + } else if (material.key.isMetallic()) { + obj.setProperty("metallic", material.metallic); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT)) { + obj.setProperty("scattering", FALLTHROUGH); + } else if (material.key.isScattering()) { + obj.setProperty("scattering", material.scattering); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT)) { + obj.setProperty("unlit", FALLTHROUGH); + } else if (material.key.isUnlit()) { + obj.setProperty("unlit", material.unlit); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT)) { + obj.setProperty("occlusionMap", FALLTHROUGH); + } else if (!material.occlusionMap.isEmpty()) { + obj.setProperty("occlusionMap", material.occlusionMap); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::LIGHT_MAP_BIT)) { + obj.setProperty("lightMap", FALLTHROUGH); + } else if (!material.lightMap.isEmpty()) { + obj.setProperty("lightMap", material.lightMap); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT)) { + obj.setProperty("scatteringMap", FALLTHROUGH); + } else if (!material.scatteringMap.isEmpty()) { + obj.setProperty("scatteringMap", material.scatteringMap); + } + + // Only set one of each of these + if (hasPropertyFallthroughs && 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 (hasPropertyFallthroughs && 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); + } + + // This needs to be implemented, but set the fallthrough for now + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) { + obj.setProperty("lightmapParams", FALLTHROUGH); + } + } else { + // See the mappings in ProceduralMatericalCache.h + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT)) { + obj.setProperty("shade", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::UNLIT_VAL_BIT]) { + obj.setProperty("shade", vec3ColorToScriptValue(engine, material.shade)); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT)) { + obj.setProperty("shadeMap", FALLTHROUGH); + } else if (!material.shadeMap.isEmpty()) { + obj.setProperty("shadeMap", material.shadeMap); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT)) { + obj.setProperty("shadingShift", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::METALLIC_VAL_BIT]) { + obj.setProperty("shadingShift", material.shadingShift); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT)) { + obj.setProperty("shadingShiftMap", FALLTHROUGH); + } else if (!material.shadingShiftMap.isEmpty()) { + obj.setProperty("shadingShiftMap", material.shadingShiftMap); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT)) { + obj.setProperty("shadingToony", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::GLOSSY_VAL_BIT]) { + obj.setProperty("shadingToony", material.shadingToony); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EXTRA_1_BIT)) { + obj.setProperty("matcap", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::EXTRA_1_BIT]) { + obj.setProperty("matcap", vec3ColorToScriptValue(engine, material.matcap)); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT)) { + obj.setProperty("matcapMap", FALLTHROUGH); + } else if (!material.matcapMap.isEmpty()) { + obj.setProperty("matcapMap", material.matcapMap); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EXTRA_2_BIT)) { + obj.setProperty("parametricRim", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::EXTRA_2_BIT]) { + obj.setProperty("parametricRim", vec3ColorToScriptValue(engine, material.parametricRim)); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EXTRA_3_BIT)) { + obj.setProperty("parametricRimFresnelPower", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::EXTRA_3_BIT]) { + obj.setProperty("parametricRimFresnelPower", material.parametricRimFresnelPower); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EXTRA_4_BIT)) { + obj.setProperty("parametricRimLift", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::EXTRA_4_BIT]) { + obj.setProperty("parametricRimLift", material.parametricRimLift); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT)) { + obj.setProperty("rimMap", FALLTHROUGH); + } else if (!material.rimMap.isEmpty()) { + obj.setProperty("rimMap", material.rimMap); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::EXTRA_5_BIT)) { + obj.setProperty("rimLightingMix", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::EXTRA_5_BIT]) { + obj.setProperty("rimLightingMix", material.rimLightingMix); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::LIGHT_MAP_BIT)) { + obj.setProperty("uvAnimationMaskMap", FALLTHROUGH); + } else if (!material.uvAnimationMaskMap.isEmpty()) { + obj.setProperty("uvAnimationMaskMap", material.uvAnimationMaskMap); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT)) { + obj.setProperty("uvAnimationScrollXSpeed", FALLTHROUGH); + obj.setProperty("uvAnimationScrollYSpeed", FALLTHROUGH); + obj.setProperty("uvAnimationRotationSpeed", FALLTHROUGH); + } else if (material.key._flags[graphics::MaterialKey::SCATTERING_VAL_BIT]) { + obj.setProperty("uvAnimationScrollXSpeed", material.uvAnimationScrollXSpeed); + obj.setProperty("uvAnimationScrollYSpeed", material.uvAnimationScrollYSpeed); + obj.setProperty("uvAnimationRotationSpeed", material.uvAnimationRotationSpeed); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::EXTRA_1_BIT)) { + obj.setProperty("outlineWidthMode", FALLTHROUGH); + } else { + obj.setProperty("outlineWidthMode", material.outlineWidthMode); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::EXTRA_2_BIT)) { + obj.setProperty("outlineWidth", FALLTHROUGH); + } else { + obj.setProperty("outlineWidth", material.outlineWidth); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::EXTRA_3_BIT)) { + obj.setProperty("outline", FALLTHROUGH); + } else { + obj.setProperty("outline", vec3ColorToScriptValue(engine, material.outline)); + } + } } else if (material.model.toStdString() == graphics::Material::HIFI_SHADER_SIMPLE) { obj.setProperty("procedural", material.procedural); } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index 21454dfda0..3cf70915c3 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -27,27 +27,50 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const opacity = material.opacity; albedo = material.albedo; - if (model.toStdString() == graphics::Material::HIFI_PBR) { + if (model.toStdString() == graphics::Material::HIFI_PBR || model.toStdString() == graphics::Material::VRM_MTOON) { opacityCutoff = material.opacityCutoff; opacityMapMode = material.opacityMapMode; - roughness = material.roughness; - metallic = material.metallic; - scattering = material.scattering; - unlit = material.unlit; emissive = material.emissive; emissiveMap = material.emissiveMap; albedoMap = material.albedoMap; opacityMap = material.opacityMap; - metallicMap = material.metallicMap; - specularMap = material.specularMap; - roughnessMap = material.roughnessMap; - glossMap = material.glossMap; normalMap = material.normalMap; bumpMap = material.bumpMap; - occlusionMap = material.occlusionMap; - lightMap = material.lightMap; - scatteringMap = material.scatteringMap; cullFaceMode = material.cullFaceMode; + + if (model.toStdString() == graphics::Material::HIFI_PBR) { + roughness = material.roughness; + metallic = material.metallic; + scattering = material.scattering; + unlit = material.unlit; + metallicMap = material.metallicMap; + specularMap = material.specularMap; + roughnessMap = material.roughnessMap; + glossMap = material.glossMap; + occlusionMap = material.occlusionMap; + lightMap = material.lightMap; + scatteringMap = material.scatteringMap; + } else { + shade = material.shade; + shadeMap = material.shadeMap; + shadingShift = material.shadingShift; + shadingShiftMap = material.shadingShiftMap; + shadingToony = material.shadingToony; + matcap = material.matcap; + matcapMap = material.matcapMap; + parametricRim = material.parametricRim; + parametricRimFresnelPower = material.parametricRimFresnelPower; + parametricRimLift = material.parametricRimLift; + rimMap = material.rimMap; + rimLightingMix = material.rimLightingMix; + outlineWidthMode = material.outlineWidthMode; + outlineWidth = material.outlineWidth; + outline = material.outline; + uvAnimationMaskMap = material.uvAnimationMaskMap; + uvAnimationScrollXSpeed = material.uvAnimationScrollXSpeed; + uvAnimationScrollYSpeed = material.uvAnimationScrollYSpeed; + uvAnimationRotationSpeed = material.uvAnimationRotationSpeed; + } } else if (model.toStdString() == graphics::Material::HIFI_SHADER_SIMPLE) { procedural = material.procedural; } @@ -67,13 +90,9 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint opacity = material->getOpacity(); albedo = material->getAlbedo(); - if (model.toStdString() == graphics::Material::HIFI_PBR) { + if (model.toStdString() == graphics::Material::HIFI_PBR || model.toStdString() == graphics::Material::VRM_MTOON) { opacityCutoff = material->getOpacityCutoff(); opacityMapMode = QString(graphics::MaterialKey::getOpacityMapModeName(material->getOpacityMapMode()).c_str()); - roughness = material->getRoughness(); - metallic = material->getMetallic(); - scattering = material->getScattering(); - unlit = material->isUnlit(); emissive = material->getEmissive(); auto map = material->getTextureMap(graphics::Material::MapChannel::EMISSIVE_MAP); @@ -89,24 +108,6 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint } } - map = material->getTextureMap(graphics::Material::MapChannel::METALLIC_MAP); - if (map && map->getTextureSource()) { - if (map->getTextureSource()->getType() == image::TextureUsage::Type::METALLIC_TEXTURE) { - metallicMap = map->getTextureSource()->getUrl().toString(); - } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::SPECULAR_TEXTURE) { - specularMap = map->getTextureSource()->getUrl().toString(); - } - } - - map = material->getTextureMap(graphics::Material::MapChannel::ROUGHNESS_MAP); - if (map && map->getTextureSource()) { - if (map->getTextureSource()->getType() == image::TextureUsage::Type::ROUGHNESS_TEXTURE) { - roughnessMap = map->getTextureSource()->getUrl().toString(); - } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::GLOSS_TEXTURE) { - glossMap = map->getTextureSource()->getUrl().toString(); - } - } - map = material->getTextureMap(graphics::Material::MapChannel::NORMAL_MAP); if (map && map->getTextureSource()) { if (map->getTextureSource()->getType() == image::TextureUsage::Type::NORMAL_TEXTURE) { @@ -116,26 +117,92 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint } } - map = material->getTextureMap(graphics::Material::MapChannel::OCCLUSION_MAP); - if (map && map->getTextureSource()) { - occlusionMap = map->getTextureSource()->getUrl().toString(); - } - - map = material->getTextureMap(graphics::Material::MapChannel::LIGHT_MAP); - if (map && map->getTextureSource()) { - lightMap = map->getTextureSource()->getUrl().toString(); - } - - map = material->getTextureMap(graphics::Material::MapChannel::SCATTERING_MAP); - if (map && map->getTextureSource()) { - scatteringMap = map->getTextureSource()->getUrl().toString(); - } - for (int i = 0; i < graphics::Material::NUM_TEXCOORD_TRANSFORMS; i++) { texCoordTransforms[i] = material->getTexCoordTransform(i); } cullFaceMode = QString(graphics::MaterialKey::getCullFaceModeName(material->getCullFaceMode()).c_str()); + + if (model.toStdString() == graphics::Material::HIFI_PBR) { + roughness = material->getRoughness(); + metallic = material->getMetallic(); + scattering = material->getScattering(); + unlit = material->isUnlit(); + + map = material->getTextureMap(graphics::Material::MapChannel::METALLIC_MAP); + if (map && map->getTextureSource()) { + if (map->getTextureSource()->getType() == image::TextureUsage::Type::METALLIC_TEXTURE) { + metallicMap = map->getTextureSource()->getUrl().toString(); + } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::SPECULAR_TEXTURE) { + specularMap = map->getTextureSource()->getUrl().toString(); + } + } + + map = material->getTextureMap(graphics::Material::MapChannel::ROUGHNESS_MAP); + if (map && map->getTextureSource()) { + if (map->getTextureSource()->getType() == image::TextureUsage::Type::ROUGHNESS_TEXTURE) { + roughnessMap = map->getTextureSource()->getUrl().toString(); + } else if (map->getTextureSource()->getType() == image::TextureUsage::Type::GLOSS_TEXTURE) { + glossMap = map->getTextureSource()->getUrl().toString(); + } + } + + map = material->getTextureMap(graphics::Material::MapChannel::OCCLUSION_MAP); + if (map && map->getTextureSource()) { + occlusionMap = map->getTextureSource()->getUrl().toString(); + } + + map = material->getTextureMap(graphics::Material::MapChannel::LIGHT_MAP); + if (map && map->getTextureSource()) { + lightMap = map->getTextureSource()->getUrl().toString(); + } + + map = material->getTextureMap(graphics::Material::MapChannel::SCATTERING_MAP); + if (map && map->getTextureSource()) { + scatteringMap = map->getTextureSource()->getUrl().toString(); + } + } else { + shade = material->getShade(); + shadingShift = material->getShadingShift(); + shadingToony = material->getShadingToony(); + matcap = material->getMatcap(); + parametricRim = material->getParametricRim(); + parametricRimFresnelPower = material->getParametricRimFresnelPower(); + parametricRimLift = material->getParametricRimLift(); + rimLightingMix = material->getRimLightingMix(); + outlineWidthMode = material->getOutlineWidthMode(); + outlineWidth = material->getOutlineWidth(); + outline = material->getOutline(); + uvAnimationScrollXSpeed = material->getUVAnimationScrollXSpeed(); + uvAnimationScrollYSpeed = material->getUVAnimationScrollYSpeed(); + uvAnimationRotationSpeed = material->getUVAnimationRotationSpeed(); + + // See the mappings in ProceduralMatericalCache.h + map = material->getTextureMap(graphics::Material::MapChannel::ROUGHNESS_MAP); + if (map && map->getTextureSource()) { + shadeMap = map->getTextureSource()->getUrl().toString(); + } + + map = material->getTextureMap(graphics::Material::MapChannel::METALLIC_MAP); + if (map && map->getTextureSource()) { + shadingShiftMap = map->getTextureSource()->getUrl().toString(); + } + + map = material->getTextureMap(graphics::Material::MapChannel::OCCLUSION_MAP); + if (map && map->getTextureSource()) { + matcapMap = map->getTextureSource()->getUrl().toString(); + } + + map = material->getTextureMap(graphics::Material::MapChannel::SCATTERING_MAP); + if (map && map->getTextureSource()) { + rimMap = map->getTextureSource()->getUrl().toString(); + } + + map = material->getTextureMap(graphics::Material::MapChannel::LIGHT_MAP); + if (map && map->getTextureSource()) { + uvAnimationMaskMap = map->getTextureSource()->getUrl().toString(); + } + } } else if (model.toStdString() == graphics::Material::HIFI_SHADER_SIMPLE) { procedural = material->getProceduralString(); } diff --git a/libraries/graphics/src/graphics/Material.cpp b/libraries/graphics/src/graphics/Material.cpp index 836487de14..0e0ec3588c 100644 --- a/libraries/graphics/src/graphics/Material.cpp +++ b/libraries/graphics/src/graphics/Material.cpp @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 12/10/2014. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -60,7 +61,8 @@ bool MaterialKey::getCullFaceModeFromName(const std::string& modeName, CullFaceM } const std::string Material::HIFI_PBR { "hifi_pbr" }; -const std::string Material::HIFI_SHADER_SIMPLE { "hifi_shader_simple" }; +const std::string Material::HIFI_SHADER_SIMPLE{ "hifi_shader_simple" }; +const std::string Material::VRM_MTOON { "vrm_mtoon" }; Material::Material() { for (int i = 0; i < NUM_TOTAL_FLAGS; i++) { @@ -258,6 +260,17 @@ void Material::setTextureTransforms(const Transform& transform, MaterialMappingM _materialParams = glm::vec2(mode, repeat); } +const glm::vec3 Material::DEFAULT_SHADE = glm::vec3(0.0f); +const float Material::DEFAULT_SHADING_SHIFT = 0.0f; +const float Material::DEFAULT_SHADING_TOONY = 0.9f; +const glm::vec3 Material::DEFAULT_MATCAP = glm::vec3(1.0f); +const glm::vec3 Material::DEFAULT_PARAMETRIC_RIM = glm::vec3(0.0f); +const float Material::DEFAULT_PARAMETRIC_RIM_FRESNEL_POWER = 5.0f; +const float Material::DEFAULT_PARAMETRIC_RIM_LIFT = 0.0f; +const float Material::DEFAULT_RIM_LIGHTING_MIX = 1.0f; +const float Material::DEFAULT_UV_ANIMATION_SCROLL_SPEED = 0.0f; +const glm::vec3 Material::DEFAULT_OUTLINE = glm::vec3(0.0f); + MultiMaterial::MultiMaterial() { Schema schema; _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema))); @@ -311,3 +324,30 @@ bool MultiMaterial::anyReferenceMaterialsOrTexturesChanged() const { return false; } + +void MultiMaterial::setisMToon(bool isMToon) { + if (isMToon != _isMToon) { + if (isMToon) { + MToonSchema toonSchema; + _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(MToonSchema), (const gpu::Byte*) &toonSchema, sizeof(MToonSchema))); + } else { + Schema schema; + _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema))); + } + } + _isMToon = isMToon; +} + +void MultiMaterial::setMToonTime() { + assert(_isMToon); + + // Some objects, like material entities, don't have persistent MultiMaterials to store this in, so we just store it once statically + static uint64_t mtoonStartTime; + static std::once_flag once; + std::call_once(once, [] { + mtoonStartTime = usecTimestampNow(); + }); + + // Minimize floating point error by doing an integer division to milliseconds, before the floating point division to seconds + _schemaBuffer.edit()._time = (float)((usecTimestampNow() - mtoonStartTime) / USECS_PER_MSEC) / MSECS_PER_SECOND; +} diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index 2eb4e0cbe1..8c67fc19a6 100644 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 12/10/2014. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -34,6 +35,7 @@ typedef std::shared_ptr< TextureMap > TextureMapPointer; // Material Key is a coarse trait description of a material used to classify the materials class MaterialKey { public: + // Be careful changing these, they need to match up with the bits in graphics/Material.slh enum FlagBit { EMISSIVE_VAL_BIT = 0, UNLIT_VAL_BIT, @@ -57,6 +59,12 @@ public: LIGHT_MAP_BIT, SCATTERING_MAP_BIT, + EXTRA_1_BIT, + EXTRA_2_BIT, + EXTRA_3_BIT, + EXTRA_4_BIT, + EXTRA_5_BIT, + NUM_FLAGS, }; typedef std::bitset Flags; @@ -419,6 +427,10 @@ public: MATERIAL_PARAMS, CULL_FACE_MODE, + EXTRA_1_BIT, + EXTRA_2_BIT, + EXTRA_3_BIT, + NUM_TOTAL_FLAGS }; std::unordered_map getPropertyFallthroughs() { return _propertyFallthroughs; } @@ -432,15 +444,43 @@ public: virtual bool isReference() const { return false; } + virtual bool isMToon() const { return false; } + static const glm::vec3 DEFAULT_SHADE; + virtual glm::vec3 getShade(bool SRGB = true) const { return glm::vec3(0.0f); } + static const float DEFAULT_SHADING_SHIFT; + virtual float getShadingShift() const { return 0.0f; } + static const float DEFAULT_SHADING_TOONY; + virtual float getShadingToony() const { return 0.0f; } + static const glm::vec3 DEFAULT_MATCAP; + virtual glm::vec3 getMatcap(bool SRGB = true) const { return glm::vec3(0.0f); } + static const glm::vec3 DEFAULT_PARAMETRIC_RIM; + virtual glm::vec3 getParametricRim(bool SRGB = true) const { return glm::vec3(0.0f); } + static const float DEFAULT_PARAMETRIC_RIM_FRESNEL_POWER; + virtual float getParametricRimFresnelPower() const { return 0.0f; } + static const float DEFAULT_PARAMETRIC_RIM_LIFT; + virtual float getParametricRimLift() const { return 0.0f; } + static const float DEFAULT_RIM_LIGHTING_MIX; + virtual float getRimLightingMix() const { return 0.0f; } + static const float DEFAULT_UV_ANIMATION_SCROLL_SPEED; + virtual float getUVAnimationScrollXSpeed() const { return 0.0f; } + virtual float getUVAnimationScrollYSpeed() const { return 0.0f; } + virtual float getUVAnimationRotationSpeed() const { return 0.0f; } + + static const glm::vec3 DEFAULT_OUTLINE; + virtual uint8_t getOutlineWidthMode() { return 0; } + virtual float getOutlineWidth() { return 0.0f; } + virtual glm::vec3 getOutline(bool SRGB = true) const { return glm::vec3(0.0f); } + static const std::string HIFI_PBR; static const std::string HIFI_SHADER_SIMPLE; + static const std::string VRM_MTOON; protected: std::string _name { "" }; + mutable MaterialKey _key { 0 }; private: std::string _model { HIFI_PBR }; - mutable MaterialKey _key { 0 }; // Material properties glm::vec3 _emissive { DEFAULT_EMISSIVE }; @@ -525,12 +565,12 @@ public: // 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 }; + glm::vec2 _lightmapParams { 0.0, 1.0 }; + Schema() { for (auto& transform : _texcoordTransforms) { transform = glm::mat4(); @@ -538,8 +578,68 @@ public: } }; + class MToonSchema { + 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 _opacityCutoff { Material::DEFAULT_OPACITY_CUTOFF }; // Opacity cutoff applyed when using opacityMap as Mask + + glm::vec3 _shade { Material::DEFAULT_SHADE }; + float _shadingShift { Material::DEFAULT_SHADING_SHIFT }; + + glm::vec3 _matcap { Material::DEFAULT_MATCAP }; + float _shadingToony { Material::DEFAULT_SHADING_TOONY }; + + glm::vec3 _parametricRim { Material::DEFAULT_PARAMETRIC_RIM }; + float _parametricRimFresnelPower { Material::DEFAULT_PARAMETRIC_RIM_FRESNEL_POWER }; + + float _parametricRimLift { Material::DEFAULT_PARAMETRIC_RIM_LIFT }; + float _rimLightingMix { Material::DEFAULT_RIM_LIGHTING_MIX }; + glm::vec2 _uvAnimationScrollSpeed { Material::DEFAULT_UV_ANIMATION_SCROLL_SPEED }; + + float _uvAnimationScrollRotationSpeed { Material::DEFAULT_UV_ANIMATION_SCROLL_SPEED }; + float _time { 0.0f }; + uint32_t _key { 0 }; // a copy of the materialKey + float _spare { 0.0f }; + + // Texture Coord Transform Array + glm::mat4 _texcoordTransforms[Material::NUM_TEXCOORD_TRANSFORMS]; + + // 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 }; + + MToonSchema() { + for (auto& transform : _texcoordTransforms) { + transform = glm::mat4(); + } + } + }; + gpu::BufferView& getSchemaBuffer() { return _schemaBuffer; } - graphics::MaterialKey getMaterialKey() const { return graphics::MaterialKey(_schemaBuffer.get()._key); } + graphics::MaterialKey getMaterialKey() const { + if (_isMToon) { + return graphics::MaterialKey(_schemaBuffer.get()._key); + } else { + return graphics::MaterialKey(_schemaBuffer.get()._key); + } + } + glm::vec4 getColor() const { + glm::vec3 albedo; + float opacity; + if (_isMToon) { + const auto& schema = _schemaBuffer.get(); + albedo = schema._albedo; + opacity = schema._opacity; + } else { + const auto& schema = _schemaBuffer.get(); + albedo = schema._albedo; + opacity = schema._opacity; + } + return glm::vec4(ColorUtils::tosRGBVec3(albedo), opacity); + } const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; } void setCullFaceMode(graphics::MaterialKey::CullFaceMode cullFaceMode) { _cullFaceMode = cullFaceMode; } @@ -559,6 +659,18 @@ public: void addReferenceTexture(const std::function& textureOperator); void addReferenceMaterial(const std::function& materialOperator); + void setisMToon(bool isMToon); + bool isMToon() const { return _isMToon; } + void setMToonTime(); + bool hasOutline() const { return _outlineWidthMode != 0 && _outlineWidth > 0.0f; } + uint8_t getOutlineWidthMode() const { return _outlineWidthMode; } + float getOutlineWidth() const { return _outlineWidth; } + glm::vec3 getOutline() const { return _outline; } + void resetOutline() { _outlineWidthMode = 0; _outlineWidth = 0.0f; _outline = glm::vec3(0.0f); } + void setOutlineWidthMode(uint8_t mode) { _outlineWidthMode = mode; } + void setOutlineWidth(float width) { _outlineWidth = width; } + void setOutline(const glm::vec3& outline) { _outline = outline; } + private: gpu::BufferView _schemaBuffer; graphics::MaterialKey::CullFaceMode _cullFaceMode { graphics::Material::DEFAULT_CULL_FACE_MODE }; @@ -576,6 +688,11 @@ private: std::vector, gpu::TexturePointer>> _referenceTextures; std::vector, graphics::MaterialPointer>> _referenceMaterials; + + bool _isMToon { false }; + uint8_t _outlineWidthMode { 0 }; + float _outlineWidth { 0.0f }; + glm::vec3 _outline { graphics::Material::DEFAULT_OUTLINE }; }; }; diff --git a/libraries/graphics/src/graphics/Material.slh b/libraries/graphics/src/graphics/Material.slh index 274dbc1cdd..4d4dcde34c 100644 --- a/libraries/graphics/src/graphics/Material.slh +++ b/libraries/graphics/src/graphics/Material.slh @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 12/16/14. // Copyright 2013 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -18,8 +19,10 @@ const int MAX_TEXCOORDS = 2; struct TexMapArray { mat4 _texcoordTransforms0; mat4 _texcoordTransforms1; - vec2 _lightmapParams; vec2 _materialParams; +<@if not HIFI_USE_MTOON@> + vec2 _lightmapParams; +<@endif@> }; <@func declareMaterialTexMapArrayBuffer()@> @@ -45,11 +48,24 @@ struct TexMapArray { // The material values (at least the material key) must be precisely bitwise accurate // to what is provided by the uniform buffer, or the material key has the wrong bits +<@if not HIFI_USE_MTOON@> struct Material { vec4 _emissiveOpacity; vec4 _albedoRoughness; vec4 _metallicScatteringOpacityCutoffKey; }; +<@else@> +struct Material { + vec4 _emissiveOpacity; + vec4 _albedoOpacityCutoff; + + vec4 _shadeShadingShift; + vec4 _matcapShadingToony; + vec4 _parametricRimAndFresnelPower; + vec4 _parametricRimLiftMixUVAnimationScrollSpeedXY; + vec4 _uvAnimationScrollRotationSpeedTimeKeySpare; +}; +<@endif@> LAYOUT_STD140(binding=GRAPHICS_BUFFER_MATERIAL) uniform materialBuffer { Material _mat; @@ -63,39 +79,91 @@ TexMapArray getTexMapArray() { return _texMapArray; } -vec3 getMaterialEmissive(Material m) { return m._emissiveOpacity.rgb; } -float getMaterialOpacity(Material m) { return m._emissiveOpacity.a; } +<@if not HIFI_USE_MTOON@> + vec3 getMaterialEmissive(Material m) { return m._emissiveOpacity.rgb; } + float getMaterialOpacity(Material m) { return m._emissiveOpacity.a; } -vec3 getMaterialAlbedo(Material m) { return m._albedoRoughness.rgb; } -float getMaterialRoughness(Material m) { return m._albedoRoughness.a; } -float getMaterialShininess(Material m) { return 1.0 - getMaterialRoughness(m); } + vec3 getMaterialAlbedo(Material m) { return m._albedoRoughness.rgb; } + float getMaterialRoughness(Material m) { return m._albedoRoughness.a; } + float getMaterialShininess(Material m) { return 1.0 - getMaterialRoughness(m); } -float getMaterialMetallic(Material m) { return m._metallicScatteringOpacityCutoffKey.x; } -float getMaterialScattering(Material m) { return m._metallicScatteringOpacityCutoffKey.y; } -float getMaterialOpacityCutoff(Material m) { return m._metallicScatteringOpacityCutoffKey.z; } + float getMaterialMetallic(Material m) { return m._metallicScatteringOpacityCutoffKey.x; } + float getMaterialScattering(Material m) { return m._metallicScatteringOpacityCutoffKey.y; } + float getMaterialOpacityCutoff(Material m) { return m._metallicScatteringOpacityCutoffKey.z; } -BITFIELD getMaterialKey(Material m) { return floatBitsToInt(m._metallicScatteringOpacityCutoffKey.w); } + BITFIELD getMaterialKey(Material m) { return floatBitsToInt(m._metallicScatteringOpacityCutoffKey.w); } -const BITFIELD EMISSIVE_VAL_BIT = 0x00000001; -const BITFIELD UNLIT_VAL_BIT = 0x00000002; -const BITFIELD ALBEDO_VAL_BIT = 0x00000004; -const BITFIELD METALLIC_VAL_BIT = 0x00000008; -const BITFIELD GLOSSY_VAL_BIT = 0x00000010; -const BITFIELD OPACITY_VAL_BIT = 0x00000020; -const BITFIELD OPACITY_MASK_MAP_BIT = 0x00000040; -const BITFIELD OPACITY_TRANSLUCENT_MAP_BIT = 0x00000080; -const BITFIELD OPACITY_MAP_MODE_BIT = 0x00000100; -const BITFIELD OPACITY_CUTOFF_VAL_BIT = 0x00000200; -const BITFIELD SCATTERING_VAL_BIT = 0x00000400; + const BITFIELD EMISSIVE_VAL_BIT = 0x00000001; + const BITFIELD UNLIT_VAL_BIT = 0x00000002; + const BITFIELD ALBEDO_VAL_BIT = 0x00000004; + const BITFIELD METALLIC_VAL_BIT = 0x00000008; + const BITFIELD GLOSSY_VAL_BIT = 0x00000010; + const BITFIELD OPACITY_VAL_BIT = 0x00000020; + const BITFIELD OPACITY_MASK_MAP_BIT = 0x00000040; + const BITFIELD OPACITY_TRANSLUCENT_MAP_BIT = 0x00000080; + const BITFIELD OPACITY_MAP_MODE_BIT = 0x00000100; + const BITFIELD OPACITY_CUTOFF_VAL_BIT = 0x00000200; + const BITFIELD SCATTERING_VAL_BIT = 0x00000400; -const BITFIELD EMISSIVE_MAP_BIT = 0x00000800; -const BITFIELD ALBEDO_MAP_BIT = 0x00001000; -const BITFIELD METALLIC_MAP_BIT = 0x00002000; -const BITFIELD ROUGHNESS_MAP_BIT = 0x00004000; -const BITFIELD NORMAL_MAP_BIT = 0x00008000; -const BITFIELD OCCLUSION_MAP_BIT = 0x00010000; -const BITFIELD LIGHTMAP_MAP_BIT = 0x00020000; -const BITFIELD SCATTERING_MAP_BIT = 0x00040000; + const BITFIELD EMISSIVE_MAP_BIT = 0x00000800; + const BITFIELD ALBEDO_MAP_BIT = 0x00001000; + const BITFIELD METALLIC_MAP_BIT = 0x00002000; + const BITFIELD ROUGHNESS_MAP_BIT = 0x00004000; + const BITFIELD NORMAL_MAP_BIT = 0x00008000; + const BITFIELD OCCLUSION_MAP_BIT = 0x00010000; + const BITFIELD LIGHTMAP_MAP_BIT = 0x00020000; + const BITFIELD SCATTERING_MAP_BIT = 0x00040000; +<@else@> + vec3 getMaterialEmissive(Material m) { return m._emissiveOpacity.rgb; } + float getMaterialOpacity(Material m) { return m._emissiveOpacity.a; } + + vec3 getMaterialAlbedo(Material m) { return m._albedoOpacityCutoff.rgb; } + float getMaterialOpacityCutoff(Material m) { return m._albedoOpacityCutoff.z; } + + vec3 getMaterialShade(Material m) { return m._shadeShadingShift.rgb; } + float getMaterialShadingShift(Material m) { return m._shadeShadingShift.a; } + + vec3 getMaterialMatcap(Material m) { return m._matcapShadingToony.rgb; } + float getMaterialShadingToony(Material m) { return m._matcapShadingToony.a; } + + vec3 getMaterialParametricRim(Material m) { return m._parametricRimAndFresnelPower.rgb; } + float getMaterialParametricRimFresnelPower(Material m) { return m._parametricRimAndFresnelPower.a; } + + float getMaterialParametricRimLift(Material m) { return m._parametricRimLiftMixUVAnimationScrollSpeedXY.r; } + float getMaterialRimLightingMix(Material m) { return m._parametricRimLiftMixUVAnimationScrollSpeedXY.g; } + + vec3 getMaterialUVScrollSpeed(Material m) { return vec3(m._parametricRimLiftMixUVAnimationScrollSpeedXY.ba, m._uvAnimationScrollRotationSpeedTimeKeySpare.r); } + float getMaterialTime(Material m) { return m._uvAnimationScrollRotationSpeedTimeKeySpare.g; } + + BITFIELD getMaterialKey(Material m) { return floatBitsToInt(m._uvAnimationScrollRotationSpeedTimeKeySpare.b); } + + const BITFIELD EMISSIVE_VAL_BIT = 0x00000001; + const BITFIELD SHADE_VAL_BIT = 0x00000002; + const BITFIELD ALBEDO_VAL_BIT = 0x00000004; + const BITFIELD SHADING_SHIFT_VAL_BIT = 0x00000008; + const BITFIELD SHADING_TOONY_VAL_BIT = 0x00000010; + const BITFIELD OPACITY_VAL_BIT = 0x00000020; + const BITFIELD OPACITY_MASK_MAP_BIT = 0x00000040; + const BITFIELD OPACITY_TRANSLUCENT_MAP_BIT = 0x00000080; + const BITFIELD OPACITY_MAP_MODE_BIT = 0x00000100; + const BITFIELD OPACITY_CUTOFF_VAL_BIT = 0x00000200; + const BITFIELD UV_ANIMATION_SCROLL_VAL_BIT = 0x00000400; + + const BITFIELD EMISSIVE_MAP_BIT = 0x00000800; + const BITFIELD ALBEDO_MAP_BIT = 0x00001000; + const BITFIELD SHADING_SHIFT_MAP_BIT = 0x00002000; + const BITFIELD SHADE_MAP_BIT = 0x00004000; + const BITFIELD NORMAL_MAP_BIT = 0x00008000; + const BITFIELD MATCAP_MAP_BIT = 0x00010000; + const BITFIELD UV_ANIMATION_MASK_MAP_BIT = 0x00020000; + const BITFIELD RIM_MAP_BIT = 0x00040000; + + const BITFIELD MATCAP_VAL_BIT = 0x00080000; + const BITFIELD PARAMETRIC_RIM_VAL_BIT = 0x00100000; + const BITFIELD PARAMETRIC_RIM_POWER_VAL_BIT = 0x00200000; + const BITFIELD PARAMETRIC_RIM_LIFT_VAL_BIT = 0x00400000; + const BITFIELD RIM_LIGHTING_MIX_VAL_BIT = 0x00800000; +<@endif@> <@endif@> diff --git a/libraries/graphics/src/graphics/MaterialTextures.slh b/libraries/graphics/src/graphics/MaterialTextures.slh index cb83f7d9cf..083a1146be 100644 --- a/libraries/graphics/src/graphics/MaterialTextures.slh +++ b/libraries/graphics/src/graphics/MaterialTextures.slh @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 2/22/16 // Copyright 2016 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -14,10 +15,70 @@ <@include graphics/ShaderConstants.h@> <@include graphics/Material.slh@> -<@func declareMaterialTextures(withAlbedo, withRoughness, withNormal, withMetallic, withEmissive, withOcclusion, withScattering)@> - #define TAA_TEXTURE_LOD_BIAS -1.0 +<@func evalMaterialNormalLOD(fragPosES, fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> +{ + vec3 normalizedNormal = normalize(<$interpolatedNormal$>.xyz); + vec3 normalizedTangent = normalize(<$interpolatedTangent$>.xyz); + vec3 normalizedBitangent = cross(normalizedNormal, normalizedTangent); + // attenuate the normal map divergence from the mesh normal based on distance + // The attenuation range [30,100] meters from the eye is arbitrary for now + vec3 localNormal = mix(<$fetchedNormal$>, vec3(0.0, 1.0, 0.0), smoothstep(30.0, 100.0, (-<$fragPosES$>).z)); + <$normal$> = vec3(normalizedBitangent * localNormal.x + normalizedNormal * localNormal.y + normalizedTangent * localNormal.z); +} +<@endfunc@> + +<@func evalMaterialAlbedo(fetchedAlbedo, materialAlbedo, matKey, albedo)@> +{ + <$albedo$>.xyz = mix(vec3(1.0), <$materialAlbedo$>, float((<$matKey$> & ALBEDO_VAL_BIT) != 0)); + <$albedo$>.xyz *= mix(vec3(1.0), <$fetchedAlbedo$>.xyz, float((<$matKey$> & ALBEDO_MAP_BIT) != 0)); +} +<@endfunc@> + +<@func evalMaterialOpacityMask(fetchedOpacity, materialOpacityCutoff, materialOpacity, matKey, opacity)@> +{ + // This path only valid for opaque or texel opaque material + <$opacity$> = mix(<$materialOpacity$>, + step(<$materialOpacityCutoff$>, <$fetchedOpacity$>), + float((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0)); +} +<@endfunc@> + +<@func evalMaterialOpacity(fetchedOpacity, materialOpacityCutoff, materialOpacity, matKey, opacity)@> +{ + // This path only valid for transparent material + <$opacity$> = mix(<$fetchedOpacity$>, + step(<$materialOpacityCutoff$>, <$fetchedOpacity$>), + float((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0)) + * <$materialOpacity$>; +} +<@endfunc@> + +<@func evalMaterialEmissive(fetchedEmissive, materialEmissive, matKey, emissive)@> +{ + <$emissive$> = mix(<$materialEmissive$>, <$fetchedEmissive$>, float((<$matKey$> & EMISSIVE_MAP_BIT) != 0)); +} +<@endfunc@> + +<@func discardTransparent(opacity)@> +{ + if (<$opacity$> < 1.0) { + discard; + } +} +<@endfunc@> +<@func discardInvisible(opacity)@> +{ + if (<$opacity$> <= 0.0) { + discard; + } +} +<@endfunc@> + +<@if not HIFI_USE_MTOON@> +<@func declareMaterialTextures(withAlbedo, withRoughness, withNormal, withMetallic, withEmissive, withOcclusion, withScattering)@> + <@include gpu/TextureTable.slh@> #ifdef GPU_TEXTURE_TABLE_BINDLESS @@ -41,14 +102,6 @@ vec4 fetchAlbedoMap(vec2 uv) { } <@endif@> -<@if withRoughness@> -#define roughnessMap 4 -float fetchRoughnessMap(vec2 uv) { - // Should take into account TAA_TEXTURE_LOD_BIAS? - return tableTexValue(matTex, roughnessMap, uv).r; -} -<@endif@> - <@if withNormal@> #define normalMap 1 vec3 fetchNormalMap(vec2 uv) { @@ -73,6 +126,14 @@ vec3 fetchEmissiveMap(vec2 uv) { } <@endif@> +<@if withRoughness@> +#define roughnessMap 4 +float fetchRoughnessMap(vec2 uv) { + // Should take into account TAA_TEXTURE_LOD_BIAS? + return tableTexValue(matTex, roughnessMap, uv).r; +} +<@endif@> + <@if withOcclusion@> #define occlusionMap 5 float fetchOcclusionMap(vec2 uv) { @@ -98,13 +159,6 @@ vec4 fetchAlbedoMap(vec2 uv) { } <@endif@> -<@if withRoughness@> -LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_ROUGHNESS) uniform sampler2D roughnessMap; -float fetchRoughnessMap(vec2 uv) { - return (texture(roughnessMap, uv, TAA_TEXTURE_LOD_BIAS).r); -} -<@endif@> - <@if withNormal@> LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_NORMAL) uniform sampler2D normalMap; vec3 fetchNormalMap(vec2 uv) { @@ -129,6 +183,13 @@ vec3 fetchEmissiveMap(vec2 uv) { } <@endif@> +<@if withRoughness@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_ROUGHNESS) uniform sampler2D roughnessMap; +float fetchRoughnessMap(vec2 uv) { + return (texture(roughnessMap, uv, TAA_TEXTURE_LOD_BIAS).r); +} +<@endif@> + <@if withOcclusion@> LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_OCCLUSION) uniform sampler2D occlusionMap; float fetchOcclusionMap(vec2 uv) { @@ -183,7 +244,6 @@ float fetchScatteringMap(vec2 uv) { <@endfunc@> - <@func declareMaterialLightmap()@> <$declareMaterialTexMapArrayBuffer()$> @@ -195,59 +255,6 @@ vec3 fetchLightMap(vec2 uv) { } <@endfunc@> -<@func evalMaterialNormalLOD(fragPosES, fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> -{ - vec3 normalizedNormal = normalize(<$interpolatedNormal$>.xyz); - vec3 normalizedTangent = normalize(<$interpolatedTangent$>.xyz); - vec3 normalizedBitangent = cross(normalizedNormal, normalizedTangent); - // attenuate the normal map divergence from the mesh normal based on distance - // The attenuation range [30,100] meters from the eye is arbitrary for now - vec3 localNormal = mix(<$fetchedNormal$>, vec3(0.0, 1.0, 0.0), smoothstep(30.0, 100.0, (-<$fragPosES$>).z)); - <$normal$> = vec3(normalizedBitangent * localNormal.x + normalizedNormal * localNormal.y + normalizedTangent * localNormal.z); -} -<@endfunc@> - -<@func evalMaterialAlbedo(fetchedAlbedo, materialAlbedo, matKey, albedo)@> -{ - <$albedo$>.xyz = mix(vec3(1.0), <$materialAlbedo$>, float((<$matKey$> & ALBEDO_VAL_BIT) != 0)); - <$albedo$>.xyz *= mix(vec3(1.0), <$fetchedAlbedo$>.xyz, float((<$matKey$> & ALBEDO_MAP_BIT) != 0)); -} -<@endfunc@> - -<@func evalMaterialOpacityMask(fetchedOpacity, materialOpacityCutoff, materialOpacity, matKey, opacity)@> -{ - // This path only valid for opaque or texel opaque material - <$opacity$> = mix(<$materialOpacity$>, - step(<$materialOpacityCutoff$>, <$fetchedOpacity$>), - float((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0)); -} -<@endfunc@> - -<@func evalMaterialOpacity(fetchedOpacity, materialOpacityCutoff, materialOpacity, matKey, opacity)@> -{ - // This path only valid for transparent material - <$opacity$> = mix(<$fetchedOpacity$>, - step(<$materialOpacityCutoff$>, <$fetchedOpacity$>), - float((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0)) - * <$materialOpacity$>; -} -<@endfunc@> - -<@func discardTransparent(opacity)@> -{ - if (<$opacity$> < 1.0) { - discard; - } -} -<@endfunc@> -<@func discardInvisible(opacity)@> -{ - if (<$opacity$> <= 0.0) { - discard; - } -} -<@endfunc@> - <@func evalMaterialRoughness(fetchedRoughness, materialRoughness, matKey, roughness)@> { <$roughness$> = mix(<$materialRoughness$>, <$fetchedRoughness$>, float((<$matKey$> & ROUGHNESS_MAP_BIT) != 0)); @@ -260,12 +267,6 @@ vec3 fetchLightMap(vec2 uv) { } <@endfunc@> -<@func evalMaterialEmissive(fetchedEmissive, materialEmissive, matKey, emissive)@> -{ - <$emissive$> = mix(<$materialEmissive$>, <$fetchedEmissive$>, float((<$matKey$> & EMISSIVE_MAP_BIT) != 0)); -} -<@endfunc@> - <@func evalMaterialOcclusion(fetchedOcclusion, matKey, occlusion)@> { <$occlusion$> = <$fetchedOcclusion$>; @@ -277,5 +278,214 @@ vec3 fetchLightMap(vec2 uv) { <$scattering$> = mix(<$materialScattering$>, <$fetchedScattering$>, float((<$matKey$> & SCATTERING_MAP_BIT) != 0)); } <@endfunc@> +<@else@> +<@func declareMToonMaterialTextures(withAlbedo, withNormal, withShade, withEmissive, withShadingShift, withMatcap, withRim, withUVAnimationMask)@> -<@endif@> \ No newline at end of file +<@include gpu/TextureTable.slh@> + +#ifdef GPU_TEXTURE_TABLE_BINDLESS + +TextureTable(0, matTex); + + +<@if withAlbedo@> +#define albedoMap 0 +vec4 fetchAlbedoMap(vec2 uv) { + return tableTexValue(matTex, albedoMap, uv); +} +<@endif@> + +<@if withNormal@> +#define normalMap 1 +vec3 fetchNormalMap(vec2 uv) { + return tableTexValue(matTex, normalMap, uv).xyz; +} +<@endif@> + +<@if withShade@> +#define shadeMap 2 +vec3 fetchShadeMap(vec2 uv) { + return tableTexValue(matTex, shadeMap, uv).rgb; +} +<@endif@> + +<@if withEmissive@> +#define emissiveMap 3 +vec3 fetchEmissiveMap(vec2 uv) { + return tableTexValue(matTex, emissiveMap, uv).rgb; +} +<@endif@> + +<@if withShadingShift@> +#define shadingShiftMap 4 +float fetchShadingShiftMap(vec2 uv) { + return tableTexValue(matTex, shadingShiftMap, uv).r; +} +<@endif@> + +<@if withMatcap@> +#define matcapMap 5 +vec3 fetchMatcapMap(vec2 uv) { + return tableTexValue(matTex, matcapMap, uv).rgb; +} +<@endif@> + +<@if withRim@> +#define rimMap 6 +vec3 fetchRimMap(vec2 uv) { + return tableTexValue(matTex, rimMap, uv).rgb; +} +<@endif@> + +<@if withUVAnimationMask@> +#define uvAnimationMaskMap 7 +float fetchUVAnimationMaskMap(vec2 uv) { + return tableTexValue(matTex, uvAnimationMaskMap, uv).r; +} +<@endif@> + +#else + +<@if withAlbedo@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_ALBEDO) uniform sampler2D albedoMap; +vec4 fetchAlbedoMap(vec2 uv) { + return texture(albedoMap, uv, TAA_TEXTURE_LOD_BIAS); +} +<@endif@> + +<@if withNormal@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_NORMAL) uniform sampler2D normalMap; +vec3 fetchNormalMap(vec2 uv) { + // unpack normal, swizzle to get into hifi tangent space with Y axis pointing out + vec2 t = 2.0 * (texture(normalMap, uv, TAA_TEXTURE_LOD_BIAS).rg - vec2(0.5, 0.5)); + vec2 t2 = t*t; + return vec3(t.x, sqrt(max(0.0, 1.0 - t2.x - t2.y)), t.y); +} +<@endif@> + +<@if withShade@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_SHADE) uniform sampler2D shadeMap; +vec3 fetchShadeMap(vec2 uv) { + return texture(shadeMap, uv, TAA_TEXTURE_LOD_BIAS).rgb; +} +<@endif@> + +<@if withEmissive@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_EMISSIVE_LIGHTMAP) uniform sampler2D emissiveMap; +vec3 fetchEmissiveMap(vec2 uv) { + return texture(emissiveMap, uv, TAA_TEXTURE_LOD_BIAS).rgb; +} +<@endif@> + +<@if withShadingShift@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_SHADING_SHIFT) uniform sampler2D shadingShiftMap; +float fetchShadingShiftMap(vec2 uv) { + return texture(shadingShiftMap, uv, TAA_TEXTURE_LOD_BIAS).r; +} +<@endif@> + +<@if withMatcap@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_MATCAP) uniform sampler2D matcapMap; +vec3 fetchMatcapMap(vec2 uv) { + return texture(matcapMap, uv, TAA_TEXTURE_LOD_BIAS).rgb; +} +<@endif@> + +<@if withRim@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_RIM) uniform sampler2D rimMap; +vec3 fetchRimMap(vec2 uv) { + return texture(rimMap, uv, TAA_TEXTURE_LOD_BIAS).rgb; +} +<@endif@> + +<@if withUVAnimationMask@> +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_UV_ANIMATION_MASK) uniform sampler2D uvAnimationMaskMap; +float fetchUVAnimationMaskMap(vec2 uv) { + return texture(uvAnimationMaskMap, uv, TAA_TEXTURE_LOD_BIAS).r; +} +<@endif@> + +#endif + +<@endfunc@> + +<@func fetchMToonMaterialTexturesCoord0(matKey, texcoord0, albedo, normal, shade, emissive, shadingShift, rim, uvScrollSpeed, time)@> + if (getTexMapArray()._materialParams.y != 1.0 && clamp(<$texcoord0$>, vec2(0.0), vec2(1.0)) != <$texcoord0$>) { + discard; + } + + vec2 texCoord = <$texcoord0$>; + +<@if uvScrollSpeed and time@> + if ((<$matKey$> & UV_ANIMATION_SCROLL_VAL_BIT) != 0) { + <$uvScrollSpeed$> *= mix(1.0, fetchUVAnimationMaskMap(texCoord), float((<$matKey$> & UV_ANIMATION_MASK_MAP_BIT) != 0)); + <$uvScrollSpeed$> *= time; + float cosTime = cos(<$uvScrollSpeed$>.z); + float sinTime = sin(<$uvScrollSpeed$>.z); + texCoord = (mat3(cosTime, sinTime, 0, -sinTime, cosTime, 0, 0, 0, 1) * vec3(texCoord - vec2(0.5), 1.0)).xy + vec2(0.5) + <$uvScrollSpeed$>.xy; + } +<@endif@> + +<@if albedo@> + vec4 <$albedo$> = mix(vec4(1.0), fetchAlbedoMap(texCoord), float((<$matKey$> & (ALBEDO_MAP_BIT | OPACITY_MASK_MAP_BIT | OPACITY_TRANSLUCENT_MAP_BIT)) != 0)); +<@endif@> +<@if normal@> + vec3 <$normal$> = mix(vec3(0.0, 1.0, 0.0), fetchNormalMap(texCoord), float((<$matKey$> & NORMAL_MAP_BIT) != 0)); +<@endif@> +<@if shade@> + vec3 <$shade$> = float((<$matKey$> & SHADE_MAP_BIT) != 0) * fetchShadeMap(texCoord); +<@endif@> +<@if emissive@> + vec3 <$emissive$> = float((<$matKey$> & EMISSIVE_MAP_BIT) != 0) * fetchEmissiveMap(texCoord); +<@endif@> +<@if shadingShift@> + float <$shadingShift$> = float((<$matKey$> & SHADING_SHIFT_MAP_BIT) != 0) * fetchShadingShiftMap(texCoord); +<@endif@> +<@if rim@> + vec3 <$rim$> = mix(vec3(1.0), fetchRimMap(texCoord), float((<$matKey$> & RIM_MAP_BIT) != 0)); +<@endif@> +<@endfunc@> + +<@func evalMaterialShade(fetchedShade, materialShade, matKey, shade)@> +{ + <$shade$> = mix(vec3(1.0), <$materialShade$>, float((<$matKey$> & SHADE_VAL_BIT) != 0)); + <$shade$> *= mix(vec3(1.0), <$fetchedShade$>.rgb, float((<$matKey$> & SHADE_MAP_BIT) != 0)); +} +<@endfunc@> + +<@func evalMaterialShadingShift(fetchedShadingShift, materialShadingShift, matKey, shadingShift)@> +{ + <$shadingShift$> = mix(0.0, <$materialShadingShift$>, float((<$matKey$> & SHADING_SHIFT_VAL_BIT) != 0)); + <$shadingShift$> += mix(0.0, <$fetchedShadingShift$>.r, float((<$matKey$> & SHADING_SHIFT_MAP_BIT) != 0)); +} +<@endfunc@> + +<@func evalMaterialMatcap(texcoord0, materialMatcap, matKey, matcap)@> +{ + if ((<$matKey$> & (MATCAP_VAL_BIT | MATCAP_MAP_BIT)) == 0) { + <$matcap$> = vec3(0.0); + } else { + <$matcap$> = mix(vec3(1.0), <$materialMatcap$>, float((<$matKey$> & MATCAP_VAL_BIT) != 0)); + <$matcap$> *= mix(vec3(1.0), fetchMatcapMap(<$texcoord0$>), float((<$matKey$> & MATCAP_MAP_BIT) != 0)); + } +} +<@endfunc@> + +<@func evalMaterialUVScrollSpeed(fetchedUVScrollMask, materialUVScrollMask, matKey, uvScrollSpeed)@> +{ + <$uvScrollSpeed$> = mix(vec3(1.0), <$materialUVScrollMask$>, float((<$matKey$> & UV_ANIMATION_MASK_MAP_BIT) != 0)); + <$uvScrollSpeed$> *= mix(1.0, <$fetchedUVScrollMask$>.r, float((<$matKey$> & UV_ANIMATION_MASK_MAP_BIT) != 0)); +} +<@endfunc@> +<@endif@> + +<@endif@> diff --git a/libraries/graphics/src/graphics/ShaderConstants.h b/libraries/graphics/src/graphics/ShaderConstants.h index 3a614d26cd..237c780a60 100644 --- a/libraries/graphics/src/graphics/ShaderConstants.h +++ b/libraries/graphics/src/graphics/ShaderConstants.h @@ -1,6 +1,7 @@ // "hifi_pbr", "hifi_shader_simple". + * Supported models are: "hifi_pbr", "hifi_shader_simple", and "vrm_mtoon". * @property {ColorFloat|RGBS|string} emissive - The emissive color, i.e., the color that the material emits. A * {@link ColorFloat} value is treated as sRGB and must have component values in the range 0.0 – * 1.0. A {@link RGBS} value can be either RGB or sRGB. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", + * "vrm_mtoon". * @property {number|string} opacity=1.0 - The opacity, range 0.01.0. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" and - * "hifi_shader_simple" models only. + * Set to "fallthrough" to fall through to the material below. Supported models: all. * @property {boolean|string} unlit=false - true if the material is unaffected by lighting, false if * it is lit by the key light and local lights. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {ColorFloat|RGBS|string} albedo - The albedo color. A {@link ColorFloat} value is treated as sRGB and must have * component values in the range 0.01.0. A {@link RGBS} value can be either RGB or sRGB. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" and - * "hifi_shader_simple" models only. + * Set to "fallthrough" to fall through to the material below. Supported models: all. * @property {number|string} roughness - The roughness, range 0.01.0. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {number|string} metallic - The metallicness, range 0.01.0. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {number|string} scattering - The scattering, range 0.01.0. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {string} emissiveMap - The URL of the emissive texture image, or an entity ID. An entity ID may be that of an - * Image or Web entity. Set to "fallthrough" to fall through to the material below. - * "hifi_pbr" model only. + * Image or Web entity. Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", + * "vrm_mtoon". * @property {string} albedoMap - The URL of the albedo texture image, or an entity ID. An entity ID may be that of an Image - * or Web entity. Set to "fallthrough" to fall through to the material below. "hifi_pbr" - * model only. + * or Web entity. Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", + * "vrm_mtoon". * @property {string} opacityMap - The URL of the opacity texture image, or an entity ID. An entity ID may be that of an Image - * or Web entity. Set the value the same as the albedoMap value for transparency. - * "hifi_pbr" model only. + * or Web entity. Set the value the same as the albedoMap value for transparency. Supported models: "hifi_pbr", + * "vrm_mtoon". * @property {string} opacityMapMode - The mode defining the interpretation of the opacity map. Values can be: *
    *
  • "OPACITY_MAP_OPAQUE" for ignoring the opacity map information.
  • @@ -166,67 +166,113 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseMaterialF *
  • "OPACITY_MAP_BLEND" for using the opacityMap for alpha blending the material surface * with the background.
  • *
- * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: all. * @property {number|string} opacityCutoff - The opacity cutoff threshold used to determine the opaque texels of the * opacityMap when opacityMapMode is "OPACITY_MAP_MASK". Range 0.0 * – 1.0. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: all. * @property {string} cullFaceMode="CULL_BACK" - The mode defining which side of the geometry should be rendered. Values can be: *
    *
  • "CULL_NONE" to render both sides of the geometry.
  • *
  • "CULL_FRONT" to cull the front faces of the geometry.
  • *
  • "CULL_BACK" (the default) to cull the back faces of the geometry.
  • *
- * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {string} cullFaceMode - The mode defining which side of the geometry should be rendered. Values can be: - *
    - *
  • "CULL_NONE" for rendering both sides of the geometry.
  • - *
  • "CULL_FRONT" for culling the front faces of the geometry.
  • - *
  • "CULL_BACK" (the default) for culling the back faces of the geometry.
  • - *
- * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: all. * @property {string} roughnessMap - The URL of the roughness texture image. You can use this or glossMap, but not * both. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {string} glossMap - The URL of the gloss texture image. You can use this or roughnessMap, but not * both. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {string} metallicMap - The URL of the metallic texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. You can use this or specularMap, but not both. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {string} specularMap - The URL of the specular texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. You can use this or metallicMap, but not both. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {string} normalMap - The URL of the normal texture image, or an entity ID. An entity ID may be that of an Image * or Web entity. You can use this or bumpMap, but not both. Set to "fallthrough" to fall - * through to the material below. "hifi_pbr" model only. + * through to the material below. Supported models: "hifi_pbr", "vrm_mtoon". * @property {string} bumpMap - The URL of the bump texture image, or an entity ID. An entity ID may be that of an Image * or Web entity. You can use this or normalMap, but not both. Set to "fallthrough" to - * fall through to the material below. "hifi_pbr" model only. + * fall through to the material below. Supported models: "hifi_pbr", "vrm_mtoon". * @property {string} occlusionMap - The URL of the occlusion texture image, or an entity ID. An entity ID may be that of * an Image or Web entity. Set to "fallthrough" to fall through to the material below. - * "hifi_pbr" model only. + * Supported models: "hifi_pbr". * @property {string} scatteringMap - The URL of the scattering texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. Only used if normalMap or bumpMap is specified. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {string} lightMap - The URL of the light map texture image, or an entity ID. An entity ID may be that of an Image - * or Web entity. Set to "fallthrough" to fall through to the material below. "hifi_pbr" - * model only. + * or Web entity. Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {Mat4|string} texCoordTransform0 - The transform to use for all of the maps apart from occlusionMap * and lightMap. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", "vrm_mtoon". * @property {Mat4|string} texCoordTransform1 - The transform to use for occlusionMap and lightMap. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", "vrm_mtoon". * @property {string} lightmapParams - Parameters for controlling how lightMap is used. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". *

Currently not used.

* @property {string} materialParams - Parameters for controlling the material projection and repetition. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", "vrm_mtoon". *

Currently not used.

* @property {boolean} defaultFallthrough=false - true if all properties fall through to the material below * unless they are set, false if properties respect their individual fall-through settings. - * "hifi_pbr" and "hifi_shader_simple" models only. - * @property {ProceduralData} procedural - The definition of a procedural shader material. "hifi_shader_simple" model only. + * Supported models: all. + * @property {ProceduralData} procedural - The definition of a procedural shader material. Supported models: "hifi_shader_simple". + * @property {ColorFloat|RGBS|string} shade - The shade color. A {@link ColorFloat} value is treated as sRGB and must have + * component values in the range 0.01.0. A {@link RGBS} value can be either RGB or sRGB. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {string} shadeMap - The URL of the shade texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} shadingShift - The shading shift. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {string} shadingShiftMap - The URL of the shading shift texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} shadingToony - The shading toony factor. Range 0.01.0. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {ColorFloat|RGBS|string} matcap - The matcap color. A {@link ColorFloat} value is treated as sRGB and must have + * component values in the range 0.01.0. A {@link RGBS} value can be either RGB or sRGB. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {string} matcapMap - The URL of the matcap texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {ColorFloat|RGBS|string} parametricRim - The rim color. A {@link ColorFloat} value is treated as sRGB and must have + * component values in the range 0.01.0. A {@link RGBS} value can be either RGB or sRGB. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} parametricRimFresnelPower - The parametric rim fresnel exponent. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} parametricRimLift - The parametric rim lift factor. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {string} rimMap - The URL of the rim texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} rimLightingMix - How much to mix between the rim color and normal lighting. Range 0.0 + * – 1.0. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {string} outlineWidthMode="none" - The mode defining how to render the outline. Values can be: + *
    + *
  • "none" (the default) to not render an outline.
  • + *
  • "worldCoordinates" to render an outline with a constant world size, i.e. its apparent size depends on distance.
  • + *
  • "screenCoordinates" to render an outline with a constant screen size, i.e. its apparent size remains constant.
  • + *
+ * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} outlineWidth - The width of the outline, in meters if outlineWidthMode is "worldCoordinates", + * or a ratio of the screen height if outlineWidthMode is "screenCoordinates". + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {ColorFloat|RGBS|string} outline - The outline color. A {@link ColorFloat} value is treated as sRGB and must have + * component values in the range 0.01.0. A {@link RGBS} value can be either RGB or sRGB. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {string} uvAnimationMaskMap - The URL of the UV animation mask texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} uvAnimationScrollXSpeed - The speed of the UV scrolling animation in the X dimension, in UV units per second. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} uvAnimationScrollYSpeed - The speed of the UV scrolling animation in the Y dimension, in UV units per second. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". + * @property {number|string} uvAnimationRotationSpeed - The speed of the UV scrolling rotation about (0.5, 0.5), in radians per second. + * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". */ // Note: See MaterialEntityItem.h for default values used in practice. std::pair> NetworkMaterialResource::parseJSONMaterial(const QJsonValue& materialJSONValue, const QUrl& baseUrl) { @@ -254,8 +300,13 @@ std::pair> NetworkMaterialResource std::array texcoordTransforms; const QString FALLTHROUGH("fallthrough"); - if (modelString == graphics::Material::HIFI_PBR) { - auto material = std::make_shared(); + if (modelString == graphics::Material::HIFI_PBR || modelString == graphics::Material::VRM_MTOON) { + std::shared_ptr material; + if (modelString == graphics::Material::HIFI_PBR) { + material = std::make_shared(); + } else { + material = std::make_shared(); + } for (auto& key : materialJSON.keys()) { if (key == "name") { auto nameJSON = materialJSON.value(key); @@ -282,13 +333,6 @@ std::pair> NetworkMaterialResource } 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) { @@ -301,21 +345,7 @@ std::pair> NetworkMaterialResource 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 == "opacityMapMode") { + } else if (key == "opacityMapMode") { auto value = materialJSON.value(key); if (value.isString()) { auto valueString = value.toString(); @@ -348,14 +378,7 @@ std::pair> NetworkMaterialResource } } } - } 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") { + } else if (key == "emissiveMap") { auto value = materialJSON.value(key); if (value.isString()) { auto valueString = value.toString(); @@ -380,46 +403,6 @@ std::pair> NetworkMaterialResource 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()) { @@ -440,36 +423,6 @@ std::pair> NetworkMaterialResource 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::LIGHT_MAP_BIT); - } else { - material->setLightMap(baseUrl.resolved(valueString)); - } - } } else if (key == "texCoordTransform0") { auto value = materialJSON.value(key); if (value.isString()) { @@ -494,15 +447,6 @@ std::pair> NetworkMaterialResource glm::mat4 transform = mat4FromVariant(valueVariant); texcoordTransforms[1] = transform; } - } 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 and update JSDoc } else if (key == "materialParams") { auto value = materialJSON.value(key); if (value.isString()) { @@ -518,6 +462,295 @@ std::pair> NetworkMaterialResource material->setDefaultFallthrough(value.toBool()); } } + + if (modelString == graphics::Material::HIFI_PBR) { + 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 == "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 == "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 == "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::LIGHT_MAP_BIT); + } else { + material->setLightMap(baseUrl.resolved(valueString)); + } + } + } 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 and update JSDoc + } + } else if (modelString == graphics::Material::VRM_MTOON) { + auto toonMaterial = std::static_pointer_cast(material); + if (key == "shade") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::SHADE_VAL_BIT); + } else { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(value, color, isSRGB); + if (valid) { + toonMaterial->setShade(color, isSRGB); + } + } + } else if (key == "shadeMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT); + } else { + toonMaterial->setShadeMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "shadingShift") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setShadingShift(value.toDouble()); + } + } else if (key == "shadingShiftMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT); + } else { + toonMaterial->setShadingShiftMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "shadingToony") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::SHADING_TOONY_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setShadingToony(value.toDouble()); + } + } else if (key == "matcap") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::MATCAP_VAL_BIT); + } else { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(value, color, isSRGB); + if (valid) { + toonMaterial->setMatcap(color, isSRGB); + } + } + } else if (key == "matcapMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT); + } else { + toonMaterial->setMatcapMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "parametricRim") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_VAL_BIT); + } else { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(value, color, isSRGB); + if (valid) { + toonMaterial->setParametricRim(color, isSRGB); + } + } + } else if (key == "parametricRimFresnelPower") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_POWER_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setParametricRimFresnelPower(value.toDouble()); + } + } else if (key == "parametricRimLift") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_LIFT_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setParametricRimLift(value.toDouble()); + } + } else if (key == "rimMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT); + } else { + toonMaterial->setRimMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "rimLightingMix") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::RIM_LIGHTING_MIX_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setRimLightingMix(value.toDouble()); + } + } else if (key == "uvAnimationMaskMap") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT); + } else { + toonMaterial->setUVAnimationMaskMap(baseUrl.resolved(valueString)); + } + } + } else if (key == "uvAnimationScrollXSpeed") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setUVAnimationScrollXSpeed(value.toDouble()); + } + } else if (key == "uvAnimationScrollYSpeed") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setUVAnimationScrollYSpeed(value.toDouble()); + } + } else if (key == "uvAnimationRotationSpeed") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setUVAnimationRotationSpeed(value.toDouble()); + } + } else if (key == "outlineWidthMode") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::OUTLINE_WIDTH_MODE_VAL_BIT); + } else { + NetworkMToonMaterial::OutlineWidthMode mode; + if (NetworkMToonMaterial::getOutlineWidthModeFromName(valueString.toStdString(), mode)) { + toonMaterial->setOutlineWidthMode(mode); + } + } + } + } else if (key == "outlineWidth") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::OUTLINE_WIDTH_VAL_BIT); + } else if (value.isDouble()) { + toonMaterial->setOutlineWidth(value.toDouble()); + } + } else if (key == "outline") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::OUTLINE_VAL_BIT); + } else { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(value, color, isSRGB); + if (valid) { + toonMaterial->setOutline(color, isSRGB); + } + } + } + // TODO: support outlineWidthTexture and outlineLightingMix + } } // Do this after the texture maps are defined, so it overrides the default transforms @@ -893,3 +1126,138 @@ bool NetworkMaterial::checkResetOpacityMap() { } return false; } + +NetworkMToonMaterial::NetworkMToonMaterial(const NetworkMToonMaterial& material) : + NetworkMaterial(material), + _shade(material._shade), + _shadingShift(material._shadingShift), + _shadingToony(material._shadingToony), + _matcap(material._matcap), + _parametricRim(material._parametricRim), + _parametricRimFresnelPower(material._parametricRimFresnelPower), + _parametricRimLift(material._parametricRimLift), + _rimLightingMix(material._rimLightingMix), + _uvAnimationScrollXSpeed(material._uvAnimationScrollXSpeed), + _uvAnimationScrollYSpeed(material._uvAnimationScrollYSpeed), + _uvAnimationRotationSpeed(material._uvAnimationRotationSpeed), + _outlineWidthMode(material._outlineWidthMode), + _outlineWidth(material._outlineWidth), + _outline(material._outline) +{} + +std::string NetworkMToonMaterial::getOutlineWidthModeName(OutlineWidthMode mode) { + const std::string names[3] = { "none", "worldCoordinates", "screenCoordinates" }; + return names[mode]; +} + +bool NetworkMToonMaterial::getOutlineWidthModeFromName(const std::string& modeName, OutlineWidthMode& mode) { + for (int i = OUTLINE_NONE; i < NUM_OUTLINE_MODES; i++) { + mode = (OutlineWidthMode)i; + if (modeName == getOutlineWidthModeName(mode)) { + return true; + } + } + return false; +} + +void NetworkMToonMaterial::setShadeMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, (MapChannel) MToonMapChannel::SHADE_MAP); + if (map) { + setTextureMap((MapChannel) MToonMapChannel::SHADE_MAP, map); + } +} + +void NetworkMToonMaterial::setShadingShiftMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::ROUGHNESS_TEXTURE, (MapChannel) MToonMapChannel::SHADING_SHIFT_MAP); + if (map) { + setTextureMap((MapChannel) MToonMapChannel::SHADING_SHIFT_MAP, map); + } +} + +void NetworkMToonMaterial::setMatcapMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, (MapChannel)MToonMapChannel::MATCAP_MAP); + if (map) { + setTextureMap((MapChannel) MToonMapChannel::MATCAP_MAP, map); + } +} + +void NetworkMToonMaterial::setRimMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, (MapChannel)MToonMapChannel::RIM_MAP); + if (map) { + setTextureMap((MapChannel) MToonMapChannel::RIM_MAP, map); + } +} + +void NetworkMToonMaterial::setUVAnimationMaskMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::ROUGHNESS_TEXTURE, (MapChannel)MToonMapChannel::UV_ANIMATION_MASK_MAP); + if (map) { + setTextureMap((MapChannel) MToonMapChannel::UV_ANIMATION_MASK_MAP, map); + } +} + +void NetworkMToonMaterial::setShade(const glm::vec3& shade, bool isSRGB) { + _key._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADE_VAL_BIT, true); + _shade = (isSRGB ? ColorUtils::sRGBToLinearVec3(shade) : shade); +} + +void NetworkMToonMaterial::setShadingShift(float shadingShift) { + _key._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_VAL_BIT, true); + _shadingShift = shadingShift; +} + +void NetworkMToonMaterial::setShadingToony(float shadingToony) { + _key._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADING_TOONY_VAL_BIT, true); + _shadingToony = shadingToony; +} + +void NetworkMToonMaterial::setMatcap(const glm::vec3& matcap, bool isSRGB) { + _key._flags.set(MToonFlagBit::MATCAP_VAL_BIT, true); + _matcap = (isSRGB ? ColorUtils::sRGBToLinearVec3(matcap) : matcap); +} + +void NetworkMToonMaterial::setParametricRim(const glm::vec3& parametricRim, bool isSRGB) { + _key._flags.set(MToonFlagBit::PARAMETRIC_RIM_VAL_BIT, true); + _parametricRim = (isSRGB ? ColorUtils::sRGBToLinearVec3(parametricRim) : parametricRim); +} + +void NetworkMToonMaterial::setParametricRimFresnelPower(float parametricRimFresnelPower) { + _key._flags.set(MToonFlagBit::PARAMETRIC_RIM_POWER_VAL_BIT, true); + _parametricRimFresnelPower = parametricRimFresnelPower; +} + +void NetworkMToonMaterial::setParametricRimLift(float parametricRimLift) { + _key._flags.set(MToonFlagBit::PARAMETRIC_RIM_LIFT_VAL_BIT, true); + _parametricRimLift = parametricRimLift; +} + +void NetworkMToonMaterial::setRimLightingMix(float rimLightingMix) { + _key._flags.set(MToonFlagBit::RIM_LIGHTING_MIX_VAL_BIT, true); + _rimLightingMix = rimLightingMix; +} + +void NetworkMToonMaterial::setUVAnimationScrollXSpeed(float uvAnimationScrollXSpeed) { + _key._flags.set(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT, true); + _uvAnimationScrollXSpeed = uvAnimationScrollXSpeed; +} + +void NetworkMToonMaterial::setUVAnimationScrollYSpeed(float uvAnimationScrollYSpeed) { + _key._flags.set(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT, true); + _uvAnimationScrollYSpeed = uvAnimationScrollYSpeed; +} + +void NetworkMToonMaterial::setUVAnimationRotationSpeed(float uvAnimationRotationSpeed) { + _key._flags.set(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT, true); + _uvAnimationRotationSpeed = uvAnimationRotationSpeed; +} + +void NetworkMToonMaterial::setOutlineWidthMode(OutlineWidthMode mode) { + _outlineWidthMode = mode; +} + +void NetworkMToonMaterial::setOutlineWidth(float width) { + _outlineWidth = width; +} + +void NetworkMToonMaterial::setOutline(const glm::vec3& outline, bool isSRGB) { + _outline = (isSRGB ? ColorUtils::sRGBToLinearVec3(outline) : outline); +} diff --git a/libraries/procedural/src/procedural/ProceduralMaterialCache.h b/libraries/procedural/src/procedural/ProceduralMaterialCache.h index 7d6a6ecdf3..74892b7fd3 100644 --- a/libraries/procedural/src/procedural/ProceduralMaterialCache.h +++ b/libraries/procedural/src/procedural/ProceduralMaterialCache.h @@ -1,6 +1,7 @@ // // Created by Sam Gondelman on 2/9/2018 // Copyright 2018 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -63,13 +64,14 @@ protected: const bool& isOriginal() const { return _isOriginal; } -private: - // Helpers for the ctors - QUrl getTextureUrl(const QUrl& baseUrl, const HFMTexture& hfmTexture); graphics::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture, image::TextureUsage::Type type, MapChannel channel); graphics::TextureMapPointer fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel); +private: + // Helpers for the ctors + QUrl getTextureUrl(const QUrl& baseUrl, const HFMTexture& hfmTexture); + Transform _albedoTransform; Transform _lightmapTransform; vec2 _lightmapParams; @@ -77,6 +79,119 @@ private: bool _isOriginal { true }; }; +class NetworkMToonMaterial : public NetworkMaterial { +public: + NetworkMToonMaterial() : NetworkMaterial() {} + NetworkMToonMaterial(const NetworkMToonMaterial& material); + + enum MToonMapChannel { + // Keep aligned with graphics/ShaderConstants.h and graphics-scripting/ScriptableModel.cpp + SHADE_MAP = MapChannel::ROUGHNESS_MAP, + SHADING_SHIFT_MAP = MapChannel::METALLIC_MAP, + MATCAP_MAP = MapChannel::OCCLUSION_MAP, + RIM_MAP = MapChannel::SCATTERING_MAP, + UV_ANIMATION_MASK_MAP = MapChannel::LIGHT_MAP, + }; + + enum MToonFlagBit { + SHADE_MAP_BIT = graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT, + SHADING_SHIFT_MAP_BIT = graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT, + MATCAP_MAP_BIT = graphics::MaterialKey::FlagBit::OCCLUSION_MAP_BIT, + RIM_MAP_BIT = graphics::MaterialKey::FlagBit::SCATTERING_MAP_BIT, + UV_ANIMATION_MASK_MAP_BIT = graphics::MaterialKey::FlagBit::LIGHT_MAP_BIT, + + SHADE_VAL_BIT = graphics::MaterialKey::FlagBit::UNLIT_VAL_BIT, + SHADING_SHIFT_VAL_BIT = graphics::MaterialKey::FlagBit::METALLIC_VAL_BIT, + SHADING_TOONY_VAL_BIT = graphics::MaterialKey::FlagBit::GLOSSY_VAL_BIT, + UV_ANIMATION_SCROLL_VAL_BIT = graphics::MaterialKey::FlagBit::SCATTERING_VAL_BIT, + MATCAP_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_1_BIT, + PARAMETRIC_RIM_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_2_BIT, + PARAMETRIC_RIM_POWER_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_3_BIT, + PARAMETRIC_RIM_LIFT_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_4_BIT, + RIM_LIGHTING_MIX_VAL_BIT = graphics::MaterialKey::FlagBit::EXTRA_5_BIT, + + OUTLINE_WIDTH_MODE_VAL_BIT = graphics::Material::ExtraFlagBit::EXTRA_1_BIT, + OUTLINE_WIDTH_VAL_BIT = graphics::Material::ExtraFlagBit::EXTRA_2_BIT, + OUTLINE_VAL_BIT = graphics::Material::ExtraFlagBit::EXTRA_3_BIT, + }; + + enum OutlineWidthMode { + OUTLINE_NONE = 0, + OUTLINE_WORLD, + OUTLINE_SCREEN, + + NUM_OUTLINE_MODES + }; + static std::string getOutlineWidthModeName(OutlineWidthMode mode); + // find the enum value from a string, return true if match found + static bool getOutlineWidthModeFromName(const std::string& modeName, OutlineWidthMode& mode); + + bool isMToon() const override { return true; } + + void setShadeMap(const QUrl& url); + void setShadingShiftMap(const QUrl& url); + void setMatcapMap(const QUrl& url); + void setRimMap(const QUrl& url); + void setUVAnimationMaskMap(const QUrl& url); + + void setShade(const glm::vec3& shade, bool isSRGB = true); + glm::vec3 getShade(bool SRGB = true) const override { return (SRGB ? ColorUtils::tosRGBVec3(_shade) : _shade); } + + void setShadingShift(float shadeShift); + float getShadingShift() const override { return _shadingShift; } + + void setShadingToony(float shadingToony); + float getShadingToony() const override { return _shadingToony; } + + void setMatcap(const glm::vec3& matcap, bool isSRGB = true); + glm::vec3 getMatcap(bool SRGB = true) const override { return (SRGB ? ColorUtils::tosRGBVec3(_matcap) : _matcap); } + + void setParametricRim(const glm::vec3& parametricRim, bool isSRGB = true); + glm::vec3 getParametricRim(bool SRGB = true) const override { return (SRGB ? ColorUtils::tosRGBVec3(_parametricRim) : _parametricRim); } + + void setParametricRimFresnelPower(float parametricRimFresnelPower); + float getParametricRimFresnelPower() const override { return _parametricRimFresnelPower; } + + void setParametricRimLift(float parametricRimLift); + float getParametricRimLift() const override { return _parametricRimLift; } + + void setRimLightingMix(float rimLightingMix); + float getRimLightingMix() const override { return _rimLightingMix; } + + void setUVAnimationScrollXSpeed(float uvAnimationScrollXSpeed); + float getUVAnimationScrollXSpeed() const override { return _uvAnimationScrollXSpeed; } + void setUVAnimationScrollYSpeed(float uvAnimationScrollYSpeed); + float getUVAnimationScrollYSpeed() const override { return _uvAnimationScrollYSpeed; } + void setUVAnimationRotationSpeed(float uvAnimationRotationSpeed); + float getUVAnimationRotationSpeed() const override { return _uvAnimationRotationSpeed; } + + void setOutlineWidthMode(OutlineWidthMode mode); + uint8_t getOutlineWidthMode() override { return _outlineWidthMode; } + void setOutlineWidth(float width); + float getOutlineWidth() override { return _outlineWidth; } + void setOutline(const glm::vec3& outline, bool isSRGB = true); + glm::vec3 getOutline(bool SRGB = true) const override { return (SRGB ? ColorUtils::tosRGBVec3(_outline) : _outline); } + +private: + glm::vec3 _shade { DEFAULT_SHADE }; + float _shadingShift { DEFAULT_SHADING_SHIFT }; + float _shadingToony { DEFAULT_SHADING_TOONY }; + + glm::vec3 _matcap { DEFAULT_MATCAP }; + glm::vec3 _parametricRim { DEFAULT_PARAMETRIC_RIM }; + float _parametricRimFresnelPower { DEFAULT_PARAMETRIC_RIM_FRESNEL_POWER }; + float _parametricRimLift { DEFAULT_PARAMETRIC_RIM_LIFT }; + float _rimLightingMix { DEFAULT_RIM_LIGHTING_MIX }; + + float _uvAnimationScrollXSpeed { DEFAULT_UV_ANIMATION_SCROLL_SPEED }; + float _uvAnimationScrollYSpeed { DEFAULT_UV_ANIMATION_SCROLL_SPEED }; + float _uvAnimationRotationSpeed { DEFAULT_UV_ANIMATION_SCROLL_SPEED }; + + OutlineWidthMode _outlineWidthMode { OutlineWidthMode::OUTLINE_NONE }; + float _outlineWidth { 0.0f }; + glm::vec3 _outline { DEFAULT_OUTLINE }; +}; + class NetworkMaterialResource : public Resource { public: NetworkMaterialResource() : Resource() {} diff --git a/libraries/procedural/src/procedural/ReferenceMaterial.cpp b/libraries/procedural/src/procedural/ReferenceMaterial.cpp index 97211eb737..65bea1bf7a 100644 --- a/libraries/procedural/src/procedural/ReferenceMaterial.cpp +++ b/libraries/procedural/src/procedural/ReferenceMaterial.cpp @@ -1,6 +1,7 @@ // // Created by HifiExperiments on 3/14/2021 // Copyright 2021 Vircadia contributors. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -162,7 +163,7 @@ bool ReferenceMaterial::isReady() const { QString ReferenceMaterial::getProceduralString() const { return resultWithLock([&] { - auto material = getMaterial(); + auto material = getProceduralMaterial(); return material ? material->getProceduralString() : QString(); }); } @@ -212,6 +213,112 @@ void ReferenceMaterial::initializeProcedural() { }); } +// MToonMaterial +bool ReferenceMaterial::isMToon() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->isMToon() : false; + }); +} + +glm::vec3 ReferenceMaterial::getShade(bool SRGB) const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getShade(SRGB) : glm::vec3(); + }); +} + +float ReferenceMaterial::getShadingShift() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getShadingShift() : 0.0f; + }); +} + +float ReferenceMaterial::getShadingToony() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getShadingToony() : 0.0f; + }); +} + +glm::vec3 ReferenceMaterial::getMatcap(bool SRGB) const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getMatcap(SRGB) : glm::vec3(); + }); +} + +glm::vec3 ReferenceMaterial::getParametricRim(bool SRGB) const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getParametricRim(SRGB) : glm::vec3(); + }); +} + +float ReferenceMaterial::getParametricRimFresnelPower() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getParametricRimFresnelPower() : 0.0f; + }); +} + +float ReferenceMaterial::getParametricRimLift() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getParametricRimLift() : 0.0f; + }); +} + +float ReferenceMaterial::getRimLightingMix() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getRimLightingMix() : 0.0f; + }); +} + +float ReferenceMaterial::getUVAnimationScrollXSpeed() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getUVAnimationScrollXSpeed() : 0.0f; + }); +} + +float ReferenceMaterial::getUVAnimationScrollYSpeed() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getUVAnimationScrollYSpeed() : 0.0f; + }); +} + +float ReferenceMaterial::getUVAnimationRotationSpeed() const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getUVAnimationRotationSpeed() : 0.0f; + }); +} + +uint8_t ReferenceMaterial::getOutlineWidthMode() { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getOutlineWidthMode() : 0; + }); +} + +float ReferenceMaterial::getOutlineWidth() { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getOutlineWidth() : 0.0f; + }); +} + +glm::vec3 ReferenceMaterial::getOutline(bool SRGB) const { + return resultWithLock([&] { + auto material = getMToonMaterial(); + return material ? material->getOutline() : glm::vec3(0.0f); + }); +} + void ReferenceMaterial::setMaterialForUUIDOperator(std::function materialForUUIDOperator) { _unboundMaterialForUUIDOperator = materialForUUIDOperator; } @@ -244,6 +351,16 @@ graphics::ProceduralMaterialPointer ReferenceMaterial::getProceduralMaterial() c return nullptr; } +std::shared_ptr ReferenceMaterial::getMToonMaterial() const { + if (_materialForUUIDOperator) { + std::shared_ptr result = nullptr; + if (auto material = _materialForUUIDOperator()) { + return std::static_pointer_cast(material); + } + } + return nullptr; +} + template inline T ReferenceMaterial::resultWithLock(F&& f) const { if (_locked) { diff --git a/libraries/procedural/src/procedural/ReferenceMaterial.h b/libraries/procedural/src/procedural/ReferenceMaterial.h index ac778f94b1..140b86fe33 100644 --- a/libraries/procedural/src/procedural/ReferenceMaterial.h +++ b/libraries/procedural/src/procedural/ReferenceMaterial.h @@ -1,6 +1,7 @@ // // Created by HifiExperiments on 3/14/2021 // Copyright 2021 Vircadia contributors. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -52,6 +53,23 @@ public: const uint64_t& created, const ProceduralProgramKey key = ProceduralProgramKey()) override; void initializeProcedural() override; + // MToonMaterial + bool isMToon() const override; + glm::vec3 getShade(bool SRGB = true) const override; + float getShadingShift() const override; + float getShadingToony() const override; + glm::vec3 getMatcap(bool SRGB = true) const override; + glm::vec3 getParametricRim(bool SRGB = true) const override; + float getParametricRimFresnelPower() const override; + float getParametricRimLift() const override; + float getRimLightingMix() const override; + float getUVAnimationScrollXSpeed() const override; + float getUVAnimationScrollYSpeed() const override; + float getUVAnimationRotationSpeed() const override; + uint8_t getOutlineWidthMode() override; + float getOutlineWidth() override; + glm::vec3 getOutline(bool SRGB = true) const override; + bool isReference() const override { return true; } std::function getReferenceOperator() const { return _materialForUUIDOperator; } @@ -65,6 +83,7 @@ private: graphics::MaterialPointer getMaterial() const; std::shared_ptr getNetworkMaterial() const; graphics::ProceduralMaterialPointer getProceduralMaterial() const; + std::shared_ptr getMToonMaterial() const; template T resultWithLock(F&& f) const; diff --git a/libraries/render-utils/src/GlobalLight.slh b/libraries/render-utils/src/GlobalLight.slh index 6702270a5a..de8702ea8c 100644 --- a/libraries/render-utils/src/GlobalLight.slh +++ b/libraries/render-utils/src/GlobalLight.slh @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 2/5/15. // Copyright 2013 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -286,5 +287,78 @@ vec3 evalGlobalLightingAlphaBlended( } <@endfunc@> +<@if HIFI_USE_MTOON@> +<@func declareEvalGlobalLightingAlphaBlendedMToon()@> + +<$declareLightingAmbient(1, 1, 1)$> +<$declareLightingDirectional()$> + +float linearstep(float a, float b, float t) { + return clamp((t - a) / (b - a), 0.0, 1.0); +} + +vec3 evalGlobalLightingAlphaBlendedMToon( + mat4 invViewMat, float obscurance, vec3 positionES, vec3 normalWS, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, + float roughness, float opacity, vec3 shade, float shadingShift, float shadingToony, vec3 matcap, + vec3 parametricRim, float parametricRimFresnelPower, float parametricRimLift, vec3 rim, float rimMix, BITFIELD matKey) +{ + <$prepareGlobalLight(positionES, normalWS)$> + + SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragEyeDirWS); + + color += emissive * isEmissiveEnabled(); + + // Ambient + vec3 ambientDiffuse; + vec3 ambientSpecular; + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surfaceWS, metallic, fresnel, albedo, obscurance); + color += ambientDiffuse; + color += evalSpecularWithOpacity(ambientSpecular, opacity); + + // Directional MToon Shading + updateSurfaceDataWithLight(surfaceWS, lightDirection); + + float shading = surfaceWS.ndotl; + shading += shadingShift; // shadingShift includes both the scalar and texture values + shading = linearstep(-1.0 + shadingToony, 1.0 - shadingToony, shading); + + color += lightIrradiance * mix(albedo, shade, shading) * isDirectionalEnabled(); + + vec3 worldViewX = normalize(vec3(surfaceWS.eyeDir.z, 0.0, -surfaceWS.eyeDir.x)); + vec3 worldViewY = cross(surfaceWS.eyeDir, worldViewX); + vec2 matcapUV = vec2(dot(worldViewX, surfaceWS.normal), dot(worldViewY, surfaceWS.normal)) * 0.495 + 0.5; + const float epsilon = 0.00001; + + vec3 rimDiffuse; + <$evalMaterialMatcap(matcapUV, matcap, matKey, rimDiffuse)$>; + float rimColor = clamp(1.0 - dot(surfaceWS.normal, surfaceWS.eyeDir) + parametricRimLift, 0.0, 1.0); + rimColor = pow(rimColor, max(parametricRimFresnelPower, epsilon)); + rimDiffuse += rimColor * parametricRim; + rimDiffuse *= rim; + rimDiffuse = rimDiffuse * mix(vec3(1.0), vec3(0.0), rimMix); + color += rimDiffuse; + + // Haze + if (isHazeEnabled() > 0.0) { + if ((hazeParams.hazeMode & HAZE_MODE_IS_KEYLIGHT_ATTENUATED) == HAZE_MODE_IS_KEYLIGHT_ATTENUATED) { + color = computeHazeColorKeyLightAttenuation(color, lightDirection, fragPositionWS); + } + + if ((hazeParams.hazeMode & HAZE_MODE_IS_ACTIVE) == HAZE_MODE_IS_ACTIVE) { + vec4 hazeColor = computeHazeColor( + positionES, // fragment position in eye coordinates + fragPositionWS, // fragment position in world coordinates + invViewMat[3].xyz, // eye position in world coordinates + lightDirection // keylight direction vector in world coordinates + ); + + color = mix(color.rgb, hazeColor.rgb, hazeColor.a); + } + } + + return color; +} +<@endfunc@> +<@endif@> <@endif@> diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index 5a8b09b018..2152219e77 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -4,6 +4,7 @@ // // Created by Olivier Prat on 08/08/17. // Copyright 2017 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -40,6 +41,7 @@ namespace gr { #define OUTLINE_STENCIL_MASK 1 extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); +extern void sortAndRenderZPassShapes(const ShapePlumberPointer& shapePlumber, const render::RenderContextPointer& renderContext, const render::ShapeBounds& inShapes, render::ItemBounds &itemBounds); HighlightResources::HighlightResources() { } @@ -108,10 +110,14 @@ PrepareDrawHighlight::PrepareDrawHighlight() { } void PrepareDrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { - auto destinationFrameBuffer = inputs; + RenderArgs* args = renderContext->args; + auto destinationFrameBuffer = inputs; _resources->update(destinationFrameBuffer); - outputs = _resources; + outputs.edit0() = _resources; + + outputs.edit1() = args->_renderMode; + args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; } gpu::PipelinePointer DrawHighlightMask::_stencilMaskPipeline; @@ -188,61 +194,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c batch.setProjectionJitter(jitter.x, jitter.y); batch.setViewTransform(viewMat); - const std::vector keys = { - ShapeKey::Builder(), ShapeKey::Builder().withFade(), - ShapeKey::Builder().withDeformed(), ShapeKey::Builder().withDeformed().withFade(), - ShapeKey::Builder().withDeformed().withDualQuatSkinned(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withFade(), - ShapeKey::Builder().withOwnPipeline(), ShapeKey::Builder().withOwnPipeline().withFade(), - ShapeKey::Builder().withDeformed().withOwnPipeline(), ShapeKey::Builder().withDeformed().withOwnPipeline().withFade(), - ShapeKey::Builder().withDeformed().withDualQuatSkinned().withOwnPipeline(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withOwnPipeline().withFade(), - }; - std::vector> sortedShapeKeys(keys.size()); - - const int OWN_PIPELINE_INDEX = 6; - for (const auto& items : inShapes) { - itemBounds.insert(itemBounds.end(), items.second.begin(), items.second.end()); - - int index = items.first.hasOwnPipeline() ? OWN_PIPELINE_INDEX : 0; - if (items.first.isDeformed()) { - index += 2; - if (items.first.isDualQuatSkinned()) { - index += 2; - } - } - - if (items.first.isFaded()) { - index += 1; - } - - sortedShapeKeys[index].push_back(items.first); - } - - // Render non-withOwnPipeline things - for (size_t i = 0; i < OWN_PIPELINE_INDEX; i++) { - auto& shapeKeys = sortedShapeKeys[i]; - if (shapeKeys.size() > 0) { - const auto& shapePipeline = _shapePlumber->pickPipeline(args, keys[i]); - args->_shapePipeline = shapePipeline; - for (const auto& key : shapeKeys) { - renderShapes(renderContext, _shapePlumber, inShapes.at(key)); - } - } - } - - // Render withOwnPipeline things - for (size_t i = OWN_PIPELINE_INDEX; i < keys.size(); i++) { - auto& shapeKeys = sortedShapeKeys[i]; - if (shapeKeys.size() > 0) { - args->_shapePipeline = nullptr; - for (const auto& key : shapeKeys) { - args->_itemShapeKey = key._flags.to_ulong(); - renderShapes(renderContext, _shapePlumber, inShapes.at(key)); - } - } - } - - args->_shapePipeline = nullptr; - args->_batch = nullptr; + sortAndRenderZPassShapes(_shapePlumber, renderContext, inShapes, itemBounds); }); _boundsBuffer->setData(itemBounds.size() * sizeof(render::ItemBound), (const gpu::Byte*) itemBounds.data()); @@ -452,13 +404,28 @@ const gpu::PipelinePointer& DebugHighlight::getDepthPipeline() { return _depthPipeline; } -void SelectionToHighlight::run(const render::RenderContextPointer& renderContext, Outputs& outputs) { +void SelectionToHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { auto scene = renderContext->_scene; auto highlightStage = scene->getStage(render::HighlightStage::getName()); - outputs.clear(); + auto outlines = inputs.get0(); + auto framebuffer = inputs.get1(); + + outputs.edit0().clear(); + outputs.edit1().clear(); _sharedParameters->_highlightIds.fill(render::HighlightStage::INVALID_INDEX); + outputs.edit1().reserve(outlines.size()); + for (auto item : outlines) { + render::HighlightStyle style = scene->getOutlineStyle(item.id, renderContext->args->getViewFrustum() , framebuffer->getHeight()); + auto selectionName = "__OUTLINE_MATERIAL" + style.toString(); + if (highlightStage->getHighlightIdBySelection(selectionName) == HighlightStage::INVALID_INDEX) { + HighlightStage::Index newIndex = highlightStage->addHighlight(selectionName, style); + outputs.edit1().push_back(newIndex); + } + scene->addItemToSelection(selectionName, item.id); + } + int numLayers = 0; auto highlightList = highlightStage->getActiveHighlightIds(); @@ -467,8 +434,8 @@ void SelectionToHighlight::run(const render::RenderContextPointer& renderContext if (!scene->isSelectionEmpty(highlight._selectionName)) { auto highlightId = highlightStage->getHighlightIdBySelection(highlight._selectionName); - _sharedParameters->_highlightIds[outputs.size()] = highlightId; - outputs.emplace_back(highlight._selectionName); + _sharedParameters->_highlightIds[outputs.edit0().size()] = highlightId; + outputs.edit0().emplace_back(highlight._selectionName); numLayers++; if (numLayers == HighlightSharedParameters::MAX_PASS_COUNT) { @@ -500,6 +467,7 @@ void DrawHighlightTask::configure(const Config& config) { void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { const auto items = inputs.getN(0).get(); + const auto& outlines = items[RenderFetchCullSortTask::OUTLINE]; const auto sceneFrameBuffer = inputs.getN(1); const auto primaryFramebuffer = inputs.getN(2); const auto deferredFrameTransform = inputs.getN(3); @@ -518,16 +486,21 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren } auto sharedParameters = std::make_shared(); - const auto highlightSelectionNames = task.addJob("SelectionToHighlight", sharedParameters); + const auto selectionToHighlightInputs = SelectionToHighlight::Inputs(outlines, primaryFramebuffer).asVarying(); + const auto highlightSelectionOutputs = task.addJob("SelectionToHighlight", selectionToHighlightInputs, sharedParameters); // Prepare for highlight group rendering. - const auto highlightResources = task.addJob("PrepareHighlight", primaryFramebuffer); + const auto prepareOutputs = task.addJob("PrepareHighlight", primaryFramebuffer); + const auto highlightResources = prepareOutputs.getN(0); render::Varying highlight0Rect; + const auto extractSelectionNameInput = Varying(highlightSelectionOutputs.getN(0)); for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) { - const auto selectionName = task.addJob("ExtractSelectionName", highlightSelectionNames, i); + const auto selectionName = task.addJob("ExtractSelectionName", extractSelectionNameInput, i); const auto groupItems = addSelectItemJobs(task, selectionName, items); - const auto highlightedItemIDs = task.addJob("HighlightMetaToSubItemIDs", groupItems); + const auto highlightedSubItemIDs = task.addJob("HighlightMetaToSubItemIDs", groupItems); + const auto appendNonMetaOutlinesInput = AppendNonMetaOutlines::Inputs(highlightedSubItemIDs, groupItems).asVarying(); + const auto highlightedItemIDs = task.addJob("AppendNonMetaOutlines", appendNonMetaOutlinesInput); const auto highlightedItems = task.addJob("HighlightMetaToSubItems", highlightedItemIDs); // Sort @@ -557,6 +530,10 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren task.addJob(name, drawHighlightInputs, i, sharedParameters); } + // Cleanup + const auto cleanupInput = HighlightCleanup::Inputs(highlightSelectionOutputs.getN(1), prepareOutputs.getN(1)).asVarying(); + task.addJob("HighlightCleanup", cleanupInput); + // Debug highlight const auto debugInputs = DebugHighlight::Inputs(highlightResources, const_cast(highlight0Rect), jitter, primaryFramebuffer).asVarying(); task.addJob("HighlightDebug", debugInputs); @@ -576,3 +553,31 @@ const render::Varying DrawHighlightTask::addSelectItemJobs(JobModel& task, const return task.addJob("TransparentSelection", selectItemInput); } +void AppendNonMetaOutlines::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { + auto& scene = renderContext->_scene; + + outputs = inputs.get0(); + + const auto& groupItems = inputs.get1(); + for (auto idBound : groupItems) { + auto& item = scene->getItem(idBound.id); + if (item.exist() && !item.getKey().isMeta()) { + outputs.push_back(idBound.id); + } + } +} + + +void HighlightCleanup::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { + auto scene = renderContext->_scene; + auto highlightStage = scene->getStage(render::HighlightStage::getName()); + + for (auto index : inputs.get0()) { + std::string selectionName = highlightStage->getHighlight(index)._selectionName; + highlightStage->removeHighlight(index); + scene->removeSelection(selectionName); + } + + // Reset the render mode + renderContext->args->_renderMode = inputs.get1(); +} diff --git a/libraries/render-utils/src/HighlightEffect.h b/libraries/render-utils/src/HighlightEffect.h index 933503fdb5..a55ecdf46d 100644 --- a/libraries/render-utils/src/HighlightEffect.h +++ b/libraries/render-utils/src/HighlightEffect.h @@ -4,6 +4,7 @@ // // Created by Olivier Prat on 08/08/17. // Copyright 2017 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -65,7 +66,7 @@ using HighlightSharedParametersPointer = std::shared_ptr; using JobModel = render::Job::ModelIO; PrepareDrawHighlight(); @@ -81,12 +82,13 @@ private: class SelectionToHighlight { public: - using Outputs = std::vector; - using JobModel = render::Job::ModelO; + using Inputs = render::VaryingSet2; + using Outputs = render::VaryingSet2, std::vector>; + using JobModel = render::Job::ModelIO; SelectionToHighlight(HighlightSharedParametersPointer parameters) : _sharedParameters{ parameters } {} - void run(const render::RenderContextPointer& renderContext, Outputs& outputs); + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); private: @@ -96,7 +98,7 @@ private: class ExtractSelectionName { public: - using Inputs = SelectionToHighlight::Outputs; + using Inputs = std::vector; using Outputs = std::string; using JobModel = render::Job::ModelIO; @@ -209,6 +211,25 @@ private: }; +class AppendNonMetaOutlines { +public: + using Inputs = render::VaryingSet2; + using Outputs = render::ItemIDs; + using JobModel = render::Job::ModelIO; + + AppendNonMetaOutlines() {} + + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); +}; + +class HighlightCleanup { +public: + using Inputs = render::VaryingSet2, render::Args::RenderMode>; + using JobModel = render::Job::ModelI; + + HighlightCleanup() {} + + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); +}; + #endif // hifi_render_utils_HighlightEffect_h - - diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 8ba5be54e6..f6e4b3b460 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 10/3/15. // Copyright 2015 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -220,6 +221,10 @@ void ModelMeshPartPayload::updateKey(const render::ItemKey& key) { builder.withSubMetaCulled(); } + if (_drawMaterials.hasOutline()) { + builder.withOutline(); + } + _itemKey = builder.build(); } @@ -268,11 +273,15 @@ void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, PrimitiveMode pr if (hasTangents) { builder.withTangents(); } - if (hasLightmap) { - builder.withLightMap(); - } - if (isUnlit) { - builder.withUnlit(); + if (!_drawMaterials.isMToon()) { + if (hasLightmap) { + builder.withLightMap(); + } + if (isUnlit) { + builder.withUnlit(); + } + } else { + builder.withMToon(); } if (material) { builder.withCullFaceMode(material->getCullFaceMode()); @@ -377,6 +386,11 @@ bool ModelMeshPartPayload::passesZoneOcclusionTest(const std::unordered_set& blendshapeBuffers, const QVector& blendedMeshSizes) { if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) { auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex); @@ -426,4 +440,11 @@ template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Poin } return false; } + +template <> HighlightStyle payloadGetOutlineStyle(const ModelMeshPartPayload::Pointer& payload, const ViewFrustum& viewFrustum, const size_t height) { + if (payload) { + return payload->getOutlineStyle(viewFrustum, height); + } + return HighlightStyle(); +} } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 1a3a898582..b502865f94 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 10/3/15. // Copyright 2015 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -17,6 +18,7 @@ #include #include #include +#include class ModelMeshPartPayload { public: @@ -59,6 +61,7 @@ public: void setRenderWithZones(const QVector& renderWithZones) { _renderWithZones = renderWithZones; } void setBillboardMode(BillboardMode billboardMode) { _billboardMode = billboardMode; } bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const; + render::HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const; void addMaterial(graphics::MaterialLayer material) { _drawMaterials.push(material); } void removeMaterial(graphics::MaterialPointer material) { _drawMaterials.remove(material); } @@ -107,6 +110,7 @@ namespace render { template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload); template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args); template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Pointer& payload, const std::unordered_set& containingZones); + template <> HighlightStyle payloadGetOutlineStyle(const ModelMeshPartPayload::Pointer& payload, const ViewFrustum& viewFrustum, const size_t height); } #endif // hifi_MeshPartPayload_h diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index ed9fb326c4..ccbbb4d66e 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -5,6 +5,7 @@ // // Created by Zach Pomerantz on 1/28/2016. // Copyright 2016 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -42,6 +43,7 @@ namespace gr { void initDeferredPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); void initForwardPipelines(ShapePlumber& plumber); void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); +void sortAndRenderZPassShapes(const ShapePlumberPointer& shapePlumber, const render::RenderContextPointer& renderContext, const render::ShapeBounds& inShapes, render::ItemBounds &itemBounds); void addPlumberPipeline(ShapePlumber& plumber, const ShapeKey& key, int programId, @@ -84,6 +86,11 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip { Key::Builder().withMaterial().withTangents().withLightMap(), model_normalmap_lightmap }, { Key::Builder().withMaterial().withTranslucent().withLightMap(), model_translucent_lightmap }, { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap(), model_normalmap_translucent_lightmap }, + // Unskinned MToon + { Key::Builder().withMaterial().withMToon(), model_mtoon }, + { Key::Builder().withMaterial().withTangents().withMToon(), model_normalmap_mtoon }, + { Key::Builder().withMaterial().withTranslucent().withMToon(), model_translucent_mtoon }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon(), model_normalmap_translucent_mtoon }, // Unskinned Fade { Key::Builder().withMaterial().withFade(), model_fade }, { Key::Builder().withMaterial().withTangents().withFade(), model_normalmap_fade }, @@ -99,6 +106,11 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip { Key::Builder().withMaterial().withTangents().withLightMap().withFade(), model_normalmap_lightmap_fade }, { Key::Builder().withMaterial().withTranslucent().withLightMap().withFade(), model_translucent_lightmap_fade }, { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withFade(), model_normalmap_translucent_lightmap_fade }, + // Unskinned MToon Fade + { Key::Builder().withMaterial().withMToon().withFade(), model_mtoon_fade }, + { Key::Builder().withMaterial().withTangents().withMToon().withFade(), model_normalmap_mtoon_fade }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withFade(), model_translucent_mtoon_fade }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withFade(), model_normalmap_translucent_mtoon_fade }, // Matrix palette skinned { Key::Builder().withMaterial().withDeformed(), model_deformed }, @@ -115,6 +127,11 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip { Key::Builder().withMaterial().withTangents().withLightMap().withDeformed(), model_normalmap_lightmap_deformed }, { Key::Builder().withMaterial().withTranslucent().withLightMap().withDeformed(), model_translucent_lightmap_deformed }, { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withDeformed(), model_normalmap_translucent_lightmap_deformed }, + // Matrix palette skinned MToon + { Key::Builder().withMaterial().withMToon().withDeformed(), model_mtoon_deformed }, + { Key::Builder().withMaterial().withTangents().withMToon().withDeformed(), model_normalmap_mtoon_deformed }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withDeformed(), model_translucent_mtoon_deformed }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withDeformed(), model_normalmap_translucent_mtoon_deformed }, // Matrix palette skinned Fade { Key::Builder().withMaterial().withFade().withDeformed(), model_fade_deformed }, { Key::Builder().withMaterial().withTangents().withFade().withDeformed(), model_normalmap_fade_deformed }, @@ -130,6 +147,11 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip { Key::Builder().withMaterial().withTangents().withLightMap().withFade().withDeformed(), model_normalmap_lightmap_fade_deformed }, { Key::Builder().withMaterial().withTranslucent().withLightMap().withFade().withDeformed(), model_translucent_lightmap_fade_deformed }, { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withFade().withDeformed(), model_normalmap_translucent_lightmap_fade_deformed }, + // Matrix palette skinned MToon Fade + { Key::Builder().withMaterial().withMToon().withFade().withDeformed(), model_mtoon_fade_deformed }, + { Key::Builder().withMaterial().withTangents().withMToon().withFade().withDeformed(), model_normalmap_mtoon_fade_deformed }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withFade().withDeformed(), model_translucent_mtoon_fade_deformed }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withFade().withDeformed(), model_normalmap_translucent_mtoon_fade_deformed }, // Dual quaternion skinned { Key::Builder().withMaterial().withDeformed().withDualQuatSkinned(), model_deformeddq }, @@ -146,6 +168,11 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip { Key::Builder().withMaterial().withTangents().withLightMap().withDeformed().withDualQuatSkinned(), model_normalmap_lightmap_deformeddq }, { Key::Builder().withMaterial().withTranslucent().withLightMap().withDeformed().withDualQuatSkinned(), model_translucent_lightmap_deformeddq }, { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_lightmap_deformeddq }, + // Dual quaternion skinned MToon + { Key::Builder().withMaterial().withMToon().withDeformed().withDualQuatSkinned(), model_mtoon_deformeddq }, + { Key::Builder().withMaterial().withTangents().withMToon().withDeformed().withDualQuatSkinned(), model_normalmap_mtoon_deformeddq }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withDeformed().withDualQuatSkinned(), model_translucent_mtoon_deformeddq }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_mtoon_deformeddq }, // Dual quaternion skinned Fade { Key::Builder().withMaterial().withFade().withDeformed().withDualQuatSkinned(), model_fade_deformeddq }, { Key::Builder().withMaterial().withTangents().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_fade_deformeddq }, @@ -161,6 +188,11 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip { Key::Builder().withMaterial().withTangents().withLightMap().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_lightmap_fade_deformeddq }, { Key::Builder().withMaterial().withTranslucent().withLightMap().withFade().withDeformed().withDualQuatSkinned(), model_translucent_lightmap_fade_deformeddq }, { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_lightmap_fade_deformeddq }, + // Dual quaternion skinned MToon Fade + { Key::Builder().withMaterial().withMToon().withFade().withDeformed().withDualQuatSkinned(), model_mtoon_fade_deformeddq }, + { Key::Builder().withMaterial().withTangents().withMToon().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_mtoon_fade_deformeddq }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withFade().withDeformed().withDualQuatSkinned(), model_translucent_mtoon_fade_deformeddq }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withFade().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_mtoon_fade_deformeddq }, }; for (auto& pipeline : pipelines) { @@ -209,6 +241,11 @@ void initForwardPipelines(ShapePlumber& plumber) { { Key::Builder().withMaterial().withTangents().withLightMap(), model_normalmap_lightmap_forward }, { Key::Builder().withMaterial().withTranslucent().withLightMap(), model_translucent_lightmap_forward }, { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap(), model_normalmap_translucent_lightmap_forward }, + // Unskinned MToon + { Key::Builder().withMaterial().withMToon(), model_mtoon_forward }, + { Key::Builder().withMaterial().withTangents().withMToon(), model_normalmap_mtoon_forward }, + { Key::Builder().withMaterial().withTranslucent().withMToon(), model_translucent_mtoon_forward }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon(), model_normalmap_translucent_mtoon_forward }, // Matrix palette skinned { Key::Builder().withMaterial().withDeformed(), model_forward_deformed }, @@ -225,6 +262,11 @@ void initForwardPipelines(ShapePlumber& plumber) { { Key::Builder().withMaterial().withTangents().withLightMap().withDeformed(), model_normalmap_lightmap_forward_deformed }, { Key::Builder().withMaterial().withTranslucent().withLightMap().withDeformed(), model_translucent_lightmap_forward_deformed }, { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withDeformed(), model_normalmap_translucent_lightmap_forward_deformed }, + // Matrix palette skinned MToon + { Key::Builder().withMaterial().withMToon().withDeformed(), model_mtoon_forward_deformed }, + { Key::Builder().withMaterial().withTangents().withMToon().withDeformed(), model_normalmap_mtoon_forward_deformed }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withDeformed(), model_translucent_mtoon_forward_deformed }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withDeformed(), model_normalmap_translucent_mtoon_forward_deformed }, // Dual quaternion skinned { Key::Builder().withMaterial().withDeformed().withDualQuatSkinned(), model_forward_deformeddq }, @@ -241,6 +283,11 @@ void initForwardPipelines(ShapePlumber& plumber) { { Key::Builder().withMaterial().withTangents().withLightMap().withDeformed().withDualQuatSkinned(), model_normalmap_lightmap_forward_deformeddq }, { Key::Builder().withMaterial().withTranslucent().withLightMap().withDeformed().withDualQuatSkinned(), model_translucent_lightmap_forward_deformeddq }, { Key::Builder().withMaterial().withTangents().withTranslucent().withLightMap().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_lightmap_forward_deformeddq }, + // Dual quaternion skinned MToon + { Key::Builder().withMaterial().withMToon().withDeformed().withDualQuatSkinned(), model_mtoon_forward_deformeddq }, + { Key::Builder().withMaterial().withTangents().withMToon().withDeformed().withDualQuatSkinned(), model_normalmap_mtoon_forward_deformeddq }, + { Key::Builder().withMaterial().withTranslucent().withMToon().withDeformed().withDualQuatSkinned(), model_translucent_mtoon_forward_deformeddq }, + { Key::Builder().withMaterial().withTangents().withTranslucent().withMToon().withDeformed().withDualQuatSkinned(), model_normalmap_translucent_mtoon_forward_deformeddq }, }; for (auto& pipeline : pipelines) { @@ -286,7 +333,7 @@ void addPlumberPipeline(ShapePlumber& plumber, state->setDepthBiasSlopeScale(1.0f); } - auto baseBatchSetter = (forceLightBatchSetter || key.isTranslucent()) ? &lightBatchSetter : &batchSetter; + auto baseBatchSetter = (forceLightBatchSetter || key.isTranslucent() || key.isMToon()) ? &lightBatchSetter : &batchSetter; render::ShapePipeline::BatchSetter finalBatchSetter; if (extraBatchSetter) { finalBatchSetter = [baseBatchSetter, extraBatchSetter](const ShapePipeline& pipeline, gpu::Batch& batch, render::Args* args) { @@ -347,25 +394,97 @@ void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state, con using namespace shader::render_utils::program; shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withoutDeformed().withoutFade(), + ShapeKey::Filter::Builder().withoutMToon().withoutDeformed().withoutFade(), gpu::Shader::createProgram(model_shadow), state); shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withoutDeformed().withFade(), + ShapeKey::Filter::Builder().withoutMToon().withoutDeformed().withFade(), gpu::Shader::createProgram(model_shadow_fade), state, extraBatchSetter, itemSetter); + shapePlumber.addPipeline( + ShapeKey::Filter::Builder().withMToon().withoutDeformed().withoutFade(), + gpu::Shader::createProgram(model_shadow_mtoon), state); + shapePlumber.addPipeline( + ShapeKey::Filter::Builder().withMToon().withoutDeformed().withFade(), + gpu::Shader::createProgram(model_shadow_mtoon_fade), state, extraBatchSetter, itemSetter); shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withDeformed().withoutDualQuatSkinned().withoutFade(), + ShapeKey::Filter::Builder().withoutMToon().withDeformed().withoutDualQuatSkinned().withoutFade(), gpu::Shader::createProgram(model_shadow_deformed), state); shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withDeformed().withoutDualQuatSkinned().withFade(), + ShapeKey::Filter::Builder().withoutMToon().withDeformed().withoutDualQuatSkinned().withFade(), gpu::Shader::createProgram(model_shadow_fade_deformed), state, extraBatchSetter, itemSetter); + shapePlumber.addPipeline( + ShapeKey::Filter::Builder().withMToon().withDeformed().withoutDualQuatSkinned().withoutFade(), + gpu::Shader::createProgram(model_shadow_mtoon_deformed), state); + shapePlumber.addPipeline( + ShapeKey::Filter::Builder().withMToon().withDeformed().withoutDualQuatSkinned().withFade(), + gpu::Shader::createProgram(model_shadow_mtoon_fade_deformed), state, extraBatchSetter, itemSetter); shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withDeformed().withDualQuatSkinned().withoutFade(), + ShapeKey::Filter::Builder().withoutMToon().withDeformed().withDualQuatSkinned().withoutFade(), gpu::Shader::createProgram(model_shadow_deformeddq), state); shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withDeformed().withDualQuatSkinned().withFade(), + ShapeKey::Filter::Builder().withoutMToon().withDeformed().withDualQuatSkinned().withFade(), gpu::Shader::createProgram(model_shadow_fade_deformeddq), state, extraBatchSetter, itemSetter); + shapePlumber.addPipeline( + ShapeKey::Filter::Builder().withMToon().withDeformed().withDualQuatSkinned().withoutFade(), + gpu::Shader::createProgram(model_shadow_mtoon_deformeddq), state); + shapePlumber.addPipeline( + ShapeKey::Filter::Builder().withMToon().withDeformed().withDualQuatSkinned().withFade(), + gpu::Shader::createProgram(model_shadow_mtoon_fade_deformeddq), state, extraBatchSetter, itemSetter); +} + +void sortAndRenderZPassShapes(const ShapePlumberPointer& shapePlumber, const render::RenderContextPointer& renderContext, const render::ShapeBounds& inShapes, render::ItemBounds &itemBounds) { + std::unordered_map, ShapeKey::Hash, ShapeKey::KeyEqual> sortedShapeKeys; + std::unordered_map, ShapeKey::Hash, ShapeKey::KeyEqual> sortedOwnPipelineShapeKeys; + + for (const auto& items : inShapes) { + itemBounds.insert(itemBounds.end(), items.second.begin(), items.second.end()); + + ShapeKey::Builder variantKey = ShapeKey::Builder(); + + // The keys we need to check here have to match the ones set up in initZPassPipelines (+ addPlumberPipeline) + if (items.first.isDeformed()) { + variantKey.withDeformed(); + if (items.first.isDualQuatSkinned()) { + variantKey.withDualQuatSkinned(); + } + } + + if (items.first.isFaded()) { + variantKey.withFade(); + } + + if (items.first.isMToon()) { + variantKey.withMToon(); + } + + if (items.first.hasOwnPipeline()) { + sortedOwnPipelineShapeKeys[variantKey.build()].push_back(items.first); + } else { + sortedShapeKeys[variantKey.build()].push_back(items.first); + } + } + + // Render non-withOwnPipeline things + for (auto& variantAndKeys : sortedShapeKeys) { + if (variantAndKeys.second.size() > 0) { + for (const auto& key : variantAndKeys.second) { + renderShapes(renderContext, shapePlumber, inShapes.at(key)); + } + } + } + + // Render withOwnPipeline things + for (auto& variantAndKeys : sortedOwnPipelineShapeKeys) { + if (variantAndKeys.second.size() > 0) { + for (const auto& key : variantAndKeys.second) { + renderShapes(renderContext, shapePlumber, inShapes.at(key)); + } + } + } + + renderContext->args->_shapePipeline = nullptr; + renderContext->args->_batch = nullptr; } bool RenderPipelines::bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, render::Args::RenderMode renderMode, bool enableTextures) { @@ -375,11 +494,11 @@ bool RenderPipelines::bindMaterial(graphics::MaterialPointer& material, gpu::Bat } void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial) { - auto& schemaBuffer = multiMaterial.getSchemaBuffer(); - auto& drawMaterialTextures = multiMaterial.getTextureTable(); multiMaterial.setTexturesLoading(false); multiMaterial.resetReferenceTexturesAndMaterials(); + multiMaterial.setisMToon(!multiMaterial.empty() && multiMaterial.top().material && multiMaterial.top().material->isMToon()); + multiMaterial.resetOutline(); // The total list of things we need to look for static std::set allFlags; @@ -398,6 +517,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial graphics::MultiMaterial materials = multiMaterial; graphics::MultiMaterial::Schema schema; + graphics::MultiMaterial::MToonSchema toonSchema; graphics::MaterialKey schemaKey; std::set flagsToCheck = allFlags; @@ -425,261 +545,570 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial 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::OPACITY_CUTOFF_VAL_BIT: - if (materialKey.isOpacityCutoff()) { - schema._opacityCutoff = material->getOpacityCutoff(); - schemaKey.setOpacityCutoff(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()) { - material->resetOpacityMap(); - drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (!multiMaterial.isMToon()) { + 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::OPACITY_CUTOFF_VAL_BIT: + if (materialKey.isOpacityCutoff()) { + schema._opacityCutoff = material->getOpacityCutoff(); + schemaKey.setOpacityCutoff(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()) { + material->resetOpacityMap(); + drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setAlbedoMap(true); + schemaKey.setOpacityMaskMap(material->getKey().isOpacityMaskMap()); + schemaKey.setTranslucentMap(material->getKey().isTranslucentMap()); } - schemaKey.setAlbedoMap(true); - schemaKey.setOpacityMaskMap(material->getKey().isOpacityMaskMap()); - schemaKey.setTranslucentMap(material->getKey().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()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + 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()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setMetallicMap(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()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + 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()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setRoughnessMap(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()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + 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()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setNormalMap(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()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + 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()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setOcclusionMap(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()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + 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()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setScatteringMap(true); } - schemaKey.setScatteringMap(true); - } - break; - case graphics::MaterialKey::EMISSIVE_MAP_BIT: - // Lightmap takes precendence over emissive map for legacy reasons - if (materialKey.isEmissiveMap() && !materialKey.isLightMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); - if (itr != textureMaps.end()) { - if (itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + break; + case graphics::MaterialKey::EMISSIVE_MAP_BIT: + // Lightmap takes precendence over emissive map for legacy reasons + if (materialKey.isEmissiveMap() && !materialKey.isLightMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setEmissiveMap(true); + } else if (materialKey.isLightMap()) { + // We'll set this later when we check the lightmap + wasSet = true; } - schemaKey.setEmissiveMap(true); - } else if (materialKey.isLightMap()) { - // We'll set this later when we check the lightmap - wasSet = true; - } - break; - case graphics::MaterialKey::LIGHT_MAP_BIT: - if (materialKey.isLightMap()) { - auto itr = textureMaps.find(graphics::MaterialKey::LIGHT_MAP); - if (itr != textureMaps.end()) { - if (itr->second->isDefined()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + break; + case graphics::MaterialKey::LIGHT_MAP_BIT: + if (materialKey.isLightMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::LIGHT_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; } - wasSet = true; } else { - multiMaterial.setTexturesLoading(true); forceDefault = true; } - } else { - forceDefault = true; + schemaKey.setLightMap(true); } - schemaKey.setLightMap(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; - case graphics::Material::CULL_FACE_MODE: - if (!fallthrough) { - multiMaterial.setCullFaceMode(material->getCullFaceMode()); - wasSet = true; - } - break; - default: - break; + 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; + case graphics::Material::CULL_FACE_MODE: + if (!fallthrough) { + multiMaterial.setCullFaceMode(material->getCullFaceMode()); + wasSet = true; + } + break; + default: + break; + } + } else { + switch (flag) { + case graphics::MaterialKey::EMISSIVE_VAL_BIT: + if (materialKey.isEmissive()) { + toonSchema._emissive = material->getEmissive(false); + schemaKey.setEmissive(true); + wasSet = true; + } + break; + case graphics::MaterialKey::ALBEDO_VAL_BIT: + if (materialKey.isAlbedo()) { + toonSchema._albedo = material->getAlbedo(false); + schemaKey.setAlbedo(true); + wasSet = true; + } + break; + case graphics::MaterialKey::OPACITY_VAL_BIT: + if (materialKey.isTranslucentFactor()) { + toonSchema._opacity = material->getOpacity(); + schemaKey.setTranslucentFactor(true); + wasSet = true; + } + break; + case graphics::MaterialKey::OPACITY_CUTOFF_VAL_BIT: + if (materialKey.isOpacityCutoff()) { + toonSchema._opacityCutoff = material->getOpacityCutoff(); + schemaKey.setOpacityCutoff(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()) { + material->resetOpacityMap(); + drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setAlbedoMap(true); + schemaKey.setOpacityMaskMap(material->getKey().isOpacityMaskMap()); + schemaKey.setTranslucentMap(material->getKey().isTranslucentMap()); + } + 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()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setNormalMap(true); + } + break; + case graphics::MaterialKey::EMISSIVE_MAP_BIT: + // Lightmap takes precendence over emissive map for legacy reasons + if (materialKey.isEmissiveMap() && !materialKey.isLightMap()) { + auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey.setEmissiveMap(true); + } else if (materialKey.isLightMap()) { + // We'll set this later when we check the lightmap + wasSet = true; + } + break; + case graphics::Material::TEXCOORDTRANSFORM0: + if (!fallthrough) { + toonSchema._texcoordTransforms[0] = material->getTexCoordTransform(0); + wasSet = true; + } + break; + case graphics::Material::TEXCOORDTRANSFORM1: + if (!fallthrough) { + toonSchema._texcoordTransforms[1] = material->getTexCoordTransform(1); + wasSet = true; + } + break; + case graphics::Material::MATERIAL_PARAMS: + if (!fallthrough) { + toonSchema._materialParams = material->getMaterialParams(); + wasSet = true; + } + break; + case graphics::Material::CULL_FACE_MODE: + if (!fallthrough) { + multiMaterial.setCullFaceMode(material->getCullFaceMode()); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADE_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADE_VAL_BIT]) { + toonSchema._shade = material->getShade(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADE_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_VAL_BIT]) { + toonSchema._shadingShift = material->getShadingShift(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADING_TOONY_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADING_TOONY_VAL_BIT]) { + toonSchema._shadingToony = material->getShadingToony(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADING_TOONY_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT]) { + toonSchema._uvAnimationScrollSpeed.x = material->getUVAnimationScrollXSpeed(); + toonSchema._uvAnimationScrollSpeed.y = material->getUVAnimationScrollYSpeed(); + toonSchema._uvAnimationScrollRotationSpeed = material->getUVAnimationRotationSpeed(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_SCROLL_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT : + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT]) { + auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::SHADE_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialShade, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT, true); + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT]) { + auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::SHADING_SHIFT_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialShadingShift, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT, true); + } + break; + case NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT]) { + auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::MATCAP_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialMatcap, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT, true); + } + break; + case NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT]) { + auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::RIM_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialRim, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT, true); + } + break; + case NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT]) { + auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::UV_ANIMATION_MASK_MAP); + if (itr != textureMaps.end()) { + if (itr->second->isDefined()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialUVAnimationMask, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } + wasSet = true; + } else { + multiMaterial.setTexturesLoading(true); + forceDefault = true; + } + } else { + forceDefault = true; + } + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT, true); + } + break; + case NetworkMToonMaterial::MToonFlagBit::MATCAP_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::MATCAP_VAL_BIT]) { + toonSchema._matcap = material->getMatcap(false); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::MATCAP_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_VAL_BIT]) { + toonSchema._parametricRim = material->getParametricRim(false); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_POWER_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_POWER_VAL_BIT]) { + toonSchema._parametricRimFresnelPower = material->getParametricRimFresnelPower(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_POWER_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_LIFT_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_LIFT_VAL_BIT]) { + toonSchema._parametricRimLift = material->getParametricRimLift(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_LIFT_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::RIM_LIGHTING_MIX_VAL_BIT: + if (materialKey._flags[NetworkMToonMaterial::MToonFlagBit::RIM_LIGHTING_MIX_VAL_BIT]) { + toonSchema._rimLightingMix = material->getRimLightingMix(); + schemaKey._flags.set(NetworkMToonMaterial::MToonFlagBit::RIM_LIGHTING_MIX_VAL_BIT, true); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::OUTLINE_WIDTH_MODE_VAL_BIT: + if (!fallthrough) { + multiMaterial.setOutlineWidthMode(material->getOutlineWidthMode()); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::OUTLINE_WIDTH_VAL_BIT: + if (!fallthrough) { + multiMaterial.setOutlineWidth(material->getOutlineWidth()); + wasSet = true; + } + break; + case NetworkMToonMaterial::MToonFlagBit::OUTLINE_VAL_BIT: + if (!fallthrough) { + multiMaterial.setOutline(material->getOutline(false)); + wasSet = true; + } + break; + default: + break; + } } if (wasSet) { @@ -704,71 +1133,115 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial 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::OPACITY_CUTOFF_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::Material::CULL_FACE_MODE: - multiMaterial.setCullFaceMode(graphics::Material::DEFAULT_CULL_FACE_MODE); - 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.isLightMap()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); - } - break; - case graphics::MaterialKey::LIGHT_MAP_BIT: - if (schemaKey.isLightMap()) { - drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); - } - break; - default: - break; + if (!multiMaterial.isMToon()) { + switch (flag) { + case graphics::Material::CULL_FACE_MODE: + multiMaterial.setCullFaceMode(graphics::Material::DEFAULT_CULL_FACE_MODE); + 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.isLightMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + } + break; + case graphics::MaterialKey::LIGHT_MAP_BIT: + if (schemaKey.isLightMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); + } + break; + default: + // everything else is initialized to the correct default values in Schema() + break; + } + } else { + switch (flag) { + case graphics::Material::CULL_FACE_MODE: + multiMaterial.setCullFaceMode(graphics::Material::DEFAULT_CULL_FACE_MODE); + break; + case graphics::MaterialKey::ALBEDO_MAP_BIT: + if (schemaKey.isAlbedoMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); + } + break; + case graphics::MaterialKey::NORMAL_MAP_BIT: + if (schemaKey.isNormalMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, textureCache->getBlueTexture()); + } + break; + case graphics::MaterialKey::EMISSIVE_MAP_BIT: + if (schemaKey.isEmissiveMap() && !schemaKey.isLightMap()) { + drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT: + if (schemaKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT]) { + drawMaterialTextures->setTexture(gr::Texture::MaterialShade, textureCache->getWhiteTexture()); + } + break; + case NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT: + if (schemaKey._flags[NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT]) { + drawMaterialTextures->setTexture(gr::Texture::MaterialShadingShift, textureCache->getBlackTexture()); + } + break; + case NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT: + if (schemaKey._flags[NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT]) { + drawMaterialTextures->setTexture(gr::Texture::MaterialMatcap, textureCache->getBlackTexture()); + } + break; + case NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT: + if (schemaKey._flags[NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT]) { + drawMaterialTextures->setTexture(gr::Texture::MaterialRim, textureCache->getWhiteTexture()); + } + break; + case NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT: + if (schemaKey._flags[NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT]) { + drawMaterialTextures->setTexture(gr::Texture::MaterialUVAnimationMask, textureCache->getWhiteTexture()); + } + break; + default: + // everything else is initialized to the correct default values in ToonSchema() + break; + } } } - schema._key = (uint32_t)schemaKey._flags.to_ulong(); - schemaBuffer.edit() = schema; + auto& schemaBuffer = multiMaterial.getSchemaBuffer(); + if (multiMaterial.isMToon()) { + toonSchema._key = (uint32_t)schemaKey._flags.to_ulong(); + schemaBuffer.edit() = toonSchema; + } else { + schema._key = (uint32_t)schemaKey._flags.to_ulong(); + schemaBuffer.edit() = schema; + } multiMaterial.setNeedsUpdate(false); multiMaterial.setInitialized(); } @@ -778,10 +1251,16 @@ bool RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu: updateMultiMaterial(multiMaterial); } + if (multiMaterial.isMToon()) { + multiMaterial.setMToonTime(); + } + auto textureCache = DependencyManager::get(); static gpu::TextureTablePointer defaultMaterialTextures = std::make_shared(); static gpu::BufferView defaultMaterialSchema; + static gpu::TextureTablePointer defaultMToonMaterialTextures = std::make_shared(); + static gpu::BufferView defaultMToonMaterialSchema; static std::once_flag once; std::call_once(once, [textureCache] { @@ -795,6 +1274,18 @@ bool RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu: defaultMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, textureCache->getWhiteTexture()); defaultMaterialTextures->setTexture(gr::Texture::MaterialScattering, textureCache->getWhiteTexture()); // MaterialEmissiveLightmap has to be set later + + graphics::MultiMaterial::MToonSchema toonSchema; + defaultMToonMaterialSchema = gpu::BufferView(std::make_shared(sizeof(toonSchema), (const gpu::Byte*) &toonSchema, sizeof(toonSchema))); + + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialNormal, textureCache->getBlueTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialShade, textureCache->getWhiteTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialShadingShift, textureCache->getBlackTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialMatcap, textureCache->getBlackTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialRim, textureCache->getWhiteTexture()); + defaultMToonMaterialTextures->setTexture(gr::Texture::MaterialUVAnimationMask, textureCache->getWhiteTexture()); }); // For shadows, we only need opacity mask information @@ -805,20 +1296,24 @@ bool RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu: if (enableTextures) { batch.setResourceTextureTable(multiMaterial.getTextureTable()); } else { - if (renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { - if (key.isLightMap()) { - defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); - } else if (key.isEmissiveMap()) { - defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + if (!multiMaterial.isMToon()) { + if (renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { + if (key.isLightMap()) { + defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); + } else if (key.isEmissiveMap()) { + defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + } } - } - batch.setResourceTextureTable(defaultMaterialTextures); + batch.setResourceTextureTable(defaultMaterialTextures); + } else { + batch.setResourceTextureTable(defaultMToonMaterialTextures); + } } return true; } else { - batch.setResourceTextureTable(defaultMaterialTextures); - batch.setUniformBuffer(gr::Buffer::Material, defaultMaterialSchema); + batch.setResourceTextureTable(!multiMaterial.isMToon() ? defaultMaterialTextures : defaultMToonMaterialTextures); + batch.setUniformBuffer(gr::Buffer::Material, !multiMaterial.isMToon() ? defaultMaterialSchema : defaultMToonMaterialSchema); return false; } } diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index dbfa23a143..fa2c6091dd 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -37,6 +37,7 @@ using namespace render; extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); +extern void sortAndRenderZPassShapes(const ShapePlumberPointer& shapePlumber, const render::RenderContextPointer& renderContext, const render::ShapeBounds& inShapes, render::ItemBounds &itemBounds); void RenderShadowTask::configure(const Config& configuration) { //DependencyManager::get()->setShadowMapEnabled(configuration.isEnabled()); @@ -253,58 +254,8 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat, false); - const std::vector keys = { - ShapeKey::Builder(), ShapeKey::Builder().withFade(), - ShapeKey::Builder().withDeformed(), ShapeKey::Builder().withDeformed().withFade(), - ShapeKey::Builder().withDeformed().withDualQuatSkinned(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withFade(), - ShapeKey::Builder().withOwnPipeline(), ShapeKey::Builder().withOwnPipeline().withFade(), - ShapeKey::Builder().withDeformed().withOwnPipeline(), ShapeKey::Builder().withDeformed().withOwnPipeline().withFade(), - ShapeKey::Builder().withDeformed().withDualQuatSkinned().withOwnPipeline(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withOwnPipeline().withFade(), - }; - std::vector> sortedShapeKeys(keys.size()); - - const int OWN_PIPELINE_INDEX = 6; - for (const auto& items : inShapes) { - int index = items.first.hasOwnPipeline() ? OWN_PIPELINE_INDEX : 0; - if (items.first.isDeformed()) { - index += 2; - if (items.first.isDualQuatSkinned()) { - index += 2; - } - } - - if (items.first.isFaded()) { - index += 1; - } - - sortedShapeKeys[index].push_back(items.first); - } - - // Render non-withOwnPipeline things - for (size_t i = 0; i < OWN_PIPELINE_INDEX; i++) { - auto& shapeKeys = sortedShapeKeys[i]; - if (shapeKeys.size() > 0) { - const auto& shapePipeline = _shapePlumber->pickPipeline(args, keys[i]); - args->_shapePipeline = shapePipeline; - for (const auto& key : shapeKeys) { - renderShapes(renderContext, _shapePlumber, inShapes.at(key)); - } - } - } - - // Render withOwnPipeline things - for (size_t i = OWN_PIPELINE_INDEX; i < keys.size(); i++) { - auto& shapeKeys = sortedShapeKeys[i]; - if (shapeKeys.size() > 0) { - args->_shapePipeline = nullptr; - for (const auto& key : shapeKeys) { - args->_itemShapeKey = key._flags.to_ulong(); - renderShapes(renderContext, _shapePlumber, inShapes.at(key)); - } - } - } - - args->_shapePipeline = nullptr; + render::ItemBounds itemBounds; + sortAndRenderZPassShapes(_shapePlumber, renderContext, inShapes, itemBounds); } args->_batch = nullptr; diff --git a/libraries/render-utils/src/model.slf b/libraries/render-utils/src/model.slf index 98abc29d8c..4b40aa171c 100644 --- a/libraries/render-utils/src/model.slf +++ b/libraries/render-utils/src/model.slf @@ -5,6 +5,7 @@ // // Created by Andrzej Kapolka on 5/6/14. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -15,8 +16,45 @@ <@include render-utils/ShaderConstants.h@> <@include CullFace.slh@> +<@if HIFI_USE_MTOON@> + <@if HIFI_USE_SHADOW@> + <$declareMToonMaterialTextures(ALBEDO)$> + <@else@> + <$declareMToonMaterialTextures(ALBEDO, HIFI_USE_NORMALMAP, SHADE, EMISSIVE, SHADING_SHIFT, MATCAP, RIM, UV_ANIMATION_MASK)$> + <@endif@> +<@else@> + <@if HIFI_USE_SHADOW or HIFI_USE_UNLIT@> + <$declareMaterialTextures(ALBEDO)$> + <@else@> + <@if not HIFI_USE_LIGHTMAP@> + <@if HIFI_USE_TRANSLUCENT@> + <$declareMaterialTextures(ALBEDO, ROUGHNESS, HIFI_USE_NORMALMAP, METALLIC, EMISSIVE, OCCLUSION)$> + <@else@> + <$declareMaterialTextures(ALBEDO, ROUGHNESS, HIFI_USE_NORMALMAP, METALLIC, EMISSIVE, OCCLUSION, SCATTERING)$> + <@endif@> + <@else@> + <$declareMaterialTextures(ALBEDO, ROUGHNESS, HIFI_USE_NORMALMAP, METALLIC)$> + <$declareMaterialLightmap()$> + <@endif@> + <@endif@> +<@endif@> + <@if not HIFI_USE_SHADOW@> - <@if HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> + <@if HIFI_USE_MTOON@> + + <@include DefaultMaterials.slh@> + <@include GlobalLight.slh@> + <$declareEvalGlobalLightingAlphaBlendedMToon()$> + + <@include gpu/Transform.slh@> + <$declareStandardCameraTransform()$> + + <@if HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> + layout(location=0) out vec4 _fragColor0; + <@else@> + <@include DeferredBufferWrite.slh@> + <@endif@> + <@elif HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> <@include DefaultMaterials.slh@> <@include GlobalLight.slh@> <@if HIFI_USE_LIGHTMAP@> @@ -31,6 +69,7 @@ <@endif@> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> + layout(location=0) out vec4 _fragColor0; <@else@> <@include DeferredBufferWrite.slh@> @@ -43,29 +82,6 @@ <@include LightingModel.slh@> <@endif@> -<@if HIFI_USE_SHADOW or HIFI_USE_UNLIT@> - <$declareMaterialTextures(ALBEDO)$> -<@else@> - <@if not HIFI_USE_LIGHTMAP@> - <@if HIFI_USE_NORMALMAP and HIFI_USE_TRANSLUCENT@> - <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL , METALLIC, EMISSIVE, OCCLUSION)$> - <@elif HIFI_USE_NORMALMAP@> - <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL , METALLIC, EMISSIVE, OCCLUSION, SCATTERING)$> - <@elif HIFI_USE_TRANSLUCENT@> - <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL , METALLIC, EMISSIVE, OCCLUSION)$> - <@else@> - <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL , METALLIC, EMISSIVE, OCCLUSION, SCATTERING)$> - <@endif@> - <@else@> - <@if HIFI_USE_NORMALMAP@> - <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC)$> - <@else@> - <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC)$> - <@endif@> - <$declareMaterialLightmap()$> - <@endif@> -<@endif@> - <@if HIFI_USE_FADE@> <@include Fade.slh@> <$declareFadeFragment()$> @@ -78,7 +94,9 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; <@if not HIFI_USE_SHADOW@> layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES; layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; - layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; + <@if not HIFI_USE_MTOON@> + layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; + <@endif@> <@if HIFI_USE_NORMALMAP@> layout(location=RENDER_UTILS_ATTR_TANGENT_WS) in vec3 _tangentWS; <@endif@> @@ -101,15 +119,21 @@ void main(void) { Material mat = getMaterial(); BITFIELD matKey = getMaterialKey(mat); <@if HIFI_USE_SHADOW or HIFI_USE_UNLIT@> + <@if not HIFI_USE_MTOON@> <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex)$> + <@else@> + <$fetchMToonMaterialTexturesCoord0(matKey, _texCoord0, albedoTex)$> + <@endif@> - <@if HIFI_USE_TRANSLUCENT@> float cutoff = getMaterialOpacityCutoff(mat); - float opacity = getMaterialOpacity(mat) * _color.a; + <@if HIFI_USE_TRANSLUCENT@> + float opacity = getMaterialOpacity(mat); + <@if not HIFI_USE_MTOON@> + opacity *= _color.a; + <@endif@> <$evalMaterialOpacity(albedoTex.a, cutoff, opacity, matKey, opacity)$>; <$discardInvisible(opacity)$>; <@else@> - float cutoff = getMaterialOpacityCutoff(mat); float opacity = 1.0; <$evalMaterialOpacityMask(albedoTex.a, cutoff, opacity, matKey, opacity)$>; <$discardTransparent(opacity)$>; @@ -118,7 +142,9 @@ void main(void) { <@if not HIFI_USE_SHADOW@> vec3 albedo = getMaterialAlbedo(mat); <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; - albedo *= _color.rgb; + <@if not HIFI_USE_MTOON@> + albedo *= _color.rgb; + <@endif@> <@if HIFI_USE_FADE@> albedo += fadeEmissive; <@endif@> @@ -134,6 +160,73 @@ void main(void) { opacity, albedo * isUnlitEnabled()); <@endif@> +<@elif HIFI_USE_MTOON@> + vec3 uvScrollSpeed = getMaterialUVScrollSpeed(mat); + float time = getMaterialTime(mat); + <@if HIFI_USE_NORMALMAP@> + <$fetchMToonMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, normalTex, shadeTex, emissiveTex, shadingShiftTex, rimTex, uvScrollSpeed, time)$> + <@else@> + <$fetchMToonMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, _SCRIBE_NULL, shadeTex, emissiveTex, shadingShiftTex, rimTex, uvScrollSpeed, time)$> + <@endif@> + + float cutoff = getMaterialOpacityCutoff(mat); + <@if HIFI_USE_TRANSLUCENT@> + float opacity = getMaterialOpacity(mat); + <$evalMaterialOpacity(albedoTex.a, cutoff, opacity, matKey, opacity)$>; + <$discardInvisible(opacity)$>; + <@else@> + float opacity = 1.0; + <$evalMaterialOpacityMask(albedoTex.a, cutoff, opacity, matKey, opacity)$>; + <$discardTransparent(opacity)$>; + <@endif@> + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + <@if HIFI_USE_NORMALMAP@> + vec3 fragNormalWS; + <$evalMaterialNormalLOD(_positionES, normalTex, _normalWS, _tangentWS, fragNormalWS)$> + <@else@> + vec3 fragNormalWS = _normalWS; + <@endif@> + fragNormalWS = evalFrontOrBackFaceNormal(normalize(fragNormalWS)); + + vec3 shade = getMaterialShade(mat); + <$evalMaterialShade(shadeTex, shade, matKey, shade)$>; + + float shadingShift = getMaterialShadingShift(mat); + <$evalMaterialShadingShift(shadingShiftTex, shadingShift, matKey, shadingShift)$>; + + TransformCamera cam = getTransformCamera(); + float metallic = DEFAULT_METALLIC; + vec3 fresnel = getFresnelF0(metallic, albedo); + + vec4 color = vec4(evalGlobalLightingAlphaBlendedMToon( + cam._viewInverse, 1.0, _positionES.xyz, fragNormalWS, + albedo, fresnel, metallic, emissive, DEFAULT_ROUGHNESS, opacity, + shade, shadingShift, getMaterialShadingToony(mat), getMaterialMatcap(mat), getMaterialParametricRim(mat), + getMaterialParametricRimFresnelPower(mat), getMaterialParametricRimLift(mat), rimTex, getMaterialRimLightingMix(mat), matKey), opacity); + + <@if HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> + _fragColor0 = isUnlitEnabled() * vec4(color.rgb + <@if HIFI_USE_FADE@> + + fadeEmissive + <@endif@> + , color.a); + <@else@> + packDeferredFragmentUnlit( + fragNormalWS, + 1.0, + color.rgb + <@if HIFI_USE_FADE@> + + fadeEmissive + <@endif@> + ); + <@endif@> + <@else@> <@if not HIFI_USE_LIGHTMAP@> <@if HIFI_USE_NORMALMAP and HIFI_USE_TRANSLUCENT@> @@ -154,14 +247,13 @@ void main(void) { <@endif@> <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmap)$> <@endif@> - - <@if HIFI_USE_TRANSLUCENT@> + float cutoff = getMaterialOpacityCutoff(mat); + <@if HIFI_USE_TRANSLUCENT@> float opacity = getMaterialOpacity(mat) * _color.a; <$evalMaterialOpacity(albedoTex.a, cutoff, opacity, matKey, opacity)$>; <$discardInvisible(opacity)$>; <@else@> - float cutoff = getMaterialOpacityCutoff(mat); float opacity = 1.0; <$evalMaterialOpacityMask(albedoTex.a, cutoff, opacity, matKey, opacity)$>; <$discardTransparent(opacity)$>; diff --git a/libraries/render-utils/src/model.slv b/libraries/render-utils/src/model.slv index 319711eac2..848acfc331 100644 --- a/libraries/render-utils/src/model.slv +++ b/libraries/render-utils/src/model.slv @@ -5,6 +5,7 @@ // // Created by Hifi Engine Team // Copyright 2019 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -47,7 +48,9 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; <@if not HIFI_USE_SHADOW@> layout(location=RENDER_UTILS_ATTR_POSITION_ES) out vec4 _positionES; layout(location=RENDER_UTILS_ATTR_NORMAL_WS) out vec3 _normalWS; - layout(location=RENDER_UTILS_ATTR_COLOR) out vec4 _color; + <@if not HIFI_USE_MTOON@> + layout(location=RENDER_UTILS_ATTR_COLOR) out vec4 _color; + <@endif@> <@if HIFI_USE_NORMALMAP@> layout(location=RENDER_UTILS_ATTR_TANGENT_WS) out vec3 _tangentWS; <@endif@> @@ -96,7 +99,9 @@ void main(void) { _texCoord01 = vec4(0.0); } <@else@> +<@if not HIFI_USE_MTOON@> _color = color_sRGBAToLinear(inColor); +<@endif@> TexMapArray texMapArray = getTexMapArray(); <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _positionWS, _texCoord01.xy)$> diff --git a/libraries/render-utils/src/render-utils/model.slp b/libraries/render-utils/src/render-utils/model.slp index b63ec898eb..e69518f8fd 100644 --- a/libraries/render-utils/src/render-utils/model.slp +++ b/libraries/render-utils/src/render-utils/model.slp @@ -1 +1 @@ -DEFINES (normalmap translucent:f unlit:f/lightmap:f)/shadow fade:f/forward:f deformed:v/deformeddq:v \ No newline at end of file +DEFINES (normalmap translucent:f unlit:f/lightmap:f)/shadow mtoon fade:f/forward:f deformed:v/deformeddq:v \ No newline at end of file diff --git a/libraries/render/src/render/HighlightStyle.h b/libraries/render/src/render/HighlightStyle.h index 8bef5c33c3..795253cc2f 100644 --- a/libraries/render/src/render/HighlightStyle.h +++ b/libraries/render/src/render/HighlightStyle.h @@ -3,6 +3,7 @@ // Created by Olivier Prat on 11/06/2017. // Copyright 2017 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -12,6 +13,7 @@ #define hifi_render_utils_HighlightStyle_h #include +#include #include @@ -21,23 +23,59 @@ namespace render { class HighlightStyle { public: struct RGBA { - glm::vec3 color{ 1.0f, 0.7f, 0.2f }; - float alpha{ 0.9f }; + glm::vec3 color { 1.0f, 0.7f, 0.2f }; + float alpha { 0.9f }; RGBA(const glm::vec3& c, float a) : color(c), alpha(a) {} + + std::string toString() const { return glm::to_string(color) + " " + std::to_string(alpha); } }; - RGBA _outlineUnoccluded{ { 1.0f, 0.7f, 0.2f }, 0.9f }; - RGBA _outlineOccluded{ { 1.0f, 0.7f, 0.2f }, 0.9f }; - RGBA _fillUnoccluded{ { 0.2f, 0.7f, 1.0f }, 0.0f }; - RGBA _fillOccluded{ { 0.2f, 0.7f, 1.0f }, 0.0f }; + RGBA _outlineUnoccluded { { 1.0f, 0.7f, 0.2f }, 0.9f }; + RGBA _outlineOccluded { { 1.0f, 0.7f, 0.2f }, 0.9f }; + RGBA _fillUnoccluded { { 0.2f, 0.7f, 1.0f }, 0.0f }; + RGBA _fillOccluded { { 0.2f, 0.7f, 1.0f }, 0.0f }; - float _outlineWidth{ 2.0f }; - bool _isOutlineSmooth{ false }; + float _outlineWidth { 2.0f }; + bool _isOutlineSmooth { false }; bool isFilled() const { return _fillUnoccluded.alpha > 5e-3f || _fillOccluded.alpha > 5e-3f; } + + std::string toString() const { + return _outlineUnoccluded.toString() + _outlineOccluded.toString() + _fillUnoccluded.toString() + + _fillOccluded.toString() + std::to_string(_outlineWidth) + std::to_string(_isOutlineSmooth); + } + + static HighlightStyle calculateOutlineStyle(uint8_t mode, float outlineWidth, const glm::vec3& outline, + const glm::vec3& position, const ViewFrustum& viewFrustum, size_t screenHeight) { + HighlightStyle style; + style._outlineUnoccluded.color = outline; + style._outlineUnoccluded.alpha = 1.0f; + style._outlineOccluded.alpha = 0.0f; + style._fillUnoccluded.alpha = 0.0f; + style._fillOccluded.alpha = 0.0f; + style._isOutlineSmooth = false; + + if (mode == 1) { // OUTLINE_WORLD + // FIXME: this is a hacky approximation, which gives us somewhat accurate widths with distance based falloff. + // Our outline implementation doesn't support the necessary vertex based extrusion to do real world based outlines. + glm::vec4 viewPos = glm::inverse(viewFrustum.getView()) * glm::vec4(position, 1.0f); + + const glm::mat4& projection = viewFrustum.getProjection(); + glm::vec4 p1 = projection * (viewPos + glm::vec4(0.0f, 0.5f * outlineWidth, 0.0f, 0.0f)); + p1 /= p1.w; + glm::vec4 p2 = projection * (viewPos - glm::vec4(0.0f, 0.5f * outlineWidth, 0.0f, 0.0f)); + p2 /= p2.w; + + style._outlineWidth = 0.5 * screenHeight * fabs(p1.y - p2.y); + } else { // OUTLINE_SCREEN + style._outlineWidth = outlineWidth * screenHeight; + } + + return style; + } }; } diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index 369f227566..62b3b20523 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 1/26/16. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -160,4 +161,11 @@ namespace render { } return payload->passesZoneOcclusionTest(containingZones); } -} + + template <> HighlightStyle payloadGetOutlineStyle(const PayloadProxyInterface::Pointer& payload, const ViewFrustum& viewFrustum, const size_t height) { + if (!payload) { + return HighlightStyle(); + } + return payload->getOutlineStyle(viewFrustum, height); + } +} \ No newline at end of file diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 5952be8a84..25663899d1 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 1/26/16. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -29,6 +30,7 @@ #include "ShapePipeline.h" #include "BlendshapeConstants.h" +#include "HighlightStyle.h" namespace render { @@ -104,15 +106,17 @@ public: META_CULL_GROUP, // As a meta item, the culling of my sub items is based solely on my bounding box and my visibility in the view SUB_META_CULLED, // As a sub item of a meta render item set as cull group, need to be set to my culling to the meta render it - FIRST_TAG_BIT, // 8 Tags available to organize the items and filter them against + FIRST_TAG_BIT, // 8 Tags available to organize the items and filter them against LAST_TAG_BIT = FIRST_TAG_BIT + NUM_TAGS, - FIRST_LAYER_BIT, // 8 Exclusive Layers (encoded in 3 bits) available to organize the items in layers, an item can only belong to ONE layer + FIRST_LAYER_BIT, // 8 Exclusive Layers (encoded in 3 bits) available to organize the items in layers, an item can only belong to ONE layer LAST_LAYER_BIT = FIRST_LAYER_BIT + NUM_LAYER_BITS, + OUTLINE, // Item has an outline + __SMALLER, // Reserved bit for spatialized item to indicate that it is smaller than expected in the cell in which it belongs (probably because it overlaps over several smaller cells) - NUM_FLAGS, // Not a valid flag + NUM_FLAGS, // Not a valid flag }; typedef std::bitset Flags; @@ -170,6 +174,9 @@ public: Builder& withLayer(uint8_t layer) { _flags = evalLayerBitsWithKeyBits(layer, _flags.to_ulong()); return (*this); } Builder& withoutLayer() { return withLayer(LAYER_DEFAULT); } + Builder& withOutline() { _flags.set(OUTLINE); return (*this); } + Builder& withoutOutline() { _flags.reset(OUTLINE); return (*this); } + // Convenient standard keys that we will keep on using all over the place static Builder opaqueShape() { return Builder().withTypeShape(); } static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); } @@ -213,6 +220,8 @@ public: bool isLayered() const { return getLayer() != LAYER_DEFAULT; } bool isSpatial() const { return !isLayered(); } + bool isOutline() const { return _flags[OUTLINE]; } + // Probably not public, flags used by the scene bool isSmall() const { return _flags[__SMALLER]; } void setSmaller(bool smaller) { (smaller ? _flags.set(__SMALLER) : _flags.reset(__SMALLER)); } @@ -284,6 +293,9 @@ public: Builder& withoutLayered() { _value = ItemKey::evalLayerBitsWithKeyBits(ItemKey::LAYER_DEFAULT, _value.to_ulong()); _mask |= ItemKey::KEY_LAYER_BITS_MASK; return (*this); } Builder& withLayer(uint8_t layer) { _value = ItemKey::evalLayerBitsWithKeyBits(layer, _value.to_ulong()); _mask |= ItemKey::KEY_LAYER_BITS_MASK; return (*this); } + Builder& withoutOutline() { _value.reset(ItemKey::OUTLINE); _mask.set(ItemKey::OUTLINE); return (*this); } + Builder& withOutline() { _value.set(ItemKey::OUTLINE); _mask.set(ItemKey::OUTLINE); return (*this); } + Builder& withNothing() { _value.reset(); _mask.reset(); return (*this); } // Convenient standard keys that we will keep on using all over the place @@ -440,6 +452,8 @@ public: virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const = 0; + virtual HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const = 0; + ~PayloadInterface() {} // Status interface is local to the base class @@ -493,6 +507,8 @@ public: bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const { return _payload->passesZoneOcclusionTest(containingZones); } + HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const { return _payload->getOutlineStyle(viewFrustum, height); } + // Access the status const StatusPointer& getStatus() const { return _payload->getStatus(); } @@ -547,6 +563,12 @@ template uint32_t metaFetchMetaSubItems(const std::shared_ptr& payl // Allows payloads to determine if they should render or not, based on the zones that contain the current camera template bool payloadPassesZoneOcclusionTest(const std::shared_ptr& payloadData, const std::unordered_set& containingZones) { return true; } +// Outline Interface +// Allows payloads to specify an outline style +template HighlightStyle payloadGetOutlineStyle(const std::shared_ptr& payloadData, const ViewFrustum& viewFrustum, const size_t height) { + return HighlightStyle(); +} + // THe Payload class is the real Payload to be used // THis allow anything to be turned into a Payload as long as the required interface functions are available // When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff" @@ -573,6 +595,8 @@ public: virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const override { return payloadPassesZoneOcclusionTest(_data, containingZones); } + virtual HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const override { return payloadGetOutlineStyle(_data, viewFrustum, height); } + protected: DataPointer _data; @@ -628,6 +652,7 @@ public: virtual void render(RenderArgs* args) = 0; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const = 0; virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const = 0; + virtual HighlightStyle getOutlineStyle(const ViewFrustum& viewFrustum, const size_t height) const = 0; // FIXME: this isn't the best place for this since it's only used for ModelEntities, but currently all Entities use PayloadProxyInterface virtual void handleBlendedVertices(int blendshapeNumber, const QVector& blendshapeOffsets, @@ -640,6 +665,7 @@ template <> void payloadRender(const PayloadProxyInterface::Pointer& payload, Re template <> uint32_t metaFetchMetaSubItems(const PayloadProxyInterface::Pointer& payload, ItemIDs& subItems); template <> const ShapeKey shapeGetShapeKey(const PayloadProxyInterface::Pointer& payload); template <> bool payloadPassesZoneOcclusionTest(const PayloadProxyInterface::Pointer& payload, const std::unordered_set& containingZones); +template <> HighlightStyle payloadGetOutlineStyle(const PayloadProxyInterface::Pointer& payload, const ViewFrustum& viewFrustum, const size_t height); typedef Item::PayloadPointer PayloadPointer; typedef std::vector Payloads; diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index b2656a597f..f30fbc155e 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -4,6 +4,7 @@ // // Created by Zach Pomerantz on 12/22/2016. // Copyright 2016 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -35,18 +36,20 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin const auto nonspatialSelection = task.addJob("FetchLayeredSelection", nonspatialFilter); // Multi filter visible items into different buckets - const int NUM_SPATIAL_FILTERS = 4; + const int NUM_SPATIAL_FILTERS = 5; const int NUM_NON_SPATIAL_FILTERS = 3; const int OPAQUE_SHAPE_BUCKET = 0; const int TRANSPARENT_SHAPE_BUCKET = 1; const int LIGHT_BUCKET = 2; const int META_BUCKET = 3; + const int OUTLINE_BUCKET = 4; const int BACKGROUND_BUCKET = 2; MultiFilterItems::ItemFilterArray spatialFilters = { { ItemFilter::Builder::opaqueShape(), ItemFilter::Builder::transparentShape(), ItemFilter::Builder::light(), - ItemFilter::Builder::meta() + ItemFilter::Builder::meta(), + ItemFilter::Builder().withVisible().withOutline() } }; MultiFilterItems::ItemFilterArray nonspatialFilters = { { ItemFilter::Builder::opaqueShape(), @@ -76,7 +79,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin task.addJob("ClearContainingZones"); - output = Output(BucketList{ opaques, transparents, lights, metas, + output = Output(BucketList{ opaques, transparents, lights, metas, filteredSpatialBuckets[OUTLINE_BUCKET], filteredLayeredOpaque.getN(0), filteredLayeredTransparent.getN(0), filteredLayeredOpaque.getN(1), filteredLayeredTransparent.getN(1), background }, spatialSelection); diff --git a/libraries/render/src/render/RenderFetchCullSortTask.h b/libraries/render/src/render/RenderFetchCullSortTask.h index 0b475614a1..bb01a81d81 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -4,6 +4,7 @@ // // Created by Zach Pomerantz on 12/22/2016. // Copyright 2016 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -23,6 +24,7 @@ public: TRANSPARENT_SHAPE, LIGHT, META, + OUTLINE, LAYER_FRONT_OPAQUE_SHAPE, LAYER_FRONT_TRANSPARENT_SHAPE, LAYER_HUD_OPAQUE_SHAPE, diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 5500183196..f09a646ce6 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 1/11/15. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -608,6 +609,22 @@ void Scene::resetSelections(const Transaction::SelectionResets& transactions) { } } +void Scene::addItemToSelection(const std::string& selectionName, ItemID itemID) { + std::unique_lock lock(_selectionsMutex); + auto found = _selections.find(selectionName); + if (found == _selections.end()) { + Selection selection = Selection(selectionName, { itemID }); + _selections[selectionName] = selection; + } else { + _selections[selectionName].add(itemID); + } +} + +void Scene::removeSelection(const std::string& selectionName) { + std::unique_lock lock(_selectionsMutex); + _selections.erase(selectionName); +} + // Access a particular Stage (empty if doesn't exist) // Thread safe StagePointer Scene::getStage(const Stage::Name& name) const { diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 1bc282646b..19d56b61d2 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 1/11/15. // Copyright 2014 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -166,6 +167,14 @@ public: // Thread safe bool isSelectionEmpty(const Selection::Name& name) const; + // Add a single item to a selection by name + // Thread safe + void addItemToSelection(const std::string& selectionName, ItemID itemID); + + // Remove a selection by name + // Thread safe + void removeSelection(const std::string& selectionName); + // This next call are NOT threadsafe, you have to call them from the correct thread to avoid any potential issues // Access a particular item from its ID @@ -194,6 +203,8 @@ public: void setItemTransition(ItemID id, Index transitionId); void removeItemTransition(ItemID id); + HighlightStyle getOutlineStyle(ItemID id, const ViewFrustum& viewFrustum, uint16_t height) { return _items[id].getOutlineStyle(viewFrustum, height); } + protected: // Thread safe elements that can be accessed from anywhere diff --git a/libraries/render/src/render/Selection.h b/libraries/render/src/render/Selection.h index 05b2395b42..0e3ef0eb77 100644 --- a/libraries/render/src/render/Selection.h +++ b/libraries/render/src/render/Selection.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 4/4/2017. // Copyright 2017 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -37,7 +38,8 @@ namespace render { // Test if the ID is in the selection, return the index or -1 if not present static const int NOT_FOUND{ -1 }; - + + void add(ItemID id) { _items.push_back(id); } int find(ItemID id) const; bool contains(ItemID id) const { return find(id) > NOT_FOUND; } diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index 04b9919140..8e3505b2e5 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -4,6 +4,7 @@ // // Created by Zach Pomerantz on 12/31/15. // Copyright 2015 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -38,6 +39,7 @@ public: FADE, CULL_FACE_NONE, // if neither of these are set, we're CULL_FACE_BACK CULL_FACE_FRONT, + MTOON, OWN_PIPELINE, INVALID, @@ -84,6 +86,7 @@ public: Builder& withDepthBias() { _flags.set(DEPTH_BIAS); return (*this); } Builder& withWireframe() { _flags.set(WIREFRAME); return (*this); } Builder& withFade() { _flags.set(FADE); return (*this); } + Builder& withMToon() { _flags.set(MTOON); return (*this); } Builder& withoutCullFace() { return withCullFaceMode(graphics::MaterialKey::CullFaceMode::CULL_NONE); } Builder& withCullFaceMode(graphics::MaterialKey::CullFaceMode cullFaceMode) { @@ -184,6 +187,9 @@ public: Builder& withFade() { _flags.set(FADE); _mask.set(FADE); return (*this); } Builder& withoutFade() { _flags.reset(FADE); _mask.set(FADE); return (*this); } + Builder& withMToon() { _flags.set(MTOON); _mask.set(MTOON); return (*this); } + Builder& withoutMToon() { _flags.reset(MTOON); _mask.set(MTOON); return (*this); } + Builder& withCustom(uint8_t custom) { _flags &= (~CUSTOM_MASK); _flags |= (custom << CUSTOM_0); _mask |= (CUSTOM_MASK); return (*this); } Builder& withoutCustom() { _flags &= (~CUSTOM_MASK); _mask |= (CUSTOM_MASK); return (*this); } @@ -211,6 +217,7 @@ public: bool isWireframe() const { return _flags[WIREFRAME]; } bool isCullFace() const { return !_flags[CULL_FACE_NONE] && !_flags[CULL_FACE_FRONT]; } bool isFaded() const { return _flags[FADE]; } + bool isMToon() const { return _flags[MTOON]; } bool hasOwnPipeline() const { return _flags[OWN_PIPELINE]; } bool isValid() const { return !_flags[INVALID]; } @@ -250,6 +257,7 @@ inline QDebug operator<<(QDebug debug, const ShapeKey& key) { << "isWireframe:" << key.isWireframe() << "isCullFace:" << key.isCullFace() << "isFaded:" << key.isFaded() + << "isMToon:" << key.isMToon() << "]"; } } else {