Parse gltf mesh extra and extensions + Get BlendShape index by name 45/292445/12
authorEunki, Hong <eunkiki.hong@samsung.com>
Mon, 8 May 2023 06:13:03 +0000 (15:13 +0900)
committerEunki Hong <eunkiki.hong@samsung.com>
Tue, 4 Jul 2023 12:37:32 +0000 (21:37 +0900)
Let we parse extra / extensions information from gltf2 mesh.
It will be used when we apply blendshape informations by string.

Since we are store the blendshape name, now we can get the index of
blendshape by name per each ModelNode.

Change-Id: I4e2030901b87c76b5fb205208c13184ae6ebd7b0
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
16 files changed:
automated-tests/resources/AnimatedMorphCubeAnimateNonZeroFrame.gltf
automated-tests/resources/MorphPrimitivesTest.gltf
automated-tests/src/dali-scene3d/utc-Dali-Model.cpp
dali-scene3d/internal/controls/model/model-impl.cpp
dali-scene3d/internal/controls/model/model-impl.h
dali-scene3d/internal/loader/gltf2-asset.h
dali-scene3d/internal/loader/gltf2-util.cpp
dali-scene3d/internal/loader/json-reader.h
dali-scene3d/internal/model-components/model-node-impl.cpp
dali-scene3d/internal/model-components/model-node-impl.h
dali-scene3d/public-api/controls/model/model.cpp
dali-scene3d/public-api/controls/model/model.h
dali-scene3d/public-api/loader/blend-shape-details.h
dali-scene3d/public-api/loader/scene-definition.cpp
dali-scene3d/public-api/model-components/model-node.cpp
dali-scene3d/public-api/model-components/model-node.h

