Add CastShadow and ReceiveShadow for Model and ModelNode
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / internal / controls / model / model-impl.cpp
index bdbbf3b..f012aa6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 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.
@@ -23,6 +23,7 @@
 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
 #include <dali/devel-api/actors/actor-devel.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
 #include <dali/public-api/math/math-utils.h>
 #include <dali/public-api/object/type-registry-helper.h>
 #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>
 #include <dali-scene3d/public-api/loader/camera-parameters.h>
-#include <dali-scene3d/public-api/loader/cube-map-loader.h>
-#include <dali-scene3d/public-api/loader/dli-loader.h>
-#include <dali-scene3d/public-api/loader/gltf2-loader.h>
 #include <dali-scene3d/public-api/loader/light-parameters.h>
 #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
@@ -53,6 +57,9 @@ namespace Internal
 {
 namespace
 {
+/**
+ * Creates control through type registry
+ */
 BaseHandle Create()
 {
   return Scene3D::Model::New(std::string());
@@ -62,19 +69,12 @@ BaseHandle Create()
 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::Model, Toolkit::Control, Create);
 DALI_TYPE_REGISTRATION_END()
 
-static constexpr uint32_t OFFSET_FOR_DIFFUSE_CUBE_TEXTURE  = 2u;
-static constexpr uint32_t OFFSET_FOR_SPECULAR_CUBE_TEXTURE = 1u;
-
 static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
+static constexpr float   SIZE_STEP_CONDITION = 0.1f;
 
 static constexpr bool DEFAULT_MODEL_CHILDREN_SENSITIVE = false;
 static constexpr bool DEFAULT_MODEL_CHILDREN_FOCUSABLE = false;
 
-static constexpr std::string_view KTX_EXTENSION  = ".ktx";
-static constexpr std::string_view OBJ_EXTENSION  = ".obj";
-static constexpr std::string_view GLTF_EXTENSION = ".gltf";
-static constexpr std::string_view DLI_EXTENSION  = ".dli";
-
 struct BoundingVolume
 {
   void Init()
@@ -174,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)
@@ -181,19 +252,33 @@ 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),
   mIblScaleFactor(1.0f),
+  mSceneSpecularMipmapLevels(1u),
+  mSpecularMipmapLevels(1u),
   mModelChildrenSensitive(DEFAULT_MODEL_CHILDREN_SENSITIVE),
   mModelChildrenFocusable(DEFAULT_MODEL_CHILDREN_FOCUSABLE),
   mModelResourceReady(false),
-  mIBLResourceReady(true)
+  mIblDiffuseResourceReady(true),
+  mIblSpecularResourceReady(true),
+  mIblDiffuseDirty(false),
+  mIblSpecularDirty(false),
+  mIsShadowCasting(true),
+  mIsShadowReceiving(true)
 {
 }
 
 Model::~Model()
 {
+  ResetResourceTasks();
+
+  if(ModelCacheManager::Get() && !mModelUrl.empty())
+  {
+    ModelCacheManager::Get().UnreferenceModelCache(mModelUrl);
+  }
 }
 
 Dali::Scene3D::Model Model::New(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
@@ -209,11 +294,88 @@ Dali::Scene3D::Model Model::New(const std::string& modelUrl, const std::string&
   return handle;
 }
 
-const Actor Model::GetModelRoot() const
+const Scene3D::ModelNode Model::GetModelRoot() const
 {
   return mModelRoot;
 }
 
+void Model::AddModelNode(Scene3D::ModelNode modelNode)
+{
+  if(!mModelRoot)
+  {
+    CreateModelRoot();
+  }
+
+  mModelRoot.Add(modelNode);
+  if(mModelUrl.empty())
+  {
+    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);
+  }
+}
+
 void Model::SetChildrenSensitive(bool enable)
 {
   if(mModelChildrenSensitive != enable)
@@ -251,30 +413,88 @@ bool Model::GetChildrenFocusable() const
 
 void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::string& specularUrl, float scaleFactor)
 {
-  mIBLResourceReady       = false;
-  Texture diffuseTexture  = (!diffuseUrl.empty()) ? Dali::Scene3D::Loader::LoadCubeMap(diffuseUrl) : Texture();
-  Texture specularTexture = (!specularUrl.empty()) ? Dali::Scene3D::Loader::LoadCubeMap(specularUrl) : Texture();
-  SetImageBasedLightTexture(diffuseTexture, specularTexture, scaleFactor);
-  mIBLResourceReady = true;
+  bool needIblReset = false;
+  bool isOnScene    = Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE);
+  if(mDiffuseIblUrl != diffuseUrl)
+  {
+    mDiffuseIblUrl = diffuseUrl;
+    if(mDiffuseIblUrl.empty())
+    {
+      needIblReset = true;
+    }
+    else
+    {
+      mIblDiffuseDirty         = true;
+      mIblDiffuseResourceReady = false;
+    }
+  }
 
-  // If Model resource is already ready, then set resource ready.
-  // If Model resource is still not ready, wait for model resource ready.
-  if(IsResourceReady())
+  if(mSpecularIblUrl != specularUrl)
   {
-    SetResourceReady(false);
+    mSpecularIblUrl = specularUrl;
+    if(mSpecularIblUrl.empty())
+    {
+      needIblReset = true;
+    }
+    else
+    {
+      mIblSpecularDirty         = true;
+      mIblSpecularResourceReady = false;
+    }
   }
-}
 
