Add CastShadow and ReceiveShadow for Model and ModelNode 61/307061/3
authorSeungho Baek <sbsh.baek@samsung.com>
Mon, 4 Mar 2024 09:08:27 +0000 (18:08 +0900)
committerSeungho Baek <sbsh.baek@samsung.com>
Fri, 8 Mar 2024 04:27:43 +0000 (13:27 +0900)
 - 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 <sbsh.baek@samsung.com>
automated-tests/src/dali-scene3d/utc-Dali-Model.cpp
dali-scene3d/internal/controls/model/model-impl.cpp
dali-scene3d/internal/controls/model/model-impl.h
dali-scene3d/internal/graphics/shaders/default-physically-based-shader.frag
dali-scene3d/internal/graphics/shaders/shadow-map-shader.frag
dali-scene3d/internal/model-components/model-node-impl.cpp
dali-scene3d/internal/model-components/model-node-impl.h
dali-scene3d/public-api/controls/model/model.cpp
dali-scene3d/public-api/controls/model/model.h
dali-scene3d/public-api/model-components/model-node.cpp
dali-scene3d/public-api/model-components/model-node.h

index 3ba8059..6b6670c 100644 (file)
@@ -1988,4 +1988,106 @@ int UtcDaliModelMaterialUniformChange(void)
   DALI_TEST_EQUALS(gl.CheckUniformValue<float>(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<int>(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<int>(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<int>(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<int>(shadowCastingIndex), 0, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(modelNode2.GetProperty<int>(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<int>(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<int>(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<int>(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<int>(shadowReceivingIndex), 0, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(modelNode2.GetProperty<int>(shadowReceivingIndex2), 0, TEST_LOCATION);
+  END_TEST;
 }
\ No newline at end of file
index e3e5b91..f012aa6 100644 (file)
@@ -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)
index 5a9633b..d82fa8f 100644 (file)
@@ -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
index 3b0eb21..47bb831 100644 (file)
@@ -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)
index 685c602..7d72c36 100644 (file)
@@ -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
index 1eb07db..3338069 100644 (file)
@@ -80,6 +80,9 @@ void ModelNode::Initialize()
 
 void ModelNode::OnInitialize()
 {
+  Actor self = Self();
+  self.RegisterProperty("uIsShadowCasting", static_cast<int>(mIsShadowCasting));
+  self.RegisterProperty("uIsShadowReceiving", static_cast<int>(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<int>(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<int>(mIsShadowReceiving));
+}
+
+bool ModelNode::IsShadowReceiving() const
+{
+  return mIsShadowReceiving;
+}
+
 void ModelNode::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
 {
   mDiffuseTexture       = diffuseTexture;
index 0996155..2d8e31d 100644 (file)
@@ -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
 };
 
index c6beb61..eda0041 100644 (file)
@@ -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();
index 6ae5d61..88bc524 100644 (file)
@@ -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<std::string>& 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<ModelNode>& 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:
index a93d71e..b1bfa45 100644 (file)
@@ -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
index 993d9dd..ab8856e 100644 (file)
@@ -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
   /**