Add CastShadow and ReceiveShadow for Model and ModelNode
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / internal / controls / model / model-impl.cpp
index b0f8294..f012aa6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <filesystem>
 
 // INTERNAL INCLUDES
+#include <dali-scene3d/internal/common/image-resource-loader.h>
 #include <dali-scene3d/internal/common/model-cache-manager.h>
 #include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
+#include <dali-scene3d/internal/event/collider-mesh-processor.h>
+#include <dali-scene3d/internal/light/light-impl.h>
 #include <dali-scene3d/internal/model-components/model-node-impl.h>
 #include <dali-scene3d/public-api/controls/model/model.h>
 #include <dali-scene3d/public-api/loader/animation-definition.h>
@@ -41,8 +44,9 @@
 #include <dali-scene3d/public-api/loader/load-result.h>
 #include <dali-scene3d/public-api/loader/node-definition.h>
 #include <dali-scene3d/public-api/loader/scene-definition.h>
-#include <dali-scene3d/public-api/loader/shader-definition-factory.h>
-
+#include <dali-scene3d/public-api/loader/shader-manager.h>
+#include <dali-scene3d/public-api/model-motion/motion-index/blend-shape-index.h>
+#include <dali-toolkit/public-api/controls/control-impl.h>
 using namespace Dali;
 
 namespace Dali
@@ -117,8 +121,7 @@ void ConfigureBlendShapeShaders(
   Dali::Scene3D::Loader::ResourceBundle& resources, const Dali::Scene3D::Loader::SceneDefinition& scene, Actor root, std::vector<Dali::Scene3D::Loader::BlendshapeShaderConfigurationRequest>&& requests)
 {
   std::vector<std::string> errors;
-  auto                     onError = [&errors](const std::string& msg)
-  { errors.push_back(msg); };
+  auto                     onError = [&errors](const std::string& msg) { errors.push_back(msg); };
   if(!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError))
   {
     Dali::Scene3D::Loader::ExceptionFlinger flinger(ASSERT_LOCATION);
@@ -171,6 +174,77 @@ void AddModelTreeToAABB(BoundingVolume& AABB, const Dali::Scene3D::Loader::Scene
   }
 }
 
+void UpdateBlendShapeNodeMapRecursively(Model::BlendShapeModelNodeMap& resultMap, const Scene3D::ModelNode& node)
+{
+  if(!node)
+  {
+    return;
+  }
+  const auto childCount = node.GetChildCount();
+  for(auto i = 0u; i < childCount; ++i)
+  {
+    UpdateBlendShapeNodeMapRecursively(resultMap, Scene3D::ModelNode::DownCast(node.GetChildAt(i)));
+  }
+
+  std::vector<std::string> blendShapeNames;
+  node.RetrieveBlendShapeNames(blendShapeNames);
+  for(const auto& iter : blendShapeNames)
+  {
+    // Append or create new list.
+    resultMap[iter].push_back(node);
+  }
+}
+
+void UpdateShaderRecursively(Scene3D::ModelNode node, Scene3D::Loader::ShaderManagerPtr shaderManager)
+{
+  if(!node)
+  {
+    return;
+  }
+
+  GetImplementation(node).UpdateShader(shaderManager);
+
+  uint32_t childrenCount = node.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
+  {
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(childNode)
+    {
+      UpdateShaderRecursively(childNode, shaderManager);
+    }
+  }
+}
+
+void UpdateShadowMapTextureRecursively(Scene3D::ModelNode node, Dali::Texture shadowMapTexture)
+{
+  if(!node)
+  {
+    return;
+  }
+
+  GetImplementation(node).SetShadowMapTexture(shadowMapTexture);
+
+  uint32_t childrenCount = node.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
+  {
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(childNode)
+    {
+      UpdateShadowMapTextureRecursively(childNode, shadowMapTexture);
+    }
+  }
+}
+
+void ResetResourceTask(IntrusivePtr<AsyncTask>&& asyncTask)
+{
+  if(!asyncTask)
+  {
+    return;
+  }
+  Dali::AsyncTaskManager::Get().RemoveTask(asyncTask);
+  asyncTask.Reset();
+}
+
 } // anonymous namespace
 
 Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
@@ -178,6 +252,7 @@ Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUr
   mModelUrl(modelUrl),
   mResourceDirectoryUrl(resourceDirectoryUrl),
   mModelRoot(),
