[dali_2.3.24] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / scene-definition.cpp
index f41422e..7d28053 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.
  *
  */
 
-// EXTERNAL
-#include "dali/devel-api/common/map-wrapper.h"
-#include "dali/public-api/animation/constraints.h"
+// CLASS HEADER
+#include <dali-scene3d/public-api/loader/scene-definition.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/common/map-wrapper.h>
+#include <dali/public-api/animation/constraints.h>
 
 // INTERNAL
-#include "dali-scene3d/internal/graphics/builtin-shader-extern-gen.h"
-#include "dali-scene3d/public-api/loader/blend-shape-details.h"
-#include "dali-scene3d/public-api/loader/scene-definition.h"
-#include "dali-scene3d/public-api/loader/skinning-details.h"
-#include "dali-scene3d/public-api/loader/utils.h"
+#include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-scene3d/internal/model-components/model-node-impl.h>
+#include <dali-scene3d/public-api/loader/blend-shape-details.h>
+#include <dali-scene3d/public-api/loader/skinning-details.h>
+#include <dali-scene3d/public-api/loader/utils.h>
 
-//#define DEBUG_SCENE_DEFINITION
-//#define DEBUG_JOINTS
+// #define DEBUG_SCENE_DEFINITION
+// #define DEBUG_JOINTS
 
 #if defined(DEBUG_SCENE_DEFINITION) || defined(DEBUG_JOINTS)
 #define DEBUG_ONLY(x) x
 
 #define LOGD(x) DEBUG_ONLY(printf x; printf("\n"); fflush(stdout))
 
-namespace Dali
-{
-namespace Scene3D
-{
-namespace Loader
+namespace Dali::Scene3D::Loader
 {
 namespace
 {
-const char* JOINT_MATRIX{"jointMatrix"};
-
-const std::map<Property::Type, Constraint (*)(Actor&, Property::Index)> sConstraintFactory = {
-  {Property::Type::BOOLEAN,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<bool>(a, i, [](bool& current, const PropertyInputContainer& inputs) {
-       current = inputs[0]->GetBoolean();
-     });
-   }},
-  {Property::Type::INTEGER,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<int>(a, i, [](int& current, const PropertyInputContainer& inputs) {
-       current = inputs[0]->GetInteger();
-     });
-   }},
-  {Property::Type::FLOAT,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<float>(a, i, EqualToConstraint());
-   }},
-  {Property::Type::VECTOR2,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<Vector2>(a, i, EqualToConstraint());
-   }},
-  {Property::Type::VECTOR3,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<Vector3>(a, i, EqualToConstraint());
-   }},
-  {Property::Type::VECTOR4,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<Vector4>(a, i, EqualToConstraint());
-   }},
-  {Property::Type::MATRIX,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<Matrix>(a, i, EqualToConstraint());
-   }},
-  {Property::Type::MATRIX3,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<Matrix3>(a, i, EqualToConstraint());
-   }},
-  {Property::Type::ROTATION,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<Quaternion>(a, i, EqualToConstraint());
-   }},
-};
+const std::map<Property::Type, Constraint (*)(Actor&, Property::Index)>& GetConstraintFactory()
+{
+  static const std::map<Property::Type, Constraint (*)(Actor&, Property::Index)> sConstraintFactory = {
+    {Property::Type::BOOLEAN,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<bool>(a, i, [](bool& current, const PropertyInputContainer& inputs) { current = inputs[0]->GetBoolean(); });
+     }},
+    {Property::Type::INTEGER,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<int>(a, i, [](int& current, const PropertyInputContainer& inputs) { current = inputs[0]->GetInteger(); });
+     }},
+    {Property::Type::FLOAT,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<float>(a, i, EqualToConstraint());
+     }},
+    {Property::Type::VECTOR2,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<Vector2>(a, i, EqualToConstraint());
+     }},
+    {Property::Type::VECTOR3,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<Vector3>(a, i, EqualToConstraint());
+     }},
+    {Property::Type::VECTOR4,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<Vector4>(a, i, EqualToConstraint());
+     }},
+    {Property::Type::MATRIX,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<Matrix>(a, i, EqualToConstraint());
+     }},
+    {Property::Type::MATRIX3,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<Matrix3>(a, i, EqualToConstraint());
+     }},
+    {Property::Type::ROTATION,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<Quaternion>(a, i, EqualToConstraint());
+     }},
+  };
+  return sConstraintFactory;
+}
 
 struct ResourceReflector : IResourceReflector
 {
@@ -124,7 +121,7 @@ void EnsureJointDebugShaderCreated()
 {
   if(0 == sNumScenes)
   {
-    sJointDebugShader = Shader::New(SHADER_SCENE3D_JOINT_DEBUG_VERT, SHADER_SCENE3D_JOINT_DEBUG_FRAG);
+    sJointDebugShader = Shader::New(SHADER_SCENE3D_JOINT_DEBUG_VERT, SHADER_SCENE3D_JOINT_DEBUG_FRAG, Shader::Hint::NONE, "SCENE3D_JOINT_DEBUG");
   }
   ++sNumScenes;
 }
@@ -162,7 +159,7 @@ void AddJointDebugVisual(Actor aJoint)
 
   aJoint.SetVisible(true);
 }