-void Model::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
-{
-  // If input texture is wrong, Model is rendered with SceneView's IBL.
-  if(mDiffuseTexture != diffuseTexture || mSpecularTexture != specularTexture)
+  // If one or both of diffuse url and specular url are empty,
+  // we don't need to request to load texture.
+  if(needIblReset)
   {
-    mDiffuseTexture  = diffuseTexture;
-    mSpecularTexture = specularTexture;
-    mIblScaleFactor  = scaleFactor;
+    ResetResourceTask(mIblDiffuseLoadTask);
+    ResetResourceTask(mIblSpecularLoadTask);
+
+    mIblDiffuseDirty          = false;
+    mIblSpecularDirty         = false;
+    mIblDiffuseResourceReady  = true;
+    mIblSpecularResourceReady = true;
+
+    mDiffuseTexture.Reset();
+    mSpecularTexture.Reset();
     UpdateImageBasedLightTexture();
+
+    // Request image resource GC
+    Dali::Scene3D::Internal::ImageResourceLoader::RequestGarbageCollect();
+  }
+  else
+  {
+    if(isOnScene && mIblDiffuseDirty)
+    {
+      ResetResourceTask(mIblDiffuseLoadTask);
+      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)
+    {
+      ResetResourceTask(mIblSpecularLoadTask);
+      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();
+    }
+  }
+
+  if(!Dali::Equals(mIblScaleFactor, scaleFactor))
+  {
+    mIblScaleFactor = scaleFactor;
+    UpdateImageBasedLightScaleFactor();
   }
+
+  // If diffuse and specular textures are already loaded, emits resource ready signal here.
+  NotifyResourceReady();
 }
 
 void Model::SetImageBasedLightScaleFactor(float scaleFactor)
@@ -323,6 +543,298 @@ Dali::Animation Model::GetAnimation(const std::string& name) const
   return animation;
 }
 