index 08c8daf..48a9d20 100644 (file)
   ],\r
   "meshes": [\r
     {\r
+      "extras": {\r
+        "targetNames": [\r
+          "Target_0",\r
+          "Target_1"\r
+        ]\r
+      },\r
       "primitives": [\r
         {\r
           "attributes": {\r
index a987f2d..10fb1e7 100644 (file)
           ],\r
           "mode": 4\r
         }\r
-      ]\r
+      ],\r
+      "extensions": {\r
+        "SXR_targets_names": {\r
+          "Target_0": 0\r
+        },\r
+        "avatar_shape_names": {\r
+          "Target_0": 0\r
+        }\r
+      }\r
     }\r
   ],\r
   "nodes": [\r
index c219f93..b2d8169 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <dali-toolkit-test-suite-utils.h>
 #include <dali-toolkit/dali-toolkit.h>
+#include <dali/devel-api/common/map-wrapper.h>
 #include <stdlib.h>
 #include <iostream>
 
@@ -53,6 +54,7 @@ const bool DEFAULT_MODEL_CHILDREN_FOCUSABLE = false;
  */
 const char* TEST_GLTF_FILE_NAME                    = TEST_RESOURCE_DIR "/AnimatedCube.gltf";
 const char* TEST_GLTF_ANIMATION_TEST_FILE_NAME     = TEST_RESOURCE_DIR "/animationTest.gltf";
+const char* TEST_GLTF_EXTRAS_FILE_NAME             = TEST_RESOURCE_DIR "/AnimatedMorphCubeAnimateNonZeroFrame.gltf";
 const char* TEST_GLTF_MULTIPLE_PRIMITIVE_FILE_NAME = TEST_RESOURCE_DIR "/simpleMultiplePrimitiveTest.gltf";
 const char* TEST_DLI_FILE_NAME                     = TEST_RESOURCE_DIR "/arc.dli";
 const char* TEST_DLI_EXERCISE_FILE_NAME            = TEST_RESOURCE_DIR "/exercise.dli";
@@ -1135,8 +1137,7 @@ int UtcDaliModelCameraGenerate02(void)
   CameraActor appliedCamera;
   DALI_TEST_EQUALS(model.ApplyCamera(0u, appliedCamera), false, TEST_LOCATION); // Cannot apply into empty camera.
 
-  auto CompareCameraProperties = [](CameraActor lhs, CameraActor rhs, const char* location)
-  {
+  auto CompareCameraProperties = [](CameraActor lhs, CameraActor rhs, const char* location) {
     DALI_TEST_EQUALS(lhs.GetProperty<int>(Dali::CameraActor::Property::PROJECTION_MODE), rhs.GetProperty<int>(Dali::CameraActor::Property::PROJECTION_MODE), TEST_LOCATION);
     DALI_TEST_EQUALS(lhs.GetProperty<float>(Dali::CameraActor::Property::NEAR_PLANE_DISTANCE), rhs.GetProperty<float>(Dali::CameraActor::Property::NEAR_PLANE_DISTANCE), TEST_LOCATION);
 
@@ -1488,3 +1489,53 @@ int UtcDaliModelSizeChange2(void)
 
   END_TEST;
 }
+
+int UtcDaliModelRetrieveBlendShapeNames(void)
+{
+  tet_infoline(" UtcDaliModelRetrieveBlendShapeByName.");
+
+  ToolkitTestApplication application;
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_EXTRAS_FILE_NAME);
+  model.SetProperty(Dali::Actor::Property::SIZE, Vector3(300, 300, 300));
+  application.GetScene().Add(model);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(model.GetChildCount(), 1u, TEST_LOCATION);
+
+  // Get target ModelNode that has extras
+  Scene3D::ModelNode expectNode = model.FindChildModelNodeByName("AnimatedMorphCube");
+
+  // Pair of expected blend shape index from expectNode.
+  std::map<std::string, Scene3D::Loader::BlendShapes::Index> expectBlendShapeNames = {
+    {"Target_0", 0u},
+    {"Target_1", 1u},
+  };
+
+  std::vector<std::string> blendShapeNameList;
+  model.RetrieveBlendShapeNames(blendShapeNameList);
+
+  DALI_TEST_EQUALS(blendShapeNameList.size(), expectBlendShapeNames.size(), TEST_LOCATION);
+  for(auto i = 0u; i < blendShapeNameList.size(); ++i)
+  {
+    const auto& name = blendShapeNameList[i];
+    tet_printf("Check retrieved blendshape name : %s\n", name.c_str());
+
+    const auto& iter = expectBlendShapeNames.find(name);
+    DALI_TEST_CHECK(iter != expectBlendShapeNames.end());
+
+    std::vector<Scene3D::ModelNode> nodeList;
+    model.RetrieveModelNodesByBlendShapeName(name, nodeList);
+    DALI_TEST_EQUALS(nodeList.size(), 1u, TEST_LOCATION);
+    DALI_TEST_EQUALS(nodeList[0], expectNode, TEST_LOCATION);
+    DALI_TEST_EQUALS(nodeList[0].GetBlendShapeIndexByName(name), iter->second, TEST_LOCATION);
+  }
+
+  END_TEST;
+}
index 73823d5..5e0ad30 100644 (file)
@@ -118,8 +118,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);
@@ -178,7 +177,6 @@ void AddLightRecursively(Scene3D::ModelNode node, Scene3D::Light light, uint32_t
   {
     return;
   }
-
   GetImplementation(node).AddLight(light, lightIndex);
 
   uint32_t childrenCount = node.GetChildCount();
@@ -212,6 +210,27 @@ void RemoveLightRecursively(Scene3D::ModelNode node, uint32_t lightIndex)
   }
 }
 
+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);
+  }
+}
+
 } // anonymous namespace
 
 Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
@@ -516,6 +535,29 @@ 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);
+    }
+  }
+}
+
 ///////////////////////////////////////////////////////////
 //
 // Private methods
@@ -941,19 +983,21 @@ void Model::CreateModel()
   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);