+  mShaderManager(new Scene3D::Loader::ShaderManager()),
   mNaturalSize(Vector3::ZERO),
   mModelPivot(AnchorPoint::CENTER),
   mSceneIblScaleFactor(1.0f),
@@ -190,18 +265,20 @@ Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUr
   mIblDiffuseResourceReady(true),
   mIblSpecularResourceReady(true),
   mIblDiffuseDirty(false),
-  mIblSpecularDirty(false)
+  mIblSpecularDirty(false),
+  mIsShadowCasting(true),
+  mIsShadowReceiving(true)
 {
 }
 
 Model::~Model()
 {
+  ResetResourceTasks();
+
   if(ModelCacheManager::Get() && !mModelUrl.empty())
   {
     ModelCacheManager::Get().UnreferenceModelCache(mModelUrl);
   }
-
-  ResetResourceTasks();
 }
 
 Dali::Scene3D::Model Model::New(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
@@ -235,22 +312,66 @@ void Model::AddModelNode(Scene3D::ModelNode modelNode)
     mModelResourceReady = true;
   }
 
+  UpdateShaderRecursively(modelNode, mShaderManager);
+
+  if(mShadowMapTexture)
+  {
+    UpdateShadowMapTextureRecursively(modelNode, mShadowMapTexture);
+  }
+
   if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
   {
     UpdateImageBasedLightTexture();
     UpdateImageBasedLightScaleFactor();
   }
 
+  GetImplementation(modelNode).SetRootModel(this);
+
+  // If model has a collider mesh set, add it to the container
+  if(modelNode.HasColliderMesh())
+  {
+    RegisterColliderMesh(modelNode);
+    Scene3D::ColliderMeshProcessor::Get().ColliderMeshChanged(Scene3D::Model::DownCast(Self()));
+  }
+
   if(Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
   {
     NotifyResourceReady();
   }
 }
 
+void Model::RegisterColliderMesh(Scene3D::ModelNode& modelNode)
+{
+  mColliderMeshes[modelNode.GetProperty<int>(Actor::Property::ID)] = modelNode;
+
+  // Add processor
+  Scene3D::ColliderMeshProcessor::Get().ColliderMeshChanged(Scene3D::Model::DownCast(Self()));
+}
+
+void Model::RemoveColliderMesh(Scene3D::ModelNode& node)
+{
+  auto id   = node.GetProperty<int>(Actor::Property::ID);
+  auto iter = std::find_if(mColliderMeshes.begin(), mColliderMeshes.end(), [id](auto& item) {
+    return item.first == id;
+  });
+  if(iter != mColliderMeshes.end())
+  {
+    mColliderMeshes.erase(iter);
+  }
+}
+
 void Model::RemoveModelNode(Scene3D::ModelNode modelNode)
 {
+  // remove collider mesh from the list if node is being removed
+  if(modelNode.HasColliderMesh())
+  {
+    RemoveColliderMesh(modelNode);
+    GetImplementation(modelNode).SetRootModel(nullptr);
+  }
+
   if(mModelRoot)
   {
+    UpdateShaderRecursively(modelNode, nullptr);
     mModelRoot.Remove(modelNode);
   }
 }
@@ -337,6 +458,9 @@ void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::s
     mDiffuseTexture.Reset();
     mSpecularTexture.Reset();
     UpdateImageBasedLightTexture();
+
+    // Request image resource GC
+    Dali::Scene3D::Internal::ImageResourceLoader::RequestGarbageCollect();
   }
   else
   {
@@ -346,6 +470,9 @@ void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::s
       mIblDiffuseLoadTask = new EnvironmentMapLoadTask(mDiffuseIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, MakeCallback(this, &Model::OnIblDiffuseLoadComplete));
       Dali::AsyncTaskManager::Get().AddTask(mIblDiffuseLoadTask);
       mIblDiffuseDirty = false;
+
+      // Request image resource GC
+      Dali::Scene3D::Internal::ImageResourceLoader::RequestGarbageCollect();
     }
 
     if(isOnScene && mIblSpecularDirty)
@@ -354,6 +481,9 @@ void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::s
       mIblSpecularLoadTask = new EnvironmentMapLoadTask(mSpecularIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, MakeCallback(this, &Model::OnIblSpecularLoadComplete));
       Dali::AsyncTaskManager::Get().AddTask(mIblSpecularLoadTask);
       mIblSpecularDirty = false;