+uint32_t Model::GetCameraCount() const
+{
+  return mCameraParameters.size();
+}
+
+Dali::CameraActor Model::GenerateCamera(uint32_t index) const
+{
+  Dali::CameraActor camera;
+  if(mCameraParameters.size() > index)
+  {
+    camera = Dali::CameraActor::New3DCamera();
+    if(!mCameraParameters[index].ConfigureCamera(camera, false))
+    {
+      DALI_LOG_ERROR("Fail to generate %u's camera actor : Some property was not defined. Please check model file.\n", index);
+      camera.Reset();
+      return camera;
+    }
+
+    ApplyCameraTransform(camera);
+  }
+  return camera;
+}
+
+bool Model::ApplyCamera(uint32_t index, Dali::CameraActor camera) const
+{
+  if(camera && mCameraParameters.size() > index)
+  {
+    if(!mCameraParameters[index].ConfigureCamera(camera, false))
+    {
+      DALI_LOG_ERROR("Fail to apply %u's camera actor : Some property was not defined. Please check model file.\n", index);
+      return false;
+    }
+
+    ApplyCameraTransform(camera);
+    return true;
+  }
+  return false;
+}
+
+Scene3D::ModelNode Model::FindChildModelNodeByName(std::string_view nodeName)
+{
+  Actor childActor = Self().FindChildByName(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
@@ -332,47 +844,97 @@ 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)
 {
-  if(!mModelRoot)
-  {
-    LoadModel();
-  }
-
   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)
     {
-      GetImpl(sceneView).RegisterSceneItem(this);
       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.
+    if(ModelCacheManager::Get())
+    {
+      ModelCacheManager::Get().ReferenceModelCache(mModelUrl);
+    }
+    mModelLoadTask = new ModelLoadTask(mModelUrl, mResourceDirectoryUrl, MakeCallback(this, &Model::OnModelLoadComplete));
+    Dali::AsyncTaskManager::Get().AddTask(mModelLoadTask);
+  }
+
+  // If diffuse and specular url is not valid, IBL does not need to be loaded.
+  if(!mDiffuseIblUrl.empty() && !mSpecularIblUrl.empty())
+  {
+    SetImageBasedLightSource(mDiffuseIblUrl, mSpecularIblUrl, mIblScaleFactor);
+  }
+
+  NotifyResourceReady();
+
+  mSizeNotification = Self().AddPropertyNotification(Actor::Property::SIZE, StepCondition(SIZE_STEP_CONDITION));
+  mSizeNotification.NotifySignal().Connect(this, &Model::OnSizeNotification);
   Control::OnSceneConnection(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();
   }
+
+  mSizeNotification.NotifySignal().Disconnect(this, &Model::OnSizeNotification);
+  Self().RemovePropertyNotification(mSizeNotification);
+  mSizeNotification.Reset();
+
   Control::OnSceneDisconnection();
 }
 
+void Model::OnSizeSet(const Vector3& size)
+{
+  ScaleModel(false);
+}
+
 Vector3 Model::GetNaturalSize()
 {
   if(!mModelRoot)
   {
-    LoadModel();
+    DALI_LOG_ERROR("Model is still not loaded.\n");
+    return Vector3::ZERO;
   }
 
   return mNaturalSize;
@@ -395,283 +957,428 @@ float Model::GetWidthForHeight(float height)
 void Model::OnRelayout(const Vector2& size, RelayoutContainer& container)
 {
   Control::OnRelayout(size, container);
-  ScaleModel();
+  ScaleModel(false);
 }
 
 bool Model::IsResourceReady() const
 {
-  return mModelResourceReady && mIBLResourceReady;
+  return mModelResourceReady && mIblDiffuseResourceReady && mIblSpecularResourceReady;
+}
+
+void Model::CreateModelRoot()
+{
+  mModelRoot = Scene3D::ModelNode::New();
+  mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
+  mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION);
+  mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
+  mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
+  mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
+  Self().Add(mModelRoot);
 }
 
-void Model::LoadModel()
+void Model::ScaleModel(bool useCurrentSize)
 {
-  std::filesystem::path modelUrl(mModelUrl);
-  if(mResourceDirectoryUrl.empty())
+  if(!mModelRoot)
   {
-    mResourceDirectoryUrl = std::string(modelUrl.parent_path()) + "/";
+    return;
   }
-  std::string extension = modelUrl.extension();
-  std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
 
-  Dali::Scene3D::Loader::ResourceBundle::PathProvider pathProvider = [&](Dali::Scene3D::Loader::ResourceType::Value type) {
-    return mResourceDirectoryUrl;
-  };
+  float   scale = 1.0f;
+  Vector3 size  = (useCurrentSize) ? Self().GetCurrentProperty<Vector3>(Dali::Actor::Property::SIZE) : Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
+  if(size.x > 0.0f && size.y > 0.0f)
+  {
+    scale = MAXFLOAT;
+    scale = std::min(size.x / mNaturalSize.x, scale);
+    scale = std::min(size.y / mNaturalSize.y, scale);
+  }
+  // Models in glTF and dli are defined as right hand coordinate system.
+  // DALi uses left hand coordinate system. Scaling negative is for change winding order.
+  mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scale);
+}
 