-#endif //DEBUG_JOINTS
+#endif // DEBUG_JOINTS
 
 class ActorCreatorVisitor : public NodeDefinition::IVisitor
 {
@@ -176,7 +173,7 @@ public:
   {
     mCreationContext.mXforms.modelStack.Push(n.GetLocalSpace());
 
-    Actor a = n.CreateActor(mCreationContext);
+    ModelNode a = n.CreateModelNode(mCreationContext);
     if(!mActorStack.empty())
     {
       mActorStack.back().Add(a);
@@ -194,130 +191,57 @@ public:
     mCreationContext.mXforms.modelStack.Pop();
   }
 
-  Actor GetRoot() const
+  ModelNode GetRoot() const
   {
     return mRoot;
   }
 
 private:
   NodeDefinition::CreateParams& mCreationContext;
-  std::vector<Actor>            mActorStack;
-  Actor                         mRoot;
+  std::vector<ModelNode>        mActorStack;
+  ModelNode                     mRoot;
 };
 
-bool IsAncestor(const SceneDefinition& scene, Index ancestor, Index node, Index rootHint = INVALID_INDEX)
-{
-  bool isAncestor = false;
-  while(node != rootHint && !isAncestor)
-  {
-    node       = scene.GetNode(node)->mParentIdx;
-    isAncestor = ancestor == node;
-  }
-  return isAncestor;
-}
-
-void InsertUniqueSorted(std::vector<Index>& data, Index value)
-{
-  auto iInsert = std::lower_bound(data.begin(), data.end(), value);
-  if(iInsert == data.end() || *iInsert != value)
-  {
-    data.insert(iInsert, value);
-  }
-}
-
-void RemoveFromSorted(std::vector<Index>& data, Index value)
-{
-  auto iRemove = std::lower_bound(data.begin(), data.end(), value);
-  if(iRemove != data.end() && *iRemove == value)
-  {
-    data.erase(iRemove);
-  }
-}
-
-Property::Index ConfigureJointMatrix(Actor actor, Actor ancestor, Property::Index propJointMatrix)
-{
-  Actor parent = actor.GetParent();
-  if(parent != ancestor)
-  {
-    propJointMatrix = ConfigureJointMatrix(parent, ancestor, propJointMatrix);
-  }
-
-  auto myPropJointMatrix = actor.GetPropertyIndex(JOINT_MATRIX);
-  if(myPropJointMatrix == Property::INVALID_INDEX)
-  {
-    myPropJointMatrix     = actor.RegisterProperty(JOINT_MATRIX, Matrix{false});
-    Constraint constraint = Constraint::New<Matrix>(actor, propJointMatrix, [](Matrix& output, const PropertyInputContainer& inputs) {
-      Matrix jointMatrix{false};
-      jointMatrix.SetTransformComponents(Vector3::ONE, inputs[0]->GetQuaternion(), inputs[1]->GetVector3());
-
-      Matrix::Multiply(output, jointMatrix, inputs[2]->GetMatrix());
-    });
-    constraint.AddSource(Source{actor, Actor::Property::ORIENTATION});
-    constraint.AddSource(Source{actor, Actor::Property::POSITION});
-    constraint.AddSource(Source{parent, propJointMatrix});
-    constraint.Apply();
-  }
-
-  return myPropJointMatrix;
-}
-
-void SortAndDeduplicateSkinningRequests(std::vector<SkinningShaderConfigurationRequest>& requests)
+template<typename RequestType>
+void SortAndDeduplicateRequests(std::vector<RequestType>& requests)
 {
-  // Sort requests by shaders.
+  // Sort requests by shaders and primitives.
   std::sort(requests.begin(), requests.end());
 
   // Remove duplicates.
-  auto   i           = requests.begin();
-  auto   iEnd        = requests.end();
-  Shader s           = i->mShader;
-  Index  skeletonIdx = i->mSkeletonIdx;
-  ++i;
+  auto iter    = requests.begin();
+  auto iterEnd = requests.end();
+
+  Shader         shader         = iter->mShader;
+  ModelPrimitive modelPrimitive = iter->mPrimitive;
+  ++iter;
   do
   {
-    // Multiple identical shader instances are removed.
-    while(i != iEnd && i->mShader == s)
+    // Multiple identical shader and primitive instances are removed.
+    while(iter != iterEnd && iter->mShader == shader && iter->mPrimitive == modelPrimitive)
     {
-      // Cannot have multiple skeletons input to the same shader.
-      // NOTE: DliModel now makes sure this doesn't happen.
-      DALI_ASSERT_ALWAYS(i->mSkeletonIdx == skeletonIdx &&
-                         "Skinning shader must not be shared between different skeletons.");
-
-      i->mShader = Shader();
-      ++i;
+      // Mark as removed
+      iter->mShader = Shader();
+      ++iter;
     }
 
-    if(i == iEnd)
+    if(iter == iterEnd)
     {
       break;
     }
-    s           = i->mShader;
-    skeletonIdx = i->mSkeletonIdx;
-    ++i;
+    shader         = iter->mShader;
+    modelPrimitive = iter->mPrimitive;
+    ++iter;
   } while(true);
 
-  requests.erase(std::remove_if(requests.begin(), requests.end(), [](const SkinningShaderConfigurationRequest& sscr) {
-                   return !sscr.mShader;
-                 }),
+  requests.erase(std::remove_if(requests.begin(), requests.end(), [](const RequestType& sscr) { return !sscr.mShader; }),
                  requests.end());
 }
 
-void ConfigureBoneMatrix(const Matrix& ibm, Actor joint, Shader& shader, Index& boneIdx)
+void ConfigureBoneMatrix(const Matrix& ibm, ModelNode joint, ModelPrimitive primitive, Index& boneIdx)
 {
   // Register bone transform on shader.
-  char propertyNameBuffer[32];
-  snprintf(propertyNameBuffer, sizeof(propertyNameBuffer), "%s[%d]", Skinning::BONE_UNIFORM_NAME, boneIdx);
-  DALI_ASSERT_DEBUG(shader.GetPropertyIndex(propertyNameBuffer) == Property::INVALID_INDEX);
-  auto propBoneXform = shader.RegisterProperty(propertyNameBuffer, Matrix{false});
-
-  // Constrain bone matrix to joint transform.
-  Constraint constraint = Constraint::New<Matrix>(shader, propBoneXform, [ibm](Matrix& output, const PropertyInputContainer& inputs) {
-    Matrix::Multiply(output, ibm, inputs[0]->GetMatrix());
-  });
-
-  auto propJointMatrix = joint.GetPropertyIndex(JOINT_MATRIX);
-  constraint.AddSource(Source{joint, propJointMatrix});
-  constraint.Apply();
-
+  Internal::GetImplementation(joint).SetBoneMatrix(ibm, primitive, boneIdx);
   ++boneIdx;
 }
 
@@ -447,7 +371,10 @@ void SceneDefinition::CountResourceRefs(Index iNode, const Customization::Choice
 
     void Register(ResourceType::Value type, Index id)
     {
-      ++(*refCounts)[type][id];
+      if((!(*refCounts)[type].Empty()) && ((*refCounts)[type].Size() > id))
+      {
+        ++(*refCounts)[type][id];
+      }
     }
   };
 
@@ -473,7 +400,7 @@ void SceneDefinition::CountResourceRefs(Index iNode, const Customization::Choice
   Visit(iNode, choices, refCounterVisitor);
 }
 
-Actor SceneDefinition::CreateNodes(Index iNode, const Customization::Choices& choices, NodeDefinition::CreateParams& params)
+ModelNode SceneDefinition::CreateNodes(Index iNode, const Customization::Choices& choices, NodeDefinition::CreateParams& params)
 {
   ActorCreatorVisitor actorCreatorVisitor(params);
 
@@ -562,8 +489,7 @@ bool SceneDefinition::ReparentNode(const std::string& name, const std::string& n
     {
       stream << i << ", ";
     }
-    LOGD(("%s", stream.str().c_str()));
-  };)
+    LOGD(("%s", stream.str().c_str())); };)
 
   // Remove node from children of previous parent (if any).
   if(node->mParentIdx != INVALID_INDEX)