@@ -971,8 +1015,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);
@@ -1003,6 +1046,14 @@ void Model::ResetCameraParameters()
   }
 }
 
+void Model::UpdateBlendShapeNodeMap()
+{
+  // Remove privous node map
+  mBlendShapeModelNodeMap.clear();
+
+  UpdateBlendShapeNodeMapRecursively(mBlendShapeModelNodeMap, mModelRoot);
+}
+
 } // namespace Internal
 } // namespace Scene3D
 } // namespace Dali
index 52aed7d..4dcd5c8 100644 (file)
@@ -20,6 +20,7 @@
 
 // EXTERNAL INCLUDES
 #include <dali-toolkit/public-api/controls/control-impl.h>
+#include <dali/devel-api/common/map-wrapper.h>
 #include <dali/public-api/actors/camera-actor.h>
 #include <dali/public-api/actors/layer.h>
 #include <dali/public-api/animation/animation.h>
@@ -51,8 +52,9 @@ namespace Internal
 class Model : public Dali::Toolkit::Internal::Control, public LightObserver
 {
 public:
-  using AnimationData = std::pair<std::string, Dali::Animation>;
-  using CameraData    = Loader::CameraParameters;
+  using AnimationData          = std::pair<std::string, Dali::Animation>;
+  using CameraData             = Loader::CameraParameters;
+  using BlendShapeModelNodeMap = std::map<std::string, std::vector<Scene3D::ModelNode>>;
 
   /**
    * @copydoc Model::New()
@@ -144,6 +146,16 @@ public:
    */
   Scene3D::ModelNode FindChildModelNodeByName(std::string_view nodeName);
 
+  /**
+   * @copydoc Model::RetrieveBlendShapeNames()
+   */
+  void RetrieveBlendShapeNames(std::vector<std::string>& blendShapeNames) const;
+
+  /**
+   * @copydoc Model::RetrieveModelNodesByBlendShapeName()
+   */
+  void RetrieveModelNodesByBlendShapeName(std::string_view blendShapeName, std::vector<Scene3D::ModelNode>& modelNodes) const;
+
 protected:
   /**
    * @brief Constructs a new Model.
@@ -325,6 +337,11 @@ private:
    */
   void ResetCameraParameters();
 
+  /**
+   * @brief Collect ModelNode list by blendshape name
+   */
+  void UpdateBlendShapeNodeMap();
+
 private:
   std::string                    mModelUrl;
   std::string                    mResourceDirectoryUrl;
@@ -337,6 +354,9 @@ private:
   // Light
   std::vector<Scene3D::Light> mLights;
 
+  // List of ModelNode for name of blend shape.
+  BlendShapeModelNodeMap mBlendShapeModelNodeMap;
+
   // Asynchronous loading variable
   ModelLoadTaskPtr          mModelLoadTask;
   EnvironmentMapLoadTaskPtr mIblDiffuseLoadTask;
index 7a8af2b..7d2c332 100644 (file)
@@ -426,10 +426,25 @@ struct Mesh : Named
     //TODO: extensions
   };
 
+  struct Extras
+  {
+    std::vector<std::string_view> mTargetNames;
+
+    //TODO: extras
+  };
+
+  struct Extensions
+  {
+    std::vector<std::string_view> mSXRTargetsNames;
+    std::vector<std::string_view> mAvatarShapeNames;
+
+    //TODO: extensions
+  };
+
   std::vector<Primitive> mPrimitives;
   std::vector<float>     mWeights;
-  //TODO: extras
-  //TODO: extensions
+  Extras                 mExtras;
+  Extensions             mExtensions;
 };
 
 struct Node;
index 219f207..90deac9 100644 (file)
@@ -281,6 +281,44 @@ const json::Reader<gltf2::Mesh::Primitive>& GetMeshPrimitiveReader()
   return MESH_PRIMITIVE_READER;
 }
 