-  Dali::Scene3D::Loader::ResourceBundle                        resources;
-  Dali::Scene3D::Loader::SceneDefinition                       scene;
-  std::vector<Dali::Scene3D::Loader::AnimationGroupDefinition> animGroups;
-  std::vector<Dali::Scene3D::Loader::CameraParameters>         cameraParameters;
-  std::vector<Dali::Scene3D::Loader::LightParameters>          lights;
+void Model::FitModelPosition()
+{
+  if(!mModelRoot)
+  {
+    return;
+  }
+  // Loaded model pivot is not the model center.
+  mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
+}
 
-  std::vector<Dali::Scene3D::Loader::AnimationDefinition> animations;
-  animations.clear();
+void Model::UpdateCastShadowRecursively(Scene3D::ModelNode node, bool castShadow)
+{
+  if(!node)
+  {
+    return;
+  }
 
-  Dali::Scene3D::Loader::SceneMetadata metaData;
+  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);
+  }
+}
 
-  std::filesystem::path metaDataUrl = modelUrl;
-  metaDataUrl.replace_extension("metadata");
+void Model::UpdateReceiveShadowRecursively(Scene3D::ModelNode node, bool receiveShadow)
+{
+  if(!node)
+  {
+    return;
+  }
 
-  Dali::Scene3D::Loader::LoadSceneMetadata(metaDataUrl.c_str(), metaData);
+  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);
+  }
+}
 