@@ -695,9 +621,7 @@ NodeDefinition* SceneDefinition::FindNode(const std::string& name, Index* outInd
 {
   auto iBegin = mNodes.begin();
   auto iEnd   = mNodes.end();
-  auto iFind  = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) {
-    return nd->mName == name;
-  });
+  auto iFind  = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) { return nd->mName == name; });
 
   auto result = iFind != iEnd ? iFind->get() : nullptr;
   if(result && outIndex)
@@ -711,9 +635,7 @@ const NodeDefinition* SceneDefinition::FindNode(const std::string& name, Index*
 {
   auto iBegin = mNodes.begin();
   auto iEnd   = mNodes.end();
-  auto iFind  = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) {
-    return nd->mName == name;
-  });
+  auto iFind  = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) { return nd->mName == name; });
 
   auto result = iFind != iEnd ? iFind->get() : nullptr;
   if(result && outIndex)
@@ -727,9 +649,7 @@ Index SceneDefinition::FindNodeIndex(const NodeDefinition& node) const
 {
   auto iBegin = mNodes.begin();
   auto iEnd   = mNodes.end();
-  auto iFind  = std::find_if(iBegin, iEnd, [&node](const std::unique_ptr<NodeDefinition>& n) {
-    return n.get() == &node;
-  });
+  auto iFind  = std::find_if(iBegin, iEnd, [&node](const std::unique_ptr<NodeDefinition>& n) { return n.get() == &node; });
   return iFind != iEnd ? std::distance(iBegin, iFind) : INVALID_INDEX;
 }
 