+const json::Reader<gltf2::Mesh::Extras>& GetMeshExtrasReader()
+{
+  static const auto MESH_EXTRAS_READER = std::move(json::Reader<gltf2::Mesh::Extras>()
+                                                     .Register(*json::MakeProperty("targetNames", json::Read::Array<std::string_view, json::Read::StringView>, &gltf2::Mesh::Extras::mTargetNames)));
+  return MESH_EXTRAS_READER;
+}
+
+std::vector<std::string_view> ReadMeshExtensionsTargetsName(const json_value_s& j)
+{
+  auto&                         jsonObject = json::Cast<json_object_s>(j);
+  std::vector<std::string_view> result;
+
+  auto element = jsonObject.start;
+  while(element)
+  {
+    auto     jsonString = *element->name;
+    uint32_t index      = json::Read::Number<uint32_t>(*element->value);
+
+    if(result.size() <= index)
+    {
+      result.resize(index + 1u);
+    }
+
+    result[index] = json::Read::StringView(jsonString);
+
+    element = element->next;
+  }
+  return result;
+}
+
+const json::Reader<gltf2::Mesh::Extensions>& GetMeshExtensionsReader()
+{
+  static const auto MESH_EXTENSIONS_READER = std::move(json::Reader<gltf2::Mesh::Extensions>()
+                                                         .Register(*json::MakeProperty("SXR_targets_names", ReadMeshExtensionsTargetsName, &gltf2::Mesh::Extensions::mSXRTargetsNames))
+                                                         .Register(*json::MakeProperty("avatar_shape_names", ReadMeshExtensionsTargetsName, &gltf2::Mesh::Extensions::mAvatarShapeNames)));
+  return MESH_EXTENSIONS_READER;
+}
+
 const json::Reader<gltf2::Mesh>& GetMeshReader()
 {
   static const auto MESH_READER = std::move(json::Reader<gltf2::Mesh>()
@@ -288,7 +326,9 @@ const json::Reader<gltf2::Mesh>& GetMeshReader()
                                               .Register(*json::MakeProperty("primitives",
                                                                             json::Read::Array<gltf2::Mesh::Primitive, json::ObjectReader<gltf2::Mesh::Primitive>::Read>,
                                                                             &gltf2::Mesh::mPrimitives))
-                                              .Register(*json::MakeProperty("weights", json::Read::Array<float, json::Read::Number>, &gltf2::Mesh::mWeights)));
+                                              .Register(*json::MakeProperty("weights", json::Read::Array<float, json::Read::Number>, &gltf2::Mesh::mWeights))
+                                              .Register(*json::MakeProperty("extras", json::ObjectReader<gltf2::Mesh::Extras>::Read, &gltf2::Mesh::mExtras))
+                                              .Register(*json::MakeProperty("extensions", json::ObjectReader<gltf2::Mesh::Extensions>::Read, &gltf2::Mesh::mExtensions)));
   return MESH_READER;
 }
 