-  Dali::Scene3D::Loader::LoadResult output{resources, scene, metaData, animations, animGroups, cameraParameters, lights};
+void Model::UpdateImageBasedLightTextureRecursively(Scene3D::ModelNode node, Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
+{
+  if(!node)
+  {
+    return;
+  }
 
-  if(extension == DLI_EXTENSION)
+  GetImplementation(node).SetImageBasedLightTexture(diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
+  uint32_t childrenCount = node.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
   {
-    Dali::Scene3D::Loader::DliLoader              loader;
-    Dali::Scene3D::Loader::DliLoader::InputParams input{
-      pathProvider(Dali::Scene3D::Loader::ResourceType::Mesh),
-      nullptr,
-      {},
-      {},
-      nullptr,
-      {}};
-    Dali::Scene3D::Loader::DliLoader::LoadParams loadParams{input, output};
-    if(!loader.LoadScene(mModelUrl, loadParams))
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(!childNode)
     {
-      Dali::Scene3D::Loader::ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << mModelUrl << "': " << loader.GetParseError();
+      continue;
     }
+    UpdateImageBasedLightTextureRecursively(childNode, diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
   }
-  else if(extension == GLTF_EXTENSION)
+}
+
+void Model::UpdateImageBasedLightScaleFactorRecursively(Scene3D::ModelNode node, float iblScaleFactor)
+{
+  if(!node)
   {
-    Dali::Scene3D::Loader::ShaderDefinitionFactory sdf;
-    sdf.SetResources(resources);
-    Dali::Scene3D::Loader::LoadGltfScene(mModelUrl, sdf, output);
+    return;
+  }
+
+  GetImplementation(node).SetImageBasedLightScaleFactor(iblScaleFactor);
 
-    resources.mEnvironmentMaps.push_back({});
+  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;
+    }
+    UpdateImageBasedLightScaleFactorRecursively(childNode, iblScaleFactor);
   }
-  else
+}
+
+void Model::UpdateImageBasedLightTexture()
+{
+  Dali::Texture currentDiffuseTexture          = (mDiffuseTexture && mSpecularTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
+  Dali::Texture currentSpecularTexture         = (mDiffuseTexture && mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture;
+  float         currentIblScaleFactor          = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
+  uint32_t      currentIblSpecularMipmapLevels = (mDiffuseTexture && mSpecularTexture) ? mSpecularMipmapLevels : mSceneSpecularMipmapLevels;
+
+  if(!currentDiffuseTexture || !currentSpecularTexture)
   {
-    DALI_LOG_ERROR("Unsupported model type.\n");
+    currentDiffuseTexture          = mDefaultDiffuseTexture;
+    currentSpecularTexture         = mDefaultSpecularTexture;
+    currentIblScaleFactor          = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
+    currentIblSpecularMipmapLevels = 1u;
   }
 
-  Dali::Scene3D::Loader::Transforms                   xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
-  Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, {}, {}, {}};
-  Dali::Scene3D::Loader::Customization::Choices       choices;
+  UpdateImageBasedLightTextureRecursively(mModelRoot, currentDiffuseTexture, currentSpecularTexture, currentIblScaleFactor, currentIblSpecularMipmapLevels);
+}
 
-  mModelRoot = Actor::New();
+void Model::UpdateImageBasedLightScaleFactor()
+{
+  if((!mDiffuseTexture || !mSpecularTexture) &&
+     (!mSceneDiffuseTexture || !mSceneSpecularTexture))
+  {
+    return;
+  }
 
-  BoundingVolume AABB;
-  for(auto iRoot : scene.GetRoots())
+  float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
+  UpdateImageBasedLightScaleFactorRecursively(mModelRoot, currentIblScaleFactor);
+}
+
+void Model::ApplyCameraTransform(Dali::CameraActor camera) const
+{
+  Vector3    selfPosition    = Self().GetProperty<Vector3>(Actor::Property::POSITION);
+  Quaternion selfOrientation = Self().GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+  Vector3    selfScale       = Self().GetProperty<Vector3>(Actor::Property::SCALE);
+
+  Vector3    cameraPosition    = camera.GetProperty<Vector3>(Actor::Property::POSITION);
+  Quaternion cameraOrientation = camera.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+  Vector3    cameraScale       = camera.GetProperty<Vector3>(Actor::Property::SCALE);
+
+  // Models in glTF and dli are defined as right hand coordinate system.
+  // DALi uses left hand coordinate system. Scaling negative is for change winding order.
+  if(!Dali::Equals(Y_DIRECTION.Dot(Vector3::YAXIS), 1.0f))
   {
-    auto resourceRefs = resources.CreateRefCounter();
-    scene.CountResourceRefs(iRoot, choices, resourceRefs);
-    resources.CountEnvironmentReferences(resourceRefs);
+    // Reflect by XZ plane
+    cameraPosition.y = -cameraPosition.y;
+    Quaternion yDirectionQuaternion;
+    yDirectionQuaternion.mVector = Vector3::YAXIS;
+    // Reflect orientation
+    cameraOrientation = yDirectionQuaternion * cameraOrientation * yDirectionQuaternion;
+  }
+
+  Vector3    resultPosition;
+  Quaternion resultOrientation;
+  Vector3    resultScale;
+
+  Matrix selfMatrix(false);
+  Matrix cameraMatrix(false);
+  Matrix resultMatrix(false);
+  selfMatrix.SetTransformComponents(selfScale, selfOrientation, selfPosition);
+  cameraMatrix.SetTransformComponents(cameraScale, cameraOrientation, cameraPosition);
+  Matrix::Multiply(resultMatrix, cameraMatrix, selfMatrix);
+  resultMatrix.GetTransformComponents(resultPosition, resultOrientation, resultScale);
+
+  camera.SetProperty(Actor::Property::POSITION, resultPosition);
+  camera.SetProperty(Actor::Property::ORIENTATION, resultOrientation);
+  camera.SetProperty(Actor::Property::SCALE, resultScale);
+}
 
-    resources.LoadResources(resourceRefs, pathProvider);
+void Model::NotifyShadowMapTexture(Dali::Texture shadowMapTexture)
+{
+  if(mShadowMapTexture != shadowMapTexture)
+  {
+    mShadowMapTexture = shadowMapTexture;
+    UpdateShadowMapTextureRecursively(mModelRoot, mShadowMapTexture);
+  }
+}
 
-    // glTF Mesh is defined in right hand coordinate system, with positive Y for Up direction.
-    // Because DALi uses left hand system, Y direciton will be flipped for environment map sampling.
-    for(auto&& env : resources.mEnvironmentMaps)
+void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor, uint32_t specularMipmapLevels)
+{
+  if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
+  {
+    mSceneDiffuseTexture       = diffuseTexture;
+    mSceneSpecularTexture      = specularTexture;
+    mSceneIblScaleFactor       = scaleFactor;
+    mSceneSpecularMipmapLevels = specularMipmapLevels;
+    // If Model IBL is not set, use SceneView's IBL.
+    if(!mDiffuseTexture || !mSpecularTexture)
     {
-      env.first.mYDirection = Y_DIRECTION;
+      UpdateImageBasedLightTexture();
     }
+  }
+}
 
-    if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
-    {
-      scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
-      scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
-      ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
+void Model::NotifyImageBasedLightScaleFactor(float scaleFactor)
+{
+  mSceneIblScaleFactor = scaleFactor;
+  if(mSceneDiffuseTexture && mSceneSpecularTexture)
+  {
+    UpdateImageBasedLightScaleFactor();
+  }
+}
 
-      scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
+void Model::OnModelLoadComplete()
+{
+  IntrusivePtr<Model> self = this; // Keep reference until this API finished
 
-      mModelRoot.Add(actor);
+  if(!mModelLoadTask->HasSucceeded())
+  {
+    ResetResourceTasks();
+
+    if(ModelCacheManager::Get() && !mModelUrl.empty())
+    {
+      ModelCacheManager::Get().UnreferenceModelCache(mModelUrl);
     }
 
-    AddModelTreeToAABB(AABB, scene, choices, iRoot, nodeParams, Matrix::IDENTITY);
+    return;
   }
 
-  if(!resources.mEnvironmentMaps.empty())
+  if(!mModelRoot)
   {
-    mDefaultDiffuseTexture  = resources.mEnvironmentMaps.front().second.mDiffuse;
-    mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular;
+    CreateModelRoot();
   }
+  CreateModel();
 
-  if(!animations.empty())
+  auto& resources = mModelLoadTask->GetResources();
+  auto& scene     = mModelLoadTask->GetScene();
+  CreateAnimations(scene);
+  ResetCameraParameters();
+  if(!resources.mEnvironmentMaps.empty())
   {
-    auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property) {
-      return mModelRoot.FindChildById(scene.GetNode(property.mNodeIndex)->mNodeId);
-    };
-
-    mAnimations.clear();
-    for(auto&& animation : animations)
+    if(resources.mEnvironmentMaps.front().second.mDiffuse)
     {
-      Dali::Animation anim = animation.ReAnimate(getActor);
-
-      mAnimations.push_back({animation.mName, anim});
+      mDefaultDiffuseTexture = resources.mEnvironmentMaps.front().second.mDiffuse;
+    }
+    if(resources.mEnvironmentMaps.front().second.mSpecular)
+    {
+      mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular;
     }
   }
 
-  mRenderableActors.clear();
-  CollectRenderableActor(mModelRoot);
-  UpdateImageBasedLightTexture();
-  UpdateImageBasedLightScaleFactor();
-
-  mNaturalSize = AABB.CalculateSize();
-  mModelPivot  = AABB.CalculatePivot();
-  mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
-  Vector3 controlSize = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
-  if(Dali::EqualsZero(controlSize.x) || Dali::EqualsZero(controlSize.y))
+  if(mShadowMapTexture)
   {
-    Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
+    UpdateShadowMapTextureRecursively(mModelRoot, mShadowMapTexture);
   }
-
-  FitModelPosition();
-  ScaleModel();
-
-  mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
-  mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
-  mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
-
-  Self().Add(mModelRoot);
-
+  UpdateImageBasedLightTexture();
+  UpdateImageBasedLightScaleFactor();
   Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
 
   mModelResourceReady = true;
-
-  Control::SetResourceReady(false);
+  ResetResourceTask(mModelLoadTask);
+  NotifyResourceReady();
 }
 
-void Model::ScaleModel()
+void Model::OnIblDiffuseLoadComplete()
 {
-  if(mModelRoot)
+  mDiffuseTexture = mIblDiffuseLoadTask->GetLoadedTexture();
+  ResetResourceTask(mIblDiffuseLoadTask);
+  mIblDiffuseResourceReady = true;
+  if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
   {
-    float   scale = 1.0f;
-    Vector3 size  = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
-    if(size.x > 0.0f && size.y > 0.0f)
-    {
-      scale = MAXFLOAT;
-      scale = std::min(size.x / mNaturalSize.x, scale);
-      scale = std::min(size.y / mNaturalSize.y, scale);
-    }
-    // Models in glTF and dli are defined as right hand coordinate system.
-    // DALi uses left hand coordinate system. Scaling negative is for change winding order.
-    mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scale);
+    OnIblLoadComplete();
   }
 }
 