+
+      // Request image resource GC
+      Dali::Scene3D::Internal::ImageResourceLoader::RequestGarbageCollect();
     }
   }
 
@@ -458,6 +588,253 @@ Scene3D::ModelNode Model::FindChildModelNodeByName(std::string_view nodeName)
   return Scene3D::ModelNode::DownCast(childActor);
 }
 
+void Model::RetrieveBlendShapeNames(std::vector<std::string>& blendShapeNames) const
+{
+  blendShapeNames.reserve(blendShapeNames.size() + mBlendShapeModelNodeMap.size());
+  for(const auto& iter : mBlendShapeModelNodeMap)
+  {
+    blendShapeNames.push_back(iter.first);
+  }
+}
+
+void Model::RetrieveModelNodesByBlendShapeName(std::string_view blendShapeName, std::vector<Scene3D::ModelNode>& modelNodes) const
+{
+  auto iter = mBlendShapeModelNodeMap.find(std::string(blendShapeName));
+  if(iter != mBlendShapeModelNodeMap.end())
+  {
+    const auto& modelNodeList = iter->second;
+    modelNodes.reserve(modelNodes.size() + modelNodeList.size());
+    for(const auto& nodeIter : modelNodeList)
+    {
+      modelNodes.push_back(nodeIter);
+    }
+  }
+}
+
+Dali::Animation Model::GenerateMotionDataAnimation(Scene3D::MotionData motionData)
+{
+  Dali::Animation animation;
+
+  // TODO : Need to collect duplicated codes with SetMotionData()
+
+  if(motionData)
+  {
+    const uint32_t motionCount = motionData.GetMotionCount();
+    for(uint32_t i = 0u; i < motionCount; ++i)
+    {
+      auto motionIndex = motionData.GetIndex(i);
+      auto motionValue = motionData.GetValue(i);
+      if(motionIndex && motionValue)
+      {
+        if(motionIndex.GetModelNodeId() != Property::INVALID_KEY)
+        {
+          Scene3D::ModelNode modelNode;
+          if(motionIndex.GetModelNodeId().type == Property::Key::Type::STRING)
+          {
+            modelNode = FindChildModelNodeByName(motionIndex.GetModelNodeId().stringKey);
+          }
+          else if(motionIndex.GetModelNodeId().type == Property::Key::Type::INDEX)
+          {
+            // TODO : Not implement yet.
+          }
+
+          if(modelNode)
+          {
+            KeyFrames keyFrames = motionValue.GetKeyFrames();
+
+            if(keyFrames)
+            {
+              // Try to use index first. If failed, try to use name
+              Property::Index animatedPropertyIndex = motionIndex.GetPropertyIndex(modelNode);
+              if(animatedPropertyIndex != Property::INVALID_INDEX)
+              {
+                if(DALI_UNLIKELY(!animation))
+                {
+                  animation = Animation::New(motionData.GetDuration());
+                }
+                animation.AnimateBetween(Dali::Property(modelNode, animatedPropertyIndex), keyFrames);
+              }
+              else
+              {
+                std::string    animatedPropertyName = motionIndex.GetPropertyName(modelNode);
+                Dali::Property property(modelNode, animatedPropertyName);
+                if(property.propertyIndex != Property::INVALID_INDEX)
+                {
+                  if(DALI_UNLIKELY(!animation))
+                  {
+                    animation = Animation::New(motionData.GetDuration());
+                  }
+                  animation.AnimateBetween(property, keyFrames);
+                }
+              }
+            }
+          }
+        }
+        else
+        {
+          Scene3D::BlendShapeIndex blendShapeIndex = Scene3D::BlendShapeIndex::DownCast(motionIndex);
+          if(blendShapeIndex && blendShapeIndex.GetBlendShapeId().type == Property::Key::Type::STRING)
+          {
+            // Special case : For BlendShapeIndex that doesn't have ModelNodeId and has string BlendShapeId,
+            // we need to animate all kind of blendshapes
+
+            KeyFrames keyFrames = motionValue.GetKeyFrames();
+
+            if(keyFrames)
+            {
+              std::vector<Scene3D::ModelNode> modelNodes;
+              RetrieveModelNodesByBlendShapeName(blendShapeIndex.GetBlendShapeId().stringKey, modelNodes);
+
+              for(auto& modelNode : modelNodes)
+              {
+                // Try to use index first. If failed, try to use name
+                Property::Index animatedPropertyIndex = motionIndex.GetPropertyIndex(modelNode);
+                if(animatedPropertyIndex != Property::INVALID_INDEX)
+                {
+                  if(DALI_UNLIKELY(!animation))
+                  {
+                    animation = Animation::New(motionData.GetDuration());
+                  }
+                  animation.AnimateBetween(Dali::Property(modelNode, animatedPropertyIndex), keyFrames);
+                }
+                else
+                {
+                  std::string    animatedPropertyName = motionIndex.GetPropertyName(modelNode);
+                  Dali::Property property(modelNode, animatedPropertyName);
+
+                  if(property.propertyIndex != Property::INVALID_INDEX)
+                  {
+                    if(DALI_UNLIKELY(!animation))
+                    {
+                      animation = Animation::New(motionData.GetDuration());
+                    }
+                    animation.AnimateBetween(property, keyFrames);
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  return animation;
+}
+
+void Model::SetMotionData(Scene3D::MotionData motionData)
+{
+  // TODO : Need to collect duplicated codes with GenerateMotionDataAnimation()
+
+  if(motionData)
+  {
+    const uint32_t motionCount = motionData.GetMotionCount();
+    for(uint32_t i = 0u; i < motionCount; ++i)
+    {
+      auto motionIndex = motionData.GetIndex(i);
+      auto motionValue = motionData.GetValue(i);
+      if(motionIndex && motionValue)
+      {
+        if(motionIndex.GetModelNodeId() != Property::INVALID_KEY)
+        {
+          Scene3D::ModelNode modelNode;
+          if(motionIndex.GetModelNodeId().type == Property::Key::Type::STRING)
+          {
+            modelNode = FindChildModelNodeByName(motionIndex.GetModelNodeId().stringKey);
+          }
+          else if(motionIndex.GetModelNodeId().type == Property::Key::Type::INDEX)
+          {
+            // TODO : Not implement yet.
+          }
+
+          if(modelNode)
+          {
+            Property::Value value = motionValue.GetPropertyValue();
+
+            if(value.GetType() != Property::Type::NONE)
+            {
+              // Try to use index first. If failed, try to use name
+              Property::Index propertyIndex = motionIndex.GetPropertyIndex(modelNode);
+              if(propertyIndex != Property::INVALID_INDEX)
+              {
+                modelNode.SetProperty(propertyIndex, value);
+              }
+              else
+              {
+                std::string    propertyName = motionIndex.GetPropertyName(modelNode);
+                Dali::Property property(modelNode, propertyName);
+                if(property.propertyIndex != Property::INVALID_INDEX)
+                {
+                  modelNode.SetProperty(property.propertyIndex, value);
+                }
+              }
+            }
+          }
+        }
+        else
+        {
+          Scene3D::BlendShapeIndex blendShapeIndex = Scene3D::BlendShapeIndex::DownCast(motionIndex);
+          if(blendShapeIndex && blendShapeIndex.GetBlendShapeId().type == Property::Key::Type::STRING)
+          {
+            // Special case : For BlendShapeIndex that doesn't have ModelNodeId and has string BlendShapeId,
+            // we need to animate all kind of blendshapes
+
+            Property::Value value = motionValue.GetPropertyValue();
+
+            if(value.GetType() != Property::Type::NONE)
+            {
+              std::vector<Scene3D::ModelNode> modelNodes;
+              RetrieveModelNodesByBlendShapeName(blendShapeIndex.GetBlendShapeId().stringKey, modelNodes);
+
+              for(auto& modelNode : modelNodes)
+              {
+                // Try to use index first. If failed, try to use name
+                Property::Index propertyIndex = motionIndex.GetPropertyIndex(modelNode);
+                if(propertyIndex != Property::INVALID_INDEX)
+                {
+                  modelNode.SetProperty(propertyIndex, value);
+                }
+                else
+                {
+                  std::string    propertyName = motionIndex.GetPropertyName(modelNode);
+                  Dali::Property property(modelNode, propertyName);
+                  if(property.propertyIndex != Property::INVALID_INDEX)
+                  {
+                    modelNode.SetProperty(property.propertyIndex, value);
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+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
@@ -467,10 +844,43 @@ void Model::OnInitialize()
 {
   // Make ParentOrigin as Center.
   Self().SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+
+  mDefaultDiffuseTexture  = ImageResourceLoader::GetEmptyTextureWhiteRGB();
+  mDefaultSpecularTexture = ImageResourceLoader::GetEmptyTextureWhiteRGB();
 }
 
 void Model::OnSceneConnection(int depth)
 {
+  Actor parent = Self().GetParent();
+  while(parent)
+  {
+    // If this Model has parent SceneView and the its ShaderManager is same with privious ShaderManager,
+    // this Model don't need to update shader.
+    Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(parent);
+    if(sceneView)
+    {
+      mParentSceneView = sceneView;
+      GetImpl(sceneView).RegisterSceneItem(this);
+      Scene3D::Loader::ShaderManagerPtr shaderManager = GetImpl(sceneView).GetShaderManager();
+      if(mShaderManager != shaderManager)
+      {
+        mShaderManager = shaderManager;
+        UpdateShaderRecursively(mModelRoot, mShaderManager);
+      }
+      break;
+    }
+    parent = parent.GetParent();
+  }
+
+  // Model can be added on Dali::Scene directly without SceneView.
+  // So, Model's mShaderManager and shaders of child ModelNodes are needed to be reset when this Model has not parent SceneView.
+  Scene3D::SceneView parentSceneView = mParentSceneView.GetHandle();
+  if(!parentSceneView)
+  {
+    mShaderManager = new Dali::Scene3D::Loader::ShaderManager();
+    UpdateShaderRecursively(mModelRoot, mShaderManager);
+  }
+
   if(!mModelLoadTask && !mModelResourceReady && !mModelUrl.empty())
   {
     // Request model load only if we setup url.
@@ -488,19 +898,6 @@ void Model::OnSceneConnection(int depth)
     SetImageBasedLightSource(mDiffuseIblUrl, mSpecularIblUrl, mIblScaleFactor);
   }
 
-  Actor parent = Self().GetParent();
-  while(parent)
-  {
-    Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(parent);
-    if(sceneView)
-    {
-      GetImpl(sceneView).RegisterSceneItem(this);
-      mParentSceneView = sceneView;
-      break;
-    }
-    parent = parent.GetParent();
-  }
-
   NotifyResourceReady();
 
   mSizeNotification = Self().AddPropertyNotification(Actor::Property::SIZE, StepCondition(SIZE_STEP_CONDITION));
@@ -510,8 +907,11 @@ void Model::OnSceneConnection(int depth)
 
 void Model::OnSceneDisconnection()
 {
+  // If mParentSceneView is still onScene, that means this model
+  // is disconnected from mParentSceneView's sub tree.
+  // So, Unregister this Model from SceneView.
   Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
-  if(sceneView)
+  if(sceneView && sceneView.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
   {
     GetImpl(sceneView).UnregisterSceneItem(this);
     mParentSceneView.Reset();
@@ -607,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)
@@ -634,7 +1074,6 @@ void Model::UpdateImageBasedLightScaleFactorRecursively(Scene3D::ModelNode node,
     return;
   }
 
-  node.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), iblScaleFactor);
   GetImplementation(node).SetImageBasedLightScaleFactor(iblScaleFactor);
 
   uint32_t childrenCount = node.GetChildCount();
@@ -718,6 +1157,15 @@ void Model::ApplyCameraTransform(Dali::CameraActor camera) const
   camera.SetProperty(Actor::Property::SCALE, resultScale);
 }
 
+void Model::NotifyShadowMapTexture(Dali::Texture shadowMapTexture)
+{
+  if(mShadowMapTexture != shadowMapTexture)
+  {
+    mShadowMapTexture = shadowMapTexture;
+    UpdateShadowMapTextureRecursively(mModelRoot, mShadowMapTexture);
+  }
+}
+
 void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor, uint32_t specularMipmapLevels)
 {
   if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
@@ -745,6 +1193,8 @@ void Model::NotifyImageBasedLightScaleFactor(float scaleFactor)
 
 void Model::OnModelLoadComplete()
 {
+  IntrusivePtr<Model> self = this; // Keep reference until this API finished
+
   if(!mModelLoadTask->HasSucceeded())
   {
     ResetResourceTasks();
@@ -769,17 +1219,27 @@ void Model::OnModelLoadComplete()
   ResetCameraParameters();
   if(!resources.mEnvironmentMaps.empty())
   {
-    mDefaultDiffuseTexture  = resources.mEnvironmentMaps.front().second.mDiffuse;
-    mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular;
+    if(resources.mEnvironmentMaps.front().second.mDiffuse)
+    {
+      mDefaultDiffuseTexture = resources.mEnvironmentMaps.front().second.mDiffuse;
+    }
+    if(resources.mEnvironmentMaps.front().second.mSpecular)
+    {
+      mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular;
+    }
   }
 
+  if(mShadowMapTexture)
+  {
+    UpdateShadowMapTextureRecursively(mModelRoot, mShadowMapTexture);
+  }
   UpdateImageBasedLightTexture();
   UpdateImageBasedLightScaleFactor();
   Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
 
   mModelResourceReady = true;
-  NotifyResourceReady();
   ResetResourceTask(mModelLoadTask);
+  NotifyResourceReady();
 }
 
 void Model::OnIblDiffuseLoadComplete()
@@ -827,51 +1287,44 @@ void Model::ResetResourceTasks()
   ResetResourceTask(mIblSpecularLoadTask);
 }
 
-void Model::ResetResourceTask(IntrusivePtr<AsyncTask> asyncTask)
-{
-  if(!asyncTask)
-  {
-    return;
-  }
-  Dali::AsyncTaskManager::Get().RemoveTask(asyncTask);
-  asyncTask.Reset();
-}
-
 void Model::NotifyResourceReady()
 {
   if(!IsResourceReady())
   {
     return;
   }
-  Control::SetResourceReady(false);
+  Control::SetResourceReady();
 }
 
 void Model::CreateModel()
 {
-  BoundingVolume                                      AABB;
-  auto&                                               resources       = mModelLoadTask->GetResources();
-  auto&                                               scene           = mModelLoadTask->GetScene();
-  auto&                                               resourceChoices = mModelLoadTask->GetResourceChoices();
-  Dali::Scene3D::Loader::Transforms                   xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
-  Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, {}, {}, {}};
+  BoundingVolume                    AABB;
+  auto&                             resources       = mModelLoadTask->GetResources();
+  auto&                             scene           = mModelLoadTask->GetScene();
+  auto&                             resourceChoices = mModelLoadTask->GetResourceChoices();
+  Dali::Scene3D::Loader::Transforms xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
+
+  Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, mShaderManager, {}, {}, {}};
 
   // Generate Dali handles from resource bundle. Note that we generate all scene's resouce immediatly.
   resources.GenerateResources();
   for(auto iRoot : scene.GetRoots())
   {
-    if(auto actor = scene.CreateNodes(iRoot, resourceChoices, nodeParams))
+    if(auto modelNode = scene.CreateNodes(iRoot, resourceChoices, nodeParams))
     {
-      scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
-      ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
+      scene.ConfigureSkinningShaders(resources, modelNode, std::move(nodeParams.mSkinnables));
+      ConfigureBlendShapeShaders(resources, scene, modelNode, std::move(nodeParams.mBlendshapeRequests));
 
-      scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
+      scene.ApplyConstraints(modelNode, std::move(nodeParams.mConstrainables));
 
-      mModelRoot.Add(actor);
+      mModelRoot.Add(modelNode);
     }
 
     AddModelTreeToAABB(AABB, scene, resourceChoices, iRoot, nodeParams, Matrix::IDENTITY);
   }
 
+  UpdateBlendShapeNodeMap();
+
   mNaturalSize = AABB.CalculateSize();
   mModelPivot  = AABB.CalculatePivot();
   mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
@@ -889,8 +1342,7 @@ void Model::CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene)
   mAnimations.clear();
   if(!mModelLoadTask->GetAnimations().empty())
   {
-    auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property)
-    {
+    auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property) {
       if(property.mNodeIndex == Scene3D::Loader::INVALID_INDEX)
       {
         return mModelRoot.FindChildByName(property.mNodeName);
@@ -921,6 +1373,14 @@ void Model::ResetCameraParameters()
   }
 }
 
+void Model::UpdateBlendShapeNodeMap()
+{
+  // Remove privous node map
+  mBlendShapeModelNodeMap.clear();
+
+  UpdateBlendShapeNodeMapRecursively(mBlendShapeModelNodeMap, mModelRoot);
+}
+
 } // namespace Internal
 } // namespace Scene3D
 } // namespace Dali