@@ -541,8 +581,7 @@ void AddTextureStage(uint32_t semantic, MaterialDefinition& materialDefinition,
 
 void ConvertMaterial(const gltf2::Material& material, const std::unordered_map<std::string, ImageMetadata>& imageMetaData, decltype(ResourceBundle::mMaterials)& outMaterials, ConversionContext& context)
 {
-  auto getTextureMetaData = [](const std::unordered_map<std::string, ImageMetadata>& metaData, const gltf2::TextureInfo& info)
-  {
+  auto getTextureMetaData = [](const std::unordered_map<std::string, ImageMetadata>& metaData, const gltf2::TextureInfo& info) {
     if(!info.mTexture->mSource->mUri.empty())
     {
       if(auto search = metaData.find(info.mTexture->mSource->mUri.data()); search != metaData.end())
@@ -796,6 +835,7 @@ void ConvertMeshes(const gltf2::Document& document, ConversionContext& context)
       {
         meshDefinition.mBlendShapes.reserve(primitive.mTargets.size());
         meshDefinition.mBlendShapeVersion = BlendShapes::Version::VERSION_2_0;
+        uint32_t blendShapeIndex          = 0u;
         for(const auto& target : primitive.mTargets)
         {
           MeshDefinition::BlendShape blendShape;
@@ -822,7 +862,22 @@ void ConvertMeshes(const gltf2::Document& document, ConversionContext& context)
             blendShape.weight = mesh.mWeights[meshDefinition.mBlendShapes.size()];
           }
 
+          // Get blendshape name from extras / SXR_targets_names / avatar_shape_names.
+          if(blendShapeIndex < mesh.mExtras.mTargetNames.size())
+          {
+            blendShape.name = mesh.mExtras.mTargetNames[blendShapeIndex];
+          }
+          else if(blendShapeIndex < mesh.mExtensions.mSXRTargetsNames.size())
+          {
+            blendShape.name = mesh.mExtensions.mSXRTargetsNames[blendShapeIndex];
+          }
+          else if(blendShapeIndex < mesh.mExtensions.mAvatarShapeNames.size())
+          {
+            blendShape.name = mesh.mExtensions.mAvatarShapeNames[blendShapeIndex];
+          }
+
           meshDefinition.mBlendShapes.push_back(std::move(blendShape));
+          ++blendShapeIndex;
         }
       }
 
@@ -900,8 +955,7 @@ void ConvertNode(gltf2::Node const& node, const Index gltfIndex, Index parentInd
   auto& resources = output.mResources;
 
   const auto index    = scene.GetNodeCount();
-  auto       weakNode = scene.AddNode([&]()
-                                {
+  auto       weakNode = scene.AddNode([&]() {
     std::unique_ptr<NodeDefinition> nodeDefinition{new NodeDefinition()};
 
     nodeDefinition->mParentIdx = parentIndex;
@@ -1333,6 +1387,8 @@ void SetObjectReaders()
   json::SetObjectReader(GetMaterialExtensionsReader());
   json::SetObjectReader(GetMaterialReader());
   json::SetObjectReader(GetMeshPrimitiveReader());
+  json::SetObjectReader(GetMeshExtrasReader());
+  json::SetObjectReader(GetMeshExtensionsReader());
   json::SetObjectReader(GetMeshReader());
   json::SetObjectReader(GetSkinReader());
   json::SetObjectReader(GetCameraPerspectiveReader());
index 100472a..45e4e42 100644 (file)
@@ -170,10 +170,15 @@ struct Read
     return static_cast<E>(number);
   }
 
+  static std::string_view StringView(const json_string_s& js)
+  {
+    return std::string_view(js.string, js.string_size);
+  }
+
   static std::string_view StringView(const json_value_s& j)
   {
     auto& js = Cast<json_string_s>(j);
-    return std::string_view(js.string, js.string_size);
+    return StringView(js);
   }
 
   static std::string String(const json_value_s& j)
index 64b3ec2..a37d4a8 100644 (file)
@@ -24,8 +24,8 @@
 #include <dali/public-api/object/type-registry.h>
 
 // INTERNAL INCLUDES
-#include <dali-scene3d/internal/model-components/model-primitive-impl.h>
 #include <dali-scene3d/internal/light/light-impl.h>
+#include <dali-scene3d/internal/model-components/model-primitive-impl.h>
 
 namespace Dali
 {
@@ -280,6 +280,25 @@ Scene3D::ModelNode ModelNode::FindChildModelNodeByName(std::string_view nodeName
   return Scene3D::ModelNode::DownCast(childActor);
 }
 
+void ModelNode::RetrieveBlendShapeNames(std::vector<std::string>& blendShapeNames) const
+{
+  blendShapeNames.reserve(blendShapeNames.size() + mBlendShapeIndexMap.size());
+  for(const auto& iter : mBlendShapeIndexMap)
+  {
+    blendShapeNames.push_back(iter.first);
+  }
+}
+
+Loader::BlendShapes::Index ModelNode::GetBlendShapeIndexByName(std::string_view blendShapeName) const
+{
+  auto iter = mBlendShapeIndexMap.find(std::string(blendShapeName));
+  if(iter != mBlendShapeIndexMap.end())
+  {
+    return iter->second;
+  }
+  return Loader::BlendShapes::INVALID_INDEX;
+}
+
 void ModelNode::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
 {
   mDiffuseTexture       = diffuseTexture;
@@ -321,6 +340,18 @@ void ModelNode::RemoveLight(uint32_t lightIndex)
 
 void ModelNode::SetBlendShapeData(Scene3D::Loader::BlendShapes::BlendShapeData& data, Scene3D::ModelPrimitive primitive)
 {
+  // Update mBlendShapeIndexMap
+  mBlendShapeIndexMap.clear();
+  const auto blendShapeCount = data.names.size();
+  for(Loader::BlendShapes::Index index = 0u; index < blendShapeCount; ++index)
+  {
+    auto& name = data.names[index];
+    if(!name.empty())
+    {
+      mBlendShapeIndexMap[name] = index;
+    }
+  }
+
   GetImplementation(primitive).SetBlendShapeData(data);
 }
 
@@ -372,8 +403,7 @@ void ModelNode::UpdateBoneMatrix(Scene3D::ModelPrimitive primitive)
 
     Matrix inverseMatrix = boneData.inverseMatrix;
     // Constrain bone matrix to joint transform.
-    boneData.constraint = Constraint::New<Matrix>(renderer, propBoneXform, [inverseMatrix](Matrix& output, const PropertyInputContainer& inputs)
-                                                  { Matrix::Multiply(output, inverseMatrix, inputs[0]->GetMatrix()); });
+    boneData.constraint = Constraint::New<Matrix>(renderer, propBoneXform, [inverseMatrix](Matrix& output, const PropertyInputContainer& inputs) { Matrix::Multiply(output, inverseMatrix, inputs[0]->GetMatrix()); });
 
     Actor joint = Self();
     boneData.constraint.AddSource(Source{joint, Actor::Property::WORLD_MATRIX});
index 4b9a66d..8a06116 100644 (file)
  */
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/common/map-wrapper.h>
 #include <dali/public-api/actors/custom-actor-impl.h>
 #include <dali/public-api/common/dali-common.h>
 #include <memory> // for std::unique_ptr
+#include <string>
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/model-components/model-primitive-modify-observer.h>
@@ -52,6 +54,7 @@ class DALI_SCENE3D_API ModelNode : public CustomActorImpl, public ModelPrimitive
 public:
   using ModelPrimitiveContainer = std::vector<Scene3D::ModelPrimitive>;
   using BoneDataContainer       = std::vector<Dali::Scene3D::Loader::Skinning::BoneData>;
+  using BlendShapeIndexMap      = std::map<std::string, Loader::BlendShapes::Index>;
 
   // Creation & Destruction
   /**
@@ -217,6 +220,16 @@ public: // Public Method
   Scene3D::ModelNode FindChildModelNodeByName(std::string_view nodeName);
 
   /**
+   * @copydoc Dali::Scene3D::ModelNode::RetrieveBlendShapeNames()
+   */
+  void RetrieveBlendShapeNames(std::vector<std::string>& blendShapeNames) const;
+
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::GetBlendShapeIndexByName()
+   */
+  Loader::BlendShapes::Index GetBlendShapeIndexByName(std::string_view blendShapeName) const;
+
+  /**
    * @brief Sets the diffuse and specular image-based lighting textures for a ModelPrimitive.
    *
    * @param[in] diffuseTexture The diffuse texture.
@@ -272,14 +285,15 @@ private:
   /// @cond internal
 
   // Not copyable or movable
-  DALI_INTERNAL            ModelNode(const ModelNode&) = delete; ///< Deleted copy constructor.
-  DALI_INTERNAL            ModelNode(ModelNode&&)      = delete; ///< Deleted move constructor.
+  DALI_INTERNAL ModelNode(const ModelNode&) = delete;            ///< Deleted copy constructor.
+  DALI_INTERNAL ModelNode(ModelNode&&)      = delete;            ///< Deleted move constructor.
   DALI_INTERNAL ModelNode& operator=(const ModelNode&) = delete; ///< Deleted copy assignment operator.
-  DALI_INTERNAL ModelNode& operator=(ModelNode&&)      = delete; ///< Deleted move assignment operator.
+  DALI_INTERNAL ModelNode& operator=(ModelNode&&) = delete;      ///< Deleted move assignment operator.
 
 private:
   ModelPrimitiveContainer mModelPrimitiveContainer; ///< List of model primitives
   BoneDataContainer       mBoneDataContainer;
+  BlendShapeIndexMap      mBlendShapeIndexMap; ///< Index of blend shape by name
   Dali::Texture           mSpecularTexture;
   Dali::Texture           mDiffuseTexture;
   float                   mIblScaleFactor{1.0f};
index 0df438c..5673d1a 100644 (file)
@@ -148,6 +148,16 @@ ModelNode Model::FindChildModelNodeByName(std::string_view nodeName)
   return GetImpl(*this).FindChildModelNodeByName(nodeName);
 }
 
+void Model::RetrieveBlendShapeNames(std::vector<std::string>& blendShapeNames) const
+{
+  GetImpl(*this).RetrieveBlendShapeNames(blendShapeNames);
+}
+
+void Model::RetrieveModelNodesByBlendShapeName(std::string_view blendShapeName, std::vector<ModelNode>& modelNodes) const
+{
+  GetImpl(*this).RetrieveModelNodesByBlendShapeName(blendShapeName, modelNodes);
+}
+
 } // namespace Scene3D
 
 } // namespace Dali
index 8025c64..75c3c7f 100644 (file)
@@ -318,11 +318,33 @@ public:
   /**
    * @brief Returns a child ModelNode object with a name that matches nodeName.
    *
+   * @SINCE_2_2.34
    * @param[in] nodeName The name of the child ModelNode object you want to find.
    * @return Returns a child ModelNode object with a name that matches nodeName. If there is no corresponding child ModelNode object, it returns an empty ModelNode object.
    */
   ModelNode FindChildModelNodeByName(std::string_view nodeName);
 
+  /**
+   * @brief Retrieve the list of blendshape name that current Model hold.
+   * The name will be appended end of input list.
+   *
+   * @SINCE_2_2.34
+   * @param[in, out] blendShapeNames The name of blendShape list collected.
+   * @note This method should be called after Model load finished.
+   */
+  void RetrieveBlendShapeNames(std::vector<std::string>& blendShapeNames) const;
+
+  /**
+   * @brief Retrieve the list of ModelNode that contains given blend shape name.
+   * The ModelNode will be appended end of input list.
+   *
+   * @SINCE_2_2.34
+   * @param[in] blendShapeName The name of blendShape that want to collect.
+   * @param[in, out] modelNodes The ModelNode list collected.
+   * @note This method should be called after Model load finished.
+   */
+  void RetrieveModelNodesByBlendShapeName(std::string_view blendShapeName, std::vector<ModelNode>& modelNodes) const;
+
 public: // Not intended for application developers
   /// @cond internal
   /**
index e7d2eb4..d7d6325 100644 (file)
@@ -22,6 +22,7 @@
 #include <dali/public-api/common/vector-wrapper.h>
 #include <dali/public-api/object/weak-handle.h>
 #include <dali/public-api/rendering/shader.h>
+#include <limits> ///< for std::numeric_limits
 #include <string>
 
 // INTERNAL INCLUDES
@@ -34,6 +35,9 @@ struct MeshGeometry;
 
 struct DALI_SCENE3D_API BlendShapes
 {
+  using Index                          = uint32_t;
+  static constexpr Index INVALID_INDEX = std::numeric_limits<Index>::max();
+
   enum class Version
   {
     VERSION_1_0,
@@ -53,12 +57,13 @@ struct DALI_SCENE3D_API BlendShapes
 
   struct BlendShapeData
   {
-    std::vector<float>      weights;
-    std::vector<float>      unnormalizeFactors;
-    Version                 version{Scene3D::Loader::BlendShapes::Version::INVALID};
-    uint32_t                bufferOffset{0};
-    int32_t                 components{0x0};
-    Dali::WeakHandle<Actor> mActor;
+    std::vector<std::string> names;
+    std::vector<float>       weights;
+    std::vector<float>       unnormalizeFactors;
+    Version                  version{Scene3D::Loader::BlendShapes::Version::INVALID};
+    uint32_t                 bufferOffset{0};
+    int32_t                  components{0x0};
+    Dali::WeakHandle<Actor>  mActor;
   };
 
   // shader properties - animatable (uniforms)
index 10f6866..cf5aa61 100644 (file)
@@ -860,6 +860,7 @@ bool SceneDefinition::ConfigureBlendshapeShaders(const ResourceBundle&
         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);
index 583cdd0..a664eeb 100644 (file)
@@ -118,6 +118,16 @@ ModelNode ModelNode::FindChildModelNodeByName(std::string_view nodeName)
   return Internal::GetImplementation(*this).FindChildModelNodeByName(nodeName);
 }
 
+void ModelNode::RetrieveBlendShapeNames(std::vector<std::string>& blendShapeNames) const
+{
+  return Internal::GetImplementation(*this).RetrieveBlendShapeNames(blendShapeNames);
+}
+
+Loader::BlendShapes::Index ModelNode::GetBlendShapeIndexByName(std::string_view blendShapeName) const
+{
+  return Internal::GetImplementation(*this).GetBlendShapeIndexByName(blendShapeName);
+}
+
 } // namespace Scene3D
 
 } // namespace Dali
index dc3e717..508b80c 100644 (file)
@@ -24,6 +24,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/public-api/api.h>
+#include <dali-scene3d/public-api/loader/blend-shape-details.h> ///< For Loader::BlendShapes::Index
 #include <dali-scene3d/public-api/model-components/model-primitive.h>
 
 namespace Dali
@@ -136,7 +137,6 @@ public:
   static ModelNode DownCast(BaseHandle handle);
 
 public: // Public Method
-
   /**
    * @brief Gets the number of ModelPrimitives this node has.
    *
@@ -181,11 +181,30 @@ public: // Public Method
   /**
    * @brief Returns a child ModelNode object with a name that matches nodeName.
    *
+   * @SINCE_2_2.99
    * @param[in] nodeName The name of the child ModelNode object you want to find.
    * @return Returns a child ModelNode object with a name that matches nodeName. If there is no corresponding child ModelNode object, it returns an empty ModelNode object.
    */
   ModelNode FindChildModelNodeByName(std::string_view nodeName);
 
+  /**
+   * @brief Retrieve the list of blendshape name that current ModelNode hold.
+   * The name will be appended end of input list.
+   *
+   * @SINCE_2_2.99
+   * @param[in, out] blendShapeNames The name of blendShape list collected.
+   */
+  void RetrieveBlendShapeNames(std::vector<std::string>& blendShapeNames) const;
+
+  /**
+   * @brief Get the index of blend shape by given name.
+   *
+   * @SINCE_2_2.99
+   * @param[in] blendShapeName The name of blendshape that is not empty.
+   * @return Index of blendshape, or return invalid if there is no blendshape with given name.
+   */
+  Loader::BlendShapes::Index GetBlendShapeIndexByName(std::string_view blendShapeName) const;
+
 public: // Not intended for application developers
   /// @cond internal
   /**