-void Model::FitModelPosition()
+void Model::OnIblSpecularLoadComplete()
 {
-  if(mModelRoot)
+  mSpecularTexture      = mIblSpecularLoadTask->GetLoadedTexture();
+  mSpecularMipmapLevels = mIblSpecularLoadTask->GetMipmapLevels();
+  ResetResourceTask(mIblSpecularLoadTask);
+  mIblSpecularResourceReady = true;
+  if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
   {
-    // Loaded model pivot is not the model center.
-    mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
-    mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
+    OnIblLoadComplete();
   }
 }
 
-void Model::CollectRenderableActor(Actor actor)
+void Model::OnIblLoadComplete()
 {
-  uint32_t rendererCount = actor.GetRendererCount();
-  if(rendererCount)
-  {
-    mRenderableActors.push_back(actor);
-  }
+  UpdateImageBasedLightTexture();
+  NotifyResourceReady();
+}
 
-  uint32_t childrenCount = actor.GetChildCount();
-  for(uint32_t i = 0; i < childrenCount; ++i)
+void Model::OnSizeNotification(Dali::PropertyNotification& source)
+{
+  ScaleModel(true);
+}
+
+void Model::ResetResourceTasks()
+{
+  if(!Dali::Adaptor::IsAvailable())
   {
-    CollectRenderableActor(actor.GetChildAt(i));
+    return;
   }
+  ResetResourceTask(mModelLoadTask);
+  ResetResourceTask(mIblDiffuseLoadTask);
+  ResetResourceTask(mIblSpecularLoadTask);
 }
 
