From 7892a19ca3a420eb54bcafa007d82bb5fe44c55c Mon Sep 17 00:00:00 2001 From: Seungho Baek Date: Mon, 4 Mar 2024 18:08:27 +0900 Subject: [PATCH] Add CastShadow and ReceiveShadow for Model and ModelNode - CastShadow can be used to make an object cast shadow or not. - ReceiveShadow can be used to make an object be not drawn any shadow on its surface. - Model and ModelNode have the methods. - If Model's method is called, it is inherited to its current child ModelNode. - If ModelNode's method is called, it is only affects the ModelNode itself. - The ModelNode's property changes do not affect its parent Model's property. Change-Id: I8ebeeed9ed814718a38ad7f69c43a4814a5a70ae Signed-off-by: Seungho Baek --- .../src/dali-scene3d/utc-Dali-Model.cpp | 102 +++++++++++++++++++++ .../internal/controls/model/model-impl.cpp | 67 +++++++++++++- dali-scene3d/internal/controls/model/model-impl.h | 32 +++++++ .../shaders/default-physically-based-shader.frag | 3 +- .../graphics/shaders/shadow-map-shader.frag | 7 ++ .../internal/model-components/model-node-impl.cpp | 39 ++++++++ .../internal/model-components/model-node-impl.h | 22 +++++ dali-scene3d/public-api/controls/model/model.cpp | 20 ++++ dali-scene3d/public-api/controls/model/model.h | 47 +++++++++- .../public-api/model-components/model-node.cpp | 20 ++++ .../public-api/model-components/model-node.h | 37 ++++++++ 11 files changed, 390 insertions(+), 6 deletions(-) diff --git a/automated-tests/src/dali-scene3d/utc-Dali-Model.cpp b/automated-tests/src/dali-scene3d/utc-Dali-Model.cpp index 3ba8059..6b6670c 100644 --- a/automated-tests/src/dali-scene3d/utc-Dali-Model.cpp +++ b/automated-tests/src/dali-scene3d/utc-Dali-Model.cpp @@ -1988,4 +1988,106 @@ int UtcDaliModelMaterialUniformChange(void) DALI_TEST_EQUALS(gl.CheckUniformValue(Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), expectIblFactor), true, TEST_LOCATION); END_TEST; +} + +int UtcDaliModelCastShadow(void) +{ + ToolkitTestApplication application; + + Scene3D::Model model = Scene3D::Model::New(); + application.GetScene().Add(model); + + Scene3D::ModelNode modelNode = Scene3D::ModelNode::New(); + model.AddModelNode(modelNode); + + DALI_TEST_EQUALS(model.IsShadowCasting(), true, TEST_LOCATION); + + DALI_TEST_EQUALS(modelNode.IsShadowCasting(), true, TEST_LOCATION); + + auto shadowCastingIndex = modelNode.GetPropertyIndex("uIsShadowCasting"); + DALI_TEST_EQUALS(modelNode.GetProperty(shadowCastingIndex), 1, TEST_LOCATION); + + model.CastShadow(false); + + DALI_TEST_EQUALS(model.IsShadowCasting(), false, TEST_LOCATION); + + DALI_TEST_EQUALS(modelNode.IsShadowCasting(), false, TEST_LOCATION); + + DALI_TEST_EQUALS(modelNode.GetProperty(shadowCastingIndex), 0, TEST_LOCATION); + + Scene3D::ModelNode modelNode2 = Scene3D::ModelNode::New(); + model.AddModelNode(modelNode2); + + DALI_TEST_EQUALS(modelNode2.IsShadowCasting(), true, TEST_LOCATION); + + auto shadowCastingIndex2 = modelNode2.GetPropertyIndex("uIsShadowCasting"); + DALI_TEST_EQUALS(modelNode2.GetProperty(shadowCastingIndex2), 1, TEST_LOCATION); + + modelNode.CastShadow(true); + + DALI_TEST_EQUALS(modelNode.IsShadowCasting(), true, TEST_LOCATION); + + DALI_TEST_EQUALS(model.IsShadowCasting(), false, TEST_LOCATION); + + model.CastShadow(false); + + DALI_TEST_EQUALS(model.IsShadowCasting(), false, TEST_LOCATION); + + DALI_TEST_EQUALS(modelNode.IsShadowCasting(), false, TEST_LOCATION); + + DALI_TEST_EQUALS(modelNode.GetProperty(shadowCastingIndex), 0, TEST_LOCATION); + + DALI_TEST_EQUALS(modelNode2.GetProperty(shadowCastingIndex2), 0, TEST_LOCATION); + END_TEST; +} + +int UtcDaliModelReceiveShadow(void) +{ + ToolkitTestApplication application; + + Scene3D::Model model = Scene3D::Model::New(); + application.GetScene().Add(model); + + Scene3D::ModelNode modelNode = Scene3D::ModelNode::New(); + model.AddModelNode(modelNode); + + DALI_TEST_EQUALS(model.IsShadowReceiving(), true, TEST_LOCATION); + + DALI_TEST_EQUALS(modelNode.IsShadowReceiving(), true, TEST_LOCATION); + + auto shadowReceivingIndex = modelNode.GetPropertyIndex("uIsShadowReceiving"); + DALI_TEST_EQUALS(modelNode.GetProperty(shadowReceivingIndex), 1, TEST_LOCATION); + + model.ReceiveShadow(false); + + DALI_TEST_EQUALS(model.IsShadowReceiving(), false, TEST_LOCATION); + + DALI_TEST_EQUALS(modelNode.IsShadowReceiving(), false, TEST_LOCATION); + + DALI_TEST_EQUALS(modelNode.GetProperty(shadowReceivingIndex), 0, TEST_LOCATION); + + Scene3D::ModelNode modelNode2 = Scene3D::ModelNode::New(); + model.AddModelNode(modelNode2); + + DALI_TEST_EQUALS(modelNode2.IsShadowReceiving(), true, TEST_LOCATION); + + auto shadowReceivingIndex2 = modelNode2.GetPropertyIndex("uIsShadowReceiving"); + DALI_TEST_EQUALS(modelNode2.GetProperty(shadowReceivingIndex2), 1, TEST_LOCATION); + + modelNode.ReceiveShadow(true); + + DALI_TEST_EQUALS(modelNode.IsShadowReceiving(), true, TEST_LOCATION); + + DALI_TEST_EQUALS(model.IsShadowReceiving(), false, TEST_LOCATION); + + model.ReceiveShadow(false); + + DALI_TEST_EQUALS(model.IsShadowReceiving(), false, TEST_LOCATION); + + DALI_TEST_EQUALS(modelNode.IsShadowReceiving(), false, TEST_LOCATION); + + DALI_TEST_EQUALS(modelNode.GetProperty(shadowReceivingIndex), 0, TEST_LOCATION); + + DALI_TEST_EQUALS(modelNode2.GetProperty(shadowReceivingIndex2), 0, TEST_LOCATION); + END_TEST; } \ No newline at end of file diff --git a/dali-scene3d/internal/controls/model/model-impl.cpp b/dali-scene3d/internal/controls/model/model-impl.cpp index e3e5b91..f012aa6 100644 --- a/dali-scene3d/internal/controls/model/model-impl.cpp +++ b/dali-scene3d/internal/controls/model/model-impl.cpp @@ -265,7 +265,9 @@ Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUr mIblDiffuseResourceReady(true), mIblSpecularResourceReady(true), mIblDiffuseDirty(false), - mIblSpecularDirty(false) + mIblSpecularDirty(false), + mIsShadowCasting(true), + mIsShadowReceiving(true) { } @@ -810,6 +812,29 @@ void Model::SetMotionData(Scene3D::MotionData motionData) } } +void Model::CastShadow(bool castShadow) +{ + mIsShadowCasting = castShadow; + UpdateCastShadowRecursively(mModelRoot, mIsShadowCasting); +} + +bool Model::IsShadowCasting() const +{ + return mIsShadowCasting; +} + +void Model::ReceiveShadow(bool receiveShadow) +{ + mIsShadowReceiving = receiveShadow; + UpdateReceiveShadowRecursively(mModelRoot, mIsShadowReceiving); +} + +bool Model::IsShadowReceiving() const +{ + return mIsShadowReceiving; +} + + /////////////////////////////////////////////////////////// // // Private methods @@ -982,6 +1007,46 @@ void Model::FitModelPosition() mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot); } +void Model::UpdateCastShadowRecursively(Scene3D::ModelNode node, bool castShadow) +{ + if(!node) + { + return; + } + + GetImplementation(node).CastShadow(castShadow); + uint32_t childrenCount = node.GetChildCount(); + for(uint32_t i = 0; i < childrenCount; ++i) + { + Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i)); + if(!childNode) + { + continue; + } + UpdateCastShadowRecursively(childNode, castShadow); + } +} + +void Model::UpdateReceiveShadowRecursively(Scene3D::ModelNode node, bool receiveShadow) +{ + if(!node) + { + return; + } + + GetImplementation(node).ReceiveShadow(receiveShadow); + uint32_t childrenCount = node.GetChildCount(); + for(uint32_t i = 0; i < childrenCount; ++i) + { + Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i)); + if(!childNode) + { + continue; + } + UpdateReceiveShadowRecursively(childNode, receiveShadow); + } +} + void Model::UpdateImageBasedLightTextureRecursively(Scene3D::ModelNode node, Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels) { if(!node) diff --git a/dali-scene3d/internal/controls/model/model-impl.h b/dali-scene3d/internal/controls/model/model-impl.h index 5a9633b..d82fa8f 100644 --- a/dali-scene3d/internal/controls/model/model-impl.h +++ b/dali-scene3d/internal/controls/model/model-impl.h @@ -174,6 +174,26 @@ public: void SetMotionData(Scene3D::MotionData motionData); /** + * @copydoc Model::CastShadow() + */ + void CastShadow(bool castShadow); + + /** + * @copydoc Model::IsShadowCasting() + */ + bool IsShadowCasting() const; + + /** + * @copydoc Model::ReceiveShadow() + */ + void ReceiveShadow(bool receiveShadow); + + /** + * @copydoc Model::IsShadowReceiving() + */ + bool IsShadowReceiving() const; + + /** * @copydoc Scene3D::Model::MeshHitSignal() */ Scene3D::Model::MeshHitSignalType& MeshHitSignal() @@ -295,6 +315,16 @@ private: void FitModelPosition(); /** + * @brief Makes the input node cast shadow or not. + */ + void UpdateCastShadowRecursively(Scene3D::ModelNode node, bool castShadow); + + /** + * @brief Makes the input node receive shadow or not. + */ + void UpdateReceiveShadowRecursively(Scene3D::ModelNode node, bool receiveShadow); + + /** * @brief Changes IBL information of the input node. */ void UpdateImageBasedLightTextureRecursively(Scene3D::ModelNode node, Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels); @@ -444,6 +474,8 @@ private: bool mIblSpecularResourceReady; bool mIblDiffuseDirty; bool mIblSpecularDirty; + bool mIsShadowCasting; + bool mIsShadowReceiving; }; } // namespace Internal diff --git a/dali-scene3d/internal/graphics/shaders/default-physically-based-shader.frag b/dali-scene3d/internal/graphics/shaders/default-physically-based-shader.frag index 3b0eb21..47bb831 100644 --- a/dali-scene3d/internal/graphics/shaders/default-physically-based-shader.frag +++ b/dali-scene3d/internal/graphics/shaders/default-physically-based-shader.frag @@ -96,6 +96,7 @@ uniform mediump vec3 uLightColor[MAX_LIGHTS]; // For Shadow Map uniform lowp int uIsShadowEnabled; uniform sampler2D sShadowMap; +uniform lowp int uIsShadowReceiving; #ifdef SL_VERSION_LOW uniform int uShadowMapWidth; uniform int uShadowMapHeight; @@ -300,7 +301,7 @@ void main() } } - if(float(uIsShadowEnabled) * uShadowIntensity > 0.0) + if(float(uIsShadowReceiving) * float(uIsShadowEnabled) * uShadowIntensity > 0.0) { mediump float exposureFactor = 0.0; if(uEnableShadowSoftFiltering > 0) diff --git a/dali-scene3d/internal/graphics/shaders/shadow-map-shader.frag b/dali-scene3d/internal/graphics/shaders/shadow-map-shader.frag index 685c602..7d72c36 100644 --- a/dali-scene3d/internal/graphics/shaders/shadow-map-shader.frag +++ b/dali-scene3d/internal/graphics/shaders/shadow-map-shader.frag @@ -17,6 +17,8 @@ uniform sampler2D sAlbedoAlpha; uniform sampler2D sAlbedoMetal; #endif +uniform int uIsShadowCasting; + lowp vec3 linear(lowp vec3 color) { return pow(color, vec3(2.2)); @@ -24,6 +26,11 @@ lowp vec3 linear(lowp vec3 color) void main() { + if(uIsShadowCasting == 0) + { + discard; + } + #ifdef THREE_TEX // The albedo may be defined from a base texture or a flat color #ifdef BASECOLOR_TEX diff --git a/dali-scene3d/internal/model-components/model-node-impl.cpp b/dali-scene3d/internal/model-components/model-node-impl.cpp index 1eb07db..3338069 100644 --- a/dali-scene3d/internal/model-components/model-node-impl.cpp +++ b/dali-scene3d/internal/model-components/model-node-impl.cpp @@ -80,6 +80,9 @@ void ModelNode::Initialize() void ModelNode::OnInitialize() { + Actor self = Self(); + self.RegisterProperty("uIsShadowCasting", static_cast(mIsShadowCasting)); + self.RegisterProperty("uIsShadowReceiving", static_cast(mIsShadowReceiving)); } void ModelNode::OnSceneConnection(int depth) @@ -299,6 +302,42 @@ void ModelNode::SetShadowMapTexture(Dali::Texture shadowMapTexture) } } +void ModelNode::CastShadow(bool castShadow) +{ + if(mIsShadowCasting == castShadow) + { + return; + } + + mIsShadowCasting = castShadow; + + Actor self = Self(); + self.RegisterProperty("uIsShadowCasting", static_cast(mIsShadowCasting)); +} + +bool ModelNode::IsShadowCasting() const +{ + return mIsShadowCasting; +} + +void ModelNode::ReceiveShadow(bool receiveShadow) +{ + if(mIsShadowReceiving == receiveShadow) + { + return; + } + + mIsShadowReceiving = receiveShadow; + + Actor self = Self(); + self.RegisterProperty("uIsShadowReceiving", static_cast(mIsShadowReceiving)); +} + +bool ModelNode::IsShadowReceiving() const +{ + return mIsShadowReceiving; +} + void ModelNode::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels) { mDiffuseTexture = diffuseTexture; diff --git a/dali-scene3d/internal/model-components/model-node-impl.h b/dali-scene3d/internal/model-components/model-node-impl.h index 0996155..2d8e31d 100644 --- a/dali-scene3d/internal/model-components/model-node-impl.h +++ b/dali-scene3d/internal/model-components/model-node-impl.h @@ -242,6 +242,26 @@ public: // Public Method void SetShadowMapTexture(Dali::Texture shadowMapTexture); /** + * @copydoc Dali::Scene3D::ModelNode::CastShadow() + */ + void CastShadow(bool castShadow); + + /** + * @copydoc Dali::Scene3D::ModelNode::IsShadowCasting() + */ + bool IsShadowCasting() const; + + /** + * @copydoc Dali::Scene3D::ModelNode::ReceiveShadow() + */ + void ReceiveShadow(bool receiveShadow); + + /** + * @copydoc Dali::Scene3D::ModelNode::IsShadowReceiving() + */ + bool IsShadowReceiving() const; + + /** * @brief Sets the diffuse and specular image-based lighting textures for a ModelNode. * * @param[in] diffuseTexture The diffuse texture. @@ -345,6 +365,8 @@ private: float mIblScaleFactor{1.0f}; uint32_t mSpecularMipmapLevels{1u}; + bool mIsShadowCasting{true}; + bool mIsShadowReceiving{true}; /// @endcond }; diff --git a/dali-scene3d/public-api/controls/model/model.cpp b/dali-scene3d/public-api/controls/model/model.cpp index c6beb61..eda0041 100644 --- a/dali-scene3d/public-api/controls/model/model.cpp +++ b/dali-scene3d/public-api/controls/model/model.cpp @@ -162,6 +162,26 @@ void Model::SetMotionData(MotionData motionData) GetImpl(*this).SetMotionData(motionData); } +void Model::CastShadow(bool castShadow) +{ + GetImpl(*this).CastShadow(castShadow); +} + +bool Model::IsShadowCasting() const +{ + return GetImpl(*this).IsShadowCasting(); +} + +void Model::ReceiveShadow(bool receiveShadow) +{ + GetImpl(*this).ReceiveShadow(receiveShadow); +} + +bool Model::IsShadowReceiving() const +{ + return GetImpl(*this).IsShadowReceiving(); +} + Model::MeshHitSignalType& Model::MeshHitSignal() { return GetImpl(*this).MeshHitSignal(); diff --git a/dali-scene3d/public-api/controls/model/model.h b/dali-scene3d/public-api/controls/model/model.h index 6ae5d61..88bc524 100644 --- a/dali-scene3d/public-api/controls/model/model.h +++ b/dali-scene3d/public-api/controls/model/model.h @@ -335,7 +335,7 @@ public: ModelNode FindChildModelNodeByName(std::string_view nodeName); /** - * @brief Retrieve the list of blendshape name that current Model hold. + * @brief Retrieves the list of blendshape name that current Model hold. * The name will be appended end of input list. * * @SINCE_2_2.34 @@ -345,7 +345,7 @@ public: void RetrieveBlendShapeNames(std::vector& blendShapeNames) const; /** - * @brief Retrieve the list of ModelNode that contains given blend shape name. + * @brief Retrieves the list of ModelNode that contains given blend shape name. * The ModelNode will be appended end of input list. * * @SINCE_2_2.34 @@ -356,7 +356,7 @@ public: void RetrieveModelNodesByBlendShapeName(std::string_view blendShapeName, std::vector& modelNodes) const; /** - * @brief Generate specific animation of this Model by inputed MotionData. + * @brief Generates specific animation of this Model by inputed MotionData. * * @SINCE_2_2.34 * @param[in] motionData the data of motion animation. @@ -366,7 +366,7 @@ public: Dali::Animation GenerateMotionDataAnimation(MotionData motionData); /** - * @brief Set specific values of this Model by inputed MotionData. + * @brief Sets specific values of this Model by inputed MotionData. * @note If MotionValue's ValueType is ValueType::KEY_FRAMES, the last value will be set. * * @SINCE_2_2.34 @@ -376,6 +376,45 @@ public: void SetMotionData(Scene3D::MotionData motionData); /** + * @brief Sets whether this Model casts shadow or not. + * If it is true, this model is drawn on Shadow Map. + * + * @SINCE_2_3.99 + * @param[in] castShadow Whether this Model casts shadow or not. + * @note This method affects all of the child ModelNode. + * However, same property of each child ModelNode can be changed respectively and it not changes parent's property. + */ + void CastShadow(bool castShadow); + + /** + * @brief Retrieves whether the Model casts shadow or not for Light. + * + * @SINCE_2_3.99 + * @return True if this model casts shadow. + * @note IBL does not cast any shadow. + */ + bool IsShadowCasting() const; + + /** + * @brief Sets whether this Model receives shadow or not. + * If it is true, shadows are drawn on this model. + * + * @SINCE_2_3.99 + * @param[in] receiveShadow Whether this Model receives shadow or not. + * @note This method affects all of the child ModelNode. + * However, same property of each child ModelNode can be changed respectively and it not changes parent's property. + */ + void ReceiveShadow(bool receiveShadow); + + /** + * @brief Retrieves whether the Model receives shadow or not for Light. + * + * @SINCE_2_3.99 + * @return True if this model receives shadow. + */ + bool IsShadowReceiving() const; + + /** * @brief This signal is emitted when the collider mesh is touched/hit. * * A callback of the following type may be connected: diff --git a/dali-scene3d/public-api/model-components/model-node.cpp b/dali-scene3d/public-api/model-components/model-node.cpp index a93d71e..b1bfa45 100644 --- a/dali-scene3d/public-api/model-components/model-node.cpp +++ b/dali-scene3d/public-api/model-components/model-node.cpp @@ -143,6 +143,26 @@ bool ModelNode::HasColliderMesh() const return Internal::GetImplementation(*this).HasColliderMesh(); } +void ModelNode::CastShadow(bool castShadow) +{ + Internal::GetImplementation(*this).CastShadow(castShadow); +} + +bool ModelNode::IsShadowCasting() const +{ + return Internal::GetImplementation(*this).IsShadowCasting(); +} + +void ModelNode::ReceiveShadow(bool receiveShadow) +{ + Internal::GetImplementation(*this).ReceiveShadow(receiveShadow); +} + +bool ModelNode::IsShadowReceiving() const +{ + return Internal::GetImplementation(*this).IsShadowReceiving(); +} + } // namespace Scene3D } // namespace Dali diff --git a/dali-scene3d/public-api/model-components/model-node.h b/dali-scene3d/public-api/model-components/model-node.h index 993d9dd..ab8856e 100644 --- a/dali-scene3d/public-api/model-components/model-node.h +++ b/dali-scene3d/public-api/model-components/model-node.h @@ -239,6 +239,43 @@ public: // Public Method */ [[nodiscard]] bool HasColliderMesh() const; + /** + * @brief Sets whether this ModelNode casts shadow or not. + * If it is true, this ModelNode is drawn on Shadow Map. + * + * @SINCE_2_3.99 + * @param[in] castShadow Whether this ModelNode casts shadow or not. + * @note This method affects only for this ModelNode. + */ + void CastShadow(bool castShadow); + + /** + * @brief Retrieves whether the ModelNode cast shadow or not for Light. + * + * @SINCE_2_3.99 + * @return True if this ModelNode cast shadow. + * @note IBL does not cast any shadow. + */ + bool IsShadowCasting() const; + + /** + * @brief Sets whether this ModelNode receives shadow or not. + * If it is true, shadows are drawn on this ModelNode. + * + * @SINCE_2_3.99 + * @param[in] receiveShadow Whether this ModelNode receives shadow or not. + * @note This method affects only for this ModelNode. + */ + void ReceiveShadow(bool receiveShadow); + + /** + * @brief Retrieves whether the ModelNode receives shadow or not for Light. + * + * @SINCE_2_3.99 + * @return True if this ModelNode receives shadow. + */ + bool IsShadowReceiving() const; + public: // Not intended for application developers /// @cond internal /** -- 2.7.4