@@ -779,8 +699,8 @@ void SceneDefinition::ApplyConstraints(Actor&                           root,
     if(iTarget != Property::INVALID_INDEX)
     {
       auto propertyType = cr.mTarget.GetPropertyType(iTarget);
-      auto iFind        = sConstraintFactory.find(propertyType);
-      if(iFind == sConstraintFactory.end())
+      auto iFind        = GetConstraintFactory().find(propertyType);
+      if(iFind == GetConstraintFactory().end())
       {
         onError(FormatString("node '%s': Property '%s' has unsupported type '%s'; ignored.",
                              sourceName,
@@ -824,167 +744,6 @@ void SceneDefinition::ApplyConstraints(Actor&                           root,
   }
 }
 
-void SceneDefinition::ConfigureSkeletonJoints(uint32_t iRoot, const SkeletonDefinition::Vector& skeletons, Actor root) const
-{
-  // 1, For each skeleton, for each joint, walk upwards until we reach mNodes[iRoot]. If we do, record +1
-  // to the refcount of each node we have visited, in our temporary registry. Those with refcount 1
-  // are the leaves, while the most descendant node with the highest refcount is the root of the skeleton.
-  std::map<Index, std::vector<Index>> rootsJoints;
-  std::vector<Index>                  path;
-  path.reserve(16);
-  for(auto& s : skeletons)
-  {
-    std::map<uint32_t, uint32_t> jointRefs;
-    for(auto& j : s.mJoints)
-    {
-      auto nodeIdx = j.mNodeIdx;
-      do // Traverse upwards and record each node we have visited until we reach the scene root.
-      {
-        path.push_back(nodeIdx);
-        if(nodeIdx == iRoot)
-        {
-          break;
-        }
-        auto node = GetNode(nodeIdx);
-        nodeIdx   = node->mParentIdx;
-      } while(nodeIdx != INVALID_INDEX);
-
-      if(nodeIdx == iRoot) // If the joint is in the correct scene, increment the reference count for all visited nodes.
-      {
-        for(auto i : path)
-        {
-          ++jointRefs[i];
-        }
-      }
-
-      path.clear();
-    }
-
-    // Only record the skeleton if we have encountered the root of the current scene.
-    if(jointRefs.empty())
-    {
-      continue;
-    }
-
-    Index    root   = s.mRootNodeIdx;
-    uint32_t maxRef = 0;
-    auto     iFind  = jointRefs.find(root);
-    if(iFind != jointRefs.end())
-    {
-      maxRef = iFind->second;
-    }
-
-    std::vector<Index> joints;
-    for(auto& j : jointRefs) // NOTE: jointRefs are sorted, so joints will also be.
-    {
-      // The most descendant node with the highest ref count is the root of the skeleton.
-      if(j.second > maxRef || (j.second == maxRef && IsAncestor(*this, root, j.first, iRoot)))
-      {
-        maxRef = j.second;
-
-        RemoveFromSorted(joints, root);
-        root = j.first;
-      }
-      else if(j.second == 1) // This one's a leaf.
-      {
-        InsertUniqueSorted(joints, j.first);
-      }
-    }
-
-    // Merge skeletons that share the same root.
-    auto& finalJoints = rootsJoints[root];
-    for(auto j : joints)
-    {
-      if(std::find_if(finalJoints.begin(), finalJoints.end(), [this, j, root](Index jj) {
-           return IsAncestor(*this, j, jj, root);
-         }) != finalJoints.end())
-      {
-        continue; // if the joint is found to be an ancestor of another joint already registered, move on.
-      }
-
-      auto i = j;
-      while(i != root) // See if the current joint is a better leaf, i.e. descended from another leaf - which we'll then remove.
-      {
-        auto node = GetNode(i);
-        i         = node->mParentIdx;
-
-        RemoveFromSorted(finalJoints, i);
-      }
-
-      InsertUniqueSorted(finalJoints, j);
-    }
-  }
-
-  // 2, Merge records where one root joint is descendant of another. Handle leaf node changes - remove previous
-  // leaf nodes that now have descendants, and add new ones.
-  auto iRoots    = rootsJoints.begin();
-  auto iRootsEnd = rootsJoints.end();
-  while(iRoots != iRootsEnd)
-  {
-    auto i      = iRoots->first;
-    bool merged = false;
-    while(i != iRoot) // Starting with the root joint of the skeleton, traverse upwards.
-    {
-      auto node = GetNode(i);
-      i         = node->mParentIdx;
-
-      auto iFind = rootsJoints.find(i);
-      if(iFind != rootsJoints.end()) // Check if we've reached the root of another skeleton.
-      {
-        // Now find out which leaf of iFind is an ancestor, if any.
-        auto iFindLeaf = std::find_if(iFind->second.begin(), iFind->second.end(), [this, iRoots, iFind](Index j) {
-          return IsAncestor(*this, j, iRoots->first, iFind->first);
-        });
-        if(iFindLeaf != iFind->second.end())
-        {
-          iFind->second.erase(iFindLeaf); // Will no longer be a leaf -- remove it.
-        }
-
-        // Merge iRoots with iFind
-        auto& targetJoints = iFind->second;
-        if(iRoots->second.empty()) // The root is a leaf.
-        {
-          InsertUniqueSorted(targetJoints, iRoots->first);
-        }
-        else
-          for(auto j : iRoots->second)
-          {
-            InsertUniqueSorted(targetJoints, j);
-          }
-
-        merged = true;
-        break; // Traverse no more
-      }
-    }
-
-    iRoots = merged ? rootsJoints.erase(iRoots) : std::next(iRoots);
-  }
-
-  // 3, For each root, register joint matrices and constraints
-  for(const auto& r : rootsJoints)
-  {
-    auto node      = GetNode(r.first);
-    auto rootJoint = root.FindChildByName(node->mName);
-    DALI_ASSERT_ALWAYS(!!rootJoint);
-
-    DALI_ASSERT_DEBUG(rootJoint.GetPropertyIndex(JOINT_MATRIX) == Property::INVALID_INDEX);
-    auto       propJointMatrix = rootJoint.RegisterProperty(JOINT_MATRIX, Matrix{false});
-    Constraint constraint      = Constraint::New<Matrix>(rootJoint, propJointMatrix, [](Matrix& output, const PropertyInputContainer& inputs) {
-      output.SetTransformComponents(Vector3::ONE, inputs[0]->GetQuaternion(), inputs[1]->GetVector3());
-    });
-    constraint.AddSource(Source(rootJoint, Actor::Property::ORIENTATION));
-    constraint.AddSource(Source(rootJoint, Actor::Property::POSITION));
-    constraint.Apply();
-
-    for(const auto j : r.second)
-    {
-      node       = GetNode(j);
-      auto joint = rootJoint.FindChildByName(node->mName);
-      ConfigureJointMatrix(joint, rootJoint, propJointMatrix);
-    }
-  }
-}
-
 void SceneDefinition::EnsureUniqueSkinningShaderInstances(ResourceBundle& resources) const
 {
   std::map<Index, std::map<Index, std::vector<Index*>>> skinningShaderUsers;
@@ -1042,7 +801,7 @@ void SceneDefinition::ConfigureSkinningShaders(const ResourceBundle&
     return;
   }
 
-  SortAndDeduplicateSkinningRequests(requests);
+  SortAndDeduplicateRequests(requests);
 
   for(auto& request : requests)
   {
@@ -1056,9 +815,13 @@ void SceneDefinition::ConfigureSkinningShaders(const ResourceBundle&
     Index boneIdx = 0;
     for(auto& joint : skeleton.mJoints)
     {
-      auto  node  = GetNode(joint.mNodeIdx);
-      Actor actor = rootActor.FindChildByName(node->mName);
-      ConfigureBoneMatrix(joint.mInverseBindMatrix, actor, request.mShader, boneIdx);
+      auto      node      = GetNode(joint.mNodeIdx);
+      ModelNode modelNode = ModelNode::DownCast(rootActor.FindChildByName(node->mName));
+      if(!modelNode)
+      {
+        continue;
+      }
+      ConfigureBoneMatrix(joint.mInverseBindMatrix, modelNode, request.mPrimitive, boneIdx);
     }
   }
 }
@@ -1073,49 +836,45 @@ bool SceneDefinition::ConfigureBlendshapeShaders(const ResourceBundle&
     return true;
   }
 
-  // Sort requests by shaders.
-  std::sort(requests.begin(), requests.end());
-
-  // Remove duplicates.
-  auto   i    = requests.begin();
-  auto   iEnd = requests.end();
-  Shader s    = i->mShader;
-  ++i;
-  do
-  {
-    // Multiple identical shader instances are removed.
-    while(i != iEnd && i->mShader == s)
-    {
-      i->mShader = Shader();
-      ++i;
-    }
-
-    if(i == iEnd)
-    {
-      break;
-    }
-    s = i->mShader;
-    ++i;
-  } while(true);
+  SortAndDeduplicateRequests(requests);
 
   // Configure the rest.
   bool ok = true;
 
-  for(auto& i : requests)
+  for(auto& request : requests)
   {
     Index iNode;
-    if(FindNode(i.mNodeName, &iNode))
+    if(FindNode(request.mNodeName, &iNode))
     {
       const auto& node = GetNode(iNode);
 
-      const auto& mesh = resources.mMeshes[i.mMeshIdx];
+      const auto& mesh = resources.mMeshes[request.mMeshIdx];
 
       if(mesh.first.HasBlendShapes())
       {
-        Actor actor = rootActor.FindChildByName(node->mName);
-
-        // Sets the property to be animated.
-        BlendShapes::ConfigureProperties(mesh, i.mShader, actor);
+        Actor              actor = rootActor.FindChildByName(node->mName);
+        Scene3D::ModelNode node  = Scene3D::ModelNode::DownCast(actor);
+        if(!node)
+        {
+          continue;
+        }
+        BlendShapes::BlendShapeData data;
+        data.components = 0x0;
+        for(auto&& blendShape : mesh.first.mBlendShapes)
+        {
+          data.names.push_back(blendShape.name);
+          data.weights.push_back(blendShape.weight);
+          data.components |= (blendShape.deltas.IsDefined() * BlendShapes::Component::POSITIONS) |
+                             (blendShape.normals.IsDefined() * BlendShapes::Component::NORMALS) | (blendShape.tangents.IsDefined() * BlendShapes::Component::TANGENTS);
+        }
+        for(auto&& factor : mesh.second.blendShapeUnnormalizeFactor)
+        {
+          data.unnormalizeFactors.push_back(factor);
+        }
+        data.version      = mesh.first.mBlendShapeVersion;
+        data.bufferOffset = mesh.second.blendShapeBufferOffset;
+        data.mActor       = actor;
+        Internal::GetImplementation(node).SetBlendShapeData(data, request.mPrimitive);
       }
     }
   }
@@ -1181,9 +940,8 @@ bool SceneDefinition::FindNode(const std::string& name, std::unique_ptr<NodeDefi
   // We're searching from the end assuming a higher probability of operations targeting
   // recently added nodes. (conf.: root, which is immovable, cannot be removed, and was
   // the first to be added, is index 0.)
-  auto iFind = std::find_if(mNodes.rbegin(), mNodes.rend(), [&name](const std::unique_ptr<NodeDefinition>& nd) {
-                 return nd->mName == name;
-               }).base();
+  auto iFind = std::find_if(mNodes.rbegin(), mNodes.rend(), [&name](const std::unique_ptr<NodeDefinition>& nd) { return nd->mName == name; })
+                 .base();
 
   const bool success = iFind != mNodes.begin();
   if(success && result)
@@ -1195,6 +953,4 @@ bool SceneDefinition::FindNode(const std::string& name, std::unique_ptr<NodeDefi
   return success;
 }
 
-} // namespace Loader
-} // namespace Scene3D
-} // namespace Dali
+} // namespace Dali::Scene3D::Loader