-void Model::UpdateImageBasedLightTexture()
+void Model::NotifyResourceReady()
 {
-  Dali::Texture currentDiffuseTexture  = (mDiffuseTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
-  Dali::Texture currentSpecularTexture = (mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture;
-  float         currentIBLScaleFactor  = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
-  if(!currentDiffuseTexture || !currentSpecularTexture)
+  if(!IsResourceReady())
   {
-    currentDiffuseTexture  = mDefaultDiffuseTexture;
-    currentSpecularTexture = mDefaultSpecularTexture;
-    currentIBLScaleFactor  = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
+    return;
   }
+  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, mShaderManager, {}, {}, {}};
 
-  for(auto&& actor : mRenderableActors)
+  // Generate Dali handles from resource bundle. Note that we generate all scene's resouce immediatly.
+  resources.GenerateResources();
+  for(auto iRoot : scene.GetRoots())
   {
-    Actor renderableActor = actor.GetHandle();
-    if(renderableActor)
+    if(auto modelNode = scene.CreateNodes(iRoot, resourceChoices, nodeParams))
     {
-      uint32_t rendererCount = renderableActor.GetRendererCount();
-      for(uint32_t i = 0; i < rendererCount; ++i)
-      {
-        Dali::Renderer renderer = renderableActor.GetRendererAt(i);
-        if(renderer)
-        {
-          Dali::TextureSet textures = renderer.GetTextures();
-          if(textures)
-          {
-            uint32_t textureCount = textures.GetTextureCount();
-            // EnvMap requires at least 2 texture, diffuse and specular
-            if(textureCount > 2u)
-            {
-              textures.SetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE, currentDiffuseTexture);
-              textures.SetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE, currentSpecularTexture);
-            }
-          }
-        }
-      }
-      renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor);
+      scene.ConfigureSkinningShaders(resources, modelNode, std::move(nodeParams.mSkinnables));
+      ConfigureBlendShapeShaders(resources, scene, modelNode, std::move(nodeParams.mBlendshapeRequests));
+
+      scene.ApplyConstraints(modelNode, std::move(nodeParams.mConstrainables));
+
+      mModelRoot.Add(modelNode);
     }
+
+    AddModelTreeToAABB(AABB, scene, resourceChoices, iRoot, nodeParams, Matrix::IDENTITY);
   }
-}
 
-void Model::UpdateImageBasedLightScaleFactor()
-{
-  if((!mDiffuseTexture || !mSpecularTexture) &&
-     (!mSceneDiffuseTexture || !mSceneSpecularTexture))
+  UpdateBlendShapeNodeMap();
+
+  mNaturalSize = AABB.CalculateSize();
+  mModelPivot  = AABB.CalculatePivot();
+  mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
+  Vector3 controlSize = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
+  if(Dali::EqualsZero(controlSize.x) || Dali::EqualsZero(controlSize.y))
   {
-    return;
+    Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
   }
+  FitModelPosition();
+  ScaleModel(false);
+}
 
-  float currentIBLScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
-  for(auto&& actor : mRenderableActors)
+void Model::CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene)
+{
+  mAnimations.clear();
+  if(!mModelLoadTask->GetAnimations().empty())
   {
-    Actor renderableActor = actor.GetHandle();
-    if(renderableActor)
+    auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property) {
+      if(property.mNodeIndex == Scene3D::Loader::INVALID_INDEX)
+      {
+        return mModelRoot.FindChildByName(property.mNodeName);
+      }
+      auto* node = scene.GetNode(property.mNodeIndex);
+      if(node == nullptr)
+      {
+        return Dali::Actor();
+      }
+      return mModelRoot.FindChildById(node->mNodeId);
+    };
+
+    for(auto&& animation : mModelLoadTask->GetAnimations())
     {
-      renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor);
+      Dali::Animation anim = animation.ReAnimate(getActor);
+      mAnimations.push_back({animation.GetName(), anim});
     }
   }
 }
 
-void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
+void Model::ResetCameraParameters()
 {
-  if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
+  mCameraParameters.clear();
+  if(!mModelLoadTask->GetCameras().empty())
   {
-    mSceneDiffuseTexture  = diffuseTexture;
-    mSceneSpecularTexture = specularTexture;
-    mSceneIblScaleFactor  = scaleFactor;
-    UpdateImageBasedLightTexture();
+    // Copy camera parameters.
+    std::copy(mModelLoadTask->GetCameras().begin(), mModelLoadTask->GetCameras().end(), std::back_inserter(mCameraParameters));
   }
 }
 
-void Model::NotifyImageBasedLightScaleFactor(float scaleFactor)
+void Model::UpdateBlendShapeNodeMap()
 {
-  mSceneIblScaleFactor = scaleFactor;
-  if(mSceneDiffuseTexture && mSceneSpecularTexture)
-  {
-    UpdateImageBasedLightScaleFactor();
-  }
+  // Remove privous node map
+  mBlendShapeModelNodeMap.clear();
+
+  UpdateBlendShapeNodeMapRecursively(mBlendShapeModelNodeMap, mModelRoot);
 }
 
 } // namespace Internal