Updated model loader to handle multiple attr sets 49/293049/5
authorDavid Steele <david.steele@samsung.com>
Thu, 18 May 2023 17:13:00 +0000 (18:13 +0100)
committerDavid Steele <david.steele@samsung.com>
Tue, 7 Nov 2023 13:51:23 +0000 (13:51 +0000)
Modified GLTF parser for attributes to create attribute sets
for joints/weights/texcoords/colors

Added macro definitions to ShaderOptions, fixed hash to account for
macro/def strings.

Changed shader manager to create shader with extra attributes

Change-Id: Iabe2772eaf767ca3055dd415752c5a612942f0a4

13 files changed:
dali-scene3d/internal/graphics/shaders/default-physically-based-shader.vert
dali-scene3d/internal/graphics/shaders/shadow-map-shader.vert
dali-scene3d/internal/loader/dli-loader-impl.cpp
dali-scene3d/internal/loader/gltf2-asset.cpp
dali-scene3d/internal/loader/gltf2-asset.h
dali-scene3d/internal/loader/gltf2-util.cpp
dali-scene3d/public-api/loader/mesh-definition.cpp
dali-scene3d/public-api/loader/mesh-definition.h
dali-scene3d/public-api/loader/shader-definition.cpp
dali-scene3d/public-api/loader/shader-definition.h
dali-scene3d/public-api/loader/shader-manager.cpp
dali-scene3d/public-api/loader/shader-option.cpp
dali-scene3d/public-api/loader/shader-option.h

index 2b4f000..7ea0654 100644 (file)
@@ -6,6 +6,10 @@
 
 #define MORPH defined(MORPH_POSITION) || defined(MORPH_NORMAL) || defined(MORPH_TANGENT)
 
+// If needed, define these strings in code, insert after each.
+#define ADD_EXTRA_SKINNING_ATTRIBUTES
+#define ADD_EXTRA_WEIGHTS
+
 #ifdef HIGHP
   precision highp float;
 #else
@@ -25,8 +29,9 @@ in vec3 aTangent;
 in vec4 aVertexColor;
 
 #ifdef SKINNING
-in vec4 aJoints;
-in vec4 aWeights;
+in vec4 aJoints0;
+in vec4 aWeights0;
+ADD_EXTRA_SKINNING_ATTRIBUTES;
 #endif
 
 #ifdef MORPH
@@ -150,11 +155,18 @@ void main()
 
 #endif
 
+
+
+
 #ifdef SKINNING
-  highp mat4 bone = uBone[int(aJoints.x)] * aWeights.x +
-    uBone[int(aJoints.y)] * aWeights.y +
-    uBone[int(aJoints.z)] * aWeights.z +
-    uBone[int(aJoints.w)] * aWeights.w;
+  highp mat4 bone =
+    uBone[int(aJoints0.x)] * aWeights0.x +
+    uBone[int(aJoints0.y)] * aWeights0.y +
+    uBone[int(aJoints0.z)] * aWeights0.z +
+    uBone[int(aJoints0.w)] * aWeights0.w;
+
+  ADD_EXTRA_WEIGHTS;
+
   position = bone * position;
   normal = uYDirection * (bone * vec4(normal, 0.0)).xyz;
   tangent = uYDirection * (bone * vec4(tangent, 0.0)).xyz;
@@ -163,6 +175,7 @@ void main()
 #else
   highp vec4 positionW = uModelMatrix * position;
 #endif
+
   highp vec4 positionV = uViewMatrix * positionW;
 
   vPositionToCamera = transpose(mat3(uViewMatrix)) * -vec3(positionV.xyz / positionV.w);
index 73bb37c..945198b 100644 (file)
@@ -1,11 +1,20 @@
 #version 300 es
 
+#define ADD_EXTRA_SKINNING_ATTRIBUTES
+#define ADD_EXTRA_WEIGHTS
+
 precision highp float;
 
 in vec3 aPosition;
 in vec2 aTexCoord;
 in vec4 aVertexColor;
 
+#ifdef SKINNING
+in vec4 aJoints0;
+in vec4 aWeights0;
+ADD_EXTRA_SKINNING_ATTRIBUTES;
+#endif
+
 out mediump vec2 vUV;
 out lowp vec4 vColor;
 
@@ -14,11 +23,12 @@ uniform highp mat4 uModelMatrix;
 uniform highp mat4 uProjection;
 
 #ifdef SKINNING
-  in vec4 aJoints;
-  in vec4 aWeights;
-  #define MAX_BONES 64
-  uniform mat4 uBone[MAX_BONES];
-  uniform mediump vec3 uYDirection;
+#define MAX_BONES 256
+layout(std140) uniform Bones
+{
+  mat4 uBone[MAX_BONES];
+};
+uniform mediump vec3 uYDirection;
 #endif
 
 #ifdef MORPH
@@ -86,12 +96,14 @@ void main()
 #endif
 
 #ifdef SKINNING
-  highp mat4 bone = uBone[int(aJoints.x)] * aWeights.x +
-    uBone[int(aJoints.y)] * aWeights.y +
-    uBone[int(aJoints.z)] * aWeights.z +
-    uBone[int(aJoints.w)] * aWeights.w;
-  position = bone * position;
+  highp mat4 bone = uBone[int(aJoints0.x)] * aWeights0.x +
+    uBone[int(aJoints0.y)] * aWeights0.y +
+    uBone[int(aJoints0.z)] * aWeights0.z +
+    uBone[int(aJoints0.w)] * aWeights0.w;
 
+  ADD_EXTRA_WEIGHTS;
+
+  position = bone * position;
   highp vec4 positionW = position;
 #else
   highp vec4 positionW = uModelMatrix * position;
index 668a10c..1ed195d 100644 (file)
@@ -667,8 +667,7 @@ void DliLoaderImpl::Impl::ParseShaders(const TreeNode* shaders, Dali::Scene3D::L
     auto&            node = (*i0).second;
     ShaderDefinition shaderDef;
     ReadStringVector(node.GetChild("defines"), shaderDef.mDefines);
-    auto sssIter = std::find_if(shaderDef.mDefines.begin(), shaderDef.mDefines.end(), [](std::string& item)
-                                { return (item == "SSS"); });
+    auto sssIter = std::find_if(shaderDef.mDefines.begin(), shaderDef.mDefines.end(), [](std::string& item) { return (item == "SSS"); });
     if(sssIter != shaderDef.mDefines.end())
     {
       shaderDef.mDefines.erase(sssIter);
@@ -843,10 +842,13 @@ void DliLoaderImpl::Impl::ParseMeshes(const TreeNode* meshes, Dali::Scene3D::Loa
         mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(), "normals"));
       }
 
-      if(MaskMatch(attributes, MeshDefinition::TEX_COORDS) &&
-         !ReadAttribAccessor(node.GetChild("textures"), meshDef.mTexCoords))
+      if(MaskMatch(attributes, MeshDefinition::TEX_COORDS))
       {
-        mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(), "textures"));
+        meshDef.mTexCoords.emplace_back(MeshDefinition::Accessor{});
+        if(!ReadAttribAccessor(node.GetChild("textures"), meshDef.mTexCoords[0]))
+        {
+          mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(), "textures"));
+        }
       }
 
       if(MaskMatch(attributes, MeshDefinition::TANGENTS) &&
@@ -864,11 +866,16 @@ void DliLoaderImpl::Impl::ParseMeshes(const TreeNode* meshes, Dali::Scene3D::Loa
           mOnError(FormatString("mesh %d: Expected joints0 / weights0 attribute(s) missing.",
                                 resources.mMeshes.size()));
         }
-        else if(!ReadAttribAccessor(node.GetChild("joints0"), meshDef.mJoints0) ||
-                !ReadAttribAccessor(node.GetChild("weights0"), meshDef.mWeights0))
+        else
         {
-          mOnError(FormatString("mesh %d: Failed to read skinning information.",
-                                resources.mMeshes.size()));
+          meshDef.mJoints.emplace_back(MeshDefinition::Accessor{});
+          meshDef.mWeights.emplace_back(MeshDefinition::Accessor{});
+          if(!ReadAttribAccessor(node.GetChild("joints0"), meshDef.mJoints[0]) ||
+             !ReadAttribAccessor(node.GetChild("weights0"), meshDef.mWeights[0]))
+          {
+            mOnError(FormatString("mesh %d: Failed to read skinning information.",
+                                  resources.mMeshes.size()));
+          }
         }
       }
 
@@ -1033,17 +1040,17 @@ void DliLoaderImpl::Impl::ParseMaterials(const TreeNode* materials, DliInputPara
       materialDef.mFlags |= semantic;
     }
 
-/// @TODO : Some dli shader don't implement this subsurfaceMp usage.
-///         To make visual test pass, Skip subsurfaceMap texture using
-///         until dli shaders are support it.
-//    if(ReadString(node.GetChild("subsurfaceMap"), texturePath))
-//    {
-//      ToUnixFileSeparators(texturePath);
-//
-//      const auto semantic = MaterialDefinition::SUBSURFACE;
-//      materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
-//      materialDef.mFlags |= semantic;
-//    }
+    /// @TODO : Some dli shader don't implement this subsurfaceMp usage.
+    ///         To make visual test pass, Skip subsurfaceMap texture using
+    ///         until dli shaders are support it.
+    //    if(ReadString(node.GetChild("subsurfaceMap"), texturePath))
+    //    {
+    //      ToUnixFileSeparators(texturePath);
+    //
+    //      const auto semantic = MaterialDefinition::SUBSURFACE;
+    //      materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
+    //      materialDef.mFlags |= semantic;
+    //    }
 
     if(ReadString(node.GetChild("occlusionMap"), texturePath))
     {
@@ -1447,10 +1454,10 @@ void DliLoaderImpl::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadPara
       iAnim != iAnimEnd;
       ++iAnim)
   {
-    const TreeNode&     tnAnim = (*iAnim).second;
-    uint32_t animationPropertyIndex = 0;
+    const TreeNode&     tnAnim                 = (*iAnim).second;
+    uint32_t            animationPropertyIndex = 0;
     AnimationDefinition animDef;
-    std::string animationName;
+    std::string         animationName;
     ReadString(tnAnim.GetChild(NAME), animationName);
     animDef.SetName(animationName);
 
index a6ac398..bbc092e 100644 (file)
@@ -63,21 +63,6 @@ const std::map<std::string_view, AlphaMode::Type>& GetAlphaModeTypes()
   return ALPHA_MODE_TYPES;
 }
 
-const std::map<std::string_view, Attribute::Type>& GetAttributeTypes()
-{
-  static const std::map<std::string_view, Attribute::Type> ATTRIBUTE_TYPES{
-    ENUM_STRING_MAPPING(Attribute::Type, POSITION),
-    ENUM_STRING_MAPPING(Attribute::Type, NORMAL),
-    ENUM_STRING_MAPPING(Attribute::Type, TANGENT),
-    ENUM_STRING_MAPPING(Attribute::Type, TEXCOORD_0),
-    ENUM_STRING_MAPPING(Attribute::Type, TEXCOORD_1),
-    ENUM_STRING_MAPPING(Attribute::Type, COLOR_0),
-    ENUM_STRING_MAPPING(Attribute::Type, JOINTS_0),
-    ENUM_STRING_MAPPING(Attribute::Type, WEIGHTS_0),
-  };
-  return ATTRIBUTE_TYPES;
-}
-
 const std::map<std::string_view, Animation::Sampler::Interpolation::Type>& GetAnimationSamplerInterpolation()
 {
   static const std::map<std::string_view, Animation::Sampler::Interpolation::Type> ANIMATION_SAMPLER_INTERPOLATION{
@@ -103,10 +88,70 @@ const std::map<std::string_view, Animation::Channel::Target::Type>& GetAnimation
 
 ENUM_TYPE_FROM_STRING(AccessorType, GetAccessorTypes())
 ENUM_TYPE_FROM_STRING(AlphaMode, GetAlphaModeTypes())
-ENUM_TYPE_FROM_STRING(Attribute, GetAttributeTypes())
 ENUM_TYPE_FROM_STRING(Animation::Sampler::Interpolation, GetAnimationSamplerInterpolation())
 ENUM_TYPE_FROM_STRING(Animation::Channel::Target, GetAnimationChannelTargetPathTypes())
 
+const std::map<std::string_view, Attribute::Type>& GetTargetTypes()
+{
+  static const std::map<std::string_view, Attribute::Type> TARGET_TYPES{
+    ENUM_STRING_MAPPING(Attribute::Type, POSITION),
+    ENUM_STRING_MAPPING(Attribute::Type, TANGENT),
+    ENUM_STRING_MAPPING(Attribute::Type, NORMAL),
+  };
+  return TARGET_TYPES;
+}
+
+const std::map<Attribute::Type, const char*>& GetAttributeSetTypes()
+{
+  static const std::map<Attribute::Type, const char*> ATTRIBUTE_SET_TYPES{
+    {Attribute::Type::TEXCOORD_N, "TEXCOORD_%u"},
+    {Attribute::Type::COLOR_N, "COLOR_%u"},
+    {Attribute::Type::JOINTS_N, "JOINTS_%u"},
+    {Attribute::Type::WEIGHTS_N, "WEIGHTS_%u"},
+  };
+  return ATTRIBUTE_SET_TYPES;
+}
+
+uint32_t Attribute::HashFromString(const char* token, size_t length)
+{
+  auto& table1 = GetTargetTypes();
+  auto& table2 = GetAttributeSetTypes();
+
+  std::string target(token, length);
+  std::transform(target.begin(), target.end(), target.begin(), ::toupper);
+
+  auto iFind = table1.find(std::string_view(target.c_str(), length));
+  if(iFind != table1.end())
+  {
+    return Attribute::ToHash(iFind->second, false, 0);
+  }
+
+  uint32_t hash = Attribute::ToHash(Attribute::INVALID, false, 0);
+  for(const auto& [key, match] : table2)
+  {
+    int setIndex;
+    if(sscanf(target.c_str(), match, &setIndex) > 0)
+    {
+      hash = Attribute::ToHash(key, true, setIndex);
+      break;
+    }
+  }
+  return hash;
+}
+
+Attribute::Type Attribute::TargetFromString(const char* token, size_t length)
+{
+  std::string target(token, length);
+  std::transform(target.begin(), target.end(), target.begin(), ::toupper);
+
+  auto iFind = GetTargetTypes().find(std::string_view(target.c_str(), length));
+  if(iFind != GetTargetTypes().end())
+  {
+    return iFind->second;
+  }
+  return Attribute::INVALID;
+}
+
 bool Component::IsUnsigned(Type t)
 {
   return t == UNSIGNED_BYTE || t == UNSIGNED_SHORT || t == UNSIGNED_INT;
index 8488c1e..e90cf38 100644 (file)
@@ -180,15 +180,57 @@ struct Attribute
     POSITION,
     NORMAL,
     TANGENT,
-    TEXCOORD_0,
-    TEXCOORD_1,
-    COLOR_0,
-    JOINTS_0,
-    WEIGHTS_0,
+    TEXCOORD_N,
+    COLOR_N,
+    JOINTS_N,
+    WEIGHTS_N,
     INVALID
   };
 
-  static Type FromString(const char* s, size_t len);
+  using HashType = uint32_t;
+
+  // Hash bit layout
+  // +--+--+--+--+--+--+--+
+  // |31|30|29|28|27|..| 0| bit index
+  // +--+--+--+--+--+--+--+
+  //  \_/ - Set is used
+  //     \______/ - Type enum
+  //              \_______/ - Set ID
+  static const HashType SET_SHIFT{31};
+  static const HashType TYPE_SHIFT{28};
+  static const HashType SET_MASK{0x01u << SET_SHIFT};
+  static const HashType TYPE_MASK{0x07 << TYPE_SHIFT};
+  static const HashType SET_ID_MASK{0x0fffffff};
+
+  static HashType ToHash(Type type, bool set, HashType setIndex)
+  {
+    return ((set << SET_SHIFT) & SET_MASK) | ((static_cast<HashType>(type) << TYPE_SHIFT) & TYPE_MASK) | (setIndex & SET_ID_MASK);
+  }
+
+  static Attribute::Type TypeFromHash(HashType hash)
+  {
+    return static_cast<Type>((hash & TYPE_MASK) >> TYPE_SHIFT);
+  }
+
+  static bool SetFromHash(HashType hash)
+  {
+    return (hash & SET_SHIFT) != 0;
+  }
+
+  static HashType SetIdFromHash(HashType hash)
+  {
+    return (hash & SET_ID_MASK);
+  }
+
+  /**
+   * Convert to Type + setIndex, where setIndex is N for that attr, e.g. "JOINTS_1" => {JOINTS_N, 1}
+   */
+  static HashType HashFromString(const char* s, size_t len);
+
+  /**
+   * Convert to type only, there is no set for POSITION, NORMALS or TANGENT.
+   */
+  static Attribute::Type TargetFromString(const char* s, size_t len);
 
   Attribute() = delete;
 };
@@ -416,7 +458,7 @@ struct Mesh : Named
       INVALID
     };
 
-    std::map<Attribute::Type, Ref<Accessor>>              mAttributes;
+    std::map<Attribute::HashType, Ref<Accessor>>          mAttributes;
     std::vector<std::map<Attribute::Type, Ref<Accessor>>> mTargets;
     Ref<Accessor>                                         mIndices;
     Ref<Material>                                         mMaterial;
index a6e3462..8abb5bb 100644 (file)
@@ -48,30 +48,14 @@ static const Geometry::Type GLTF2_TO_DALI_PRIMITIVES[]{
   Geometry::TRIANGLE_STRIP,
   Geometry::TRIANGLE_FAN}; //...because Dali swaps the last two.
 
-static struct AttributeMapping
-{
-  gltf2::Attribute::Type   mType;
-  MeshDefinition::Accessor MeshDefinition::*mAccessor;
-  uint16_t                                  mElementSizeRequired;
-} ATTRIBUTE_MAPPINGS[]{
-  {gltf2::Attribute::NORMAL, &MeshDefinition::mNormals, sizeof(Vector3)},
-  {gltf2::Attribute::TANGENT, &MeshDefinition::mTangents, sizeof(Vector3)},
-  {gltf2::Attribute::TEXCOORD_0, &MeshDefinition::mTexCoords, sizeof(Vector2)},
-  {gltf2::Attribute::COLOR_0, &MeshDefinition::mColors, sizeof(Vector4)},
-  {gltf2::Attribute::JOINTS_0, &MeshDefinition::mJoints0, sizeof(Vector4)},
-  {gltf2::Attribute::WEIGHTS_0, &MeshDefinition::mWeights0, sizeof(Vector4)},
-};
-
 static const Dali::Scripting::StringEnum EXTENSION_STRING_TABLE[] =
-  {
-    {"NONE", gltf2::ExtensionFlags::NONE},
-    {"KHR_mesh_quantization", gltf2::ExtensionFlags::KHR_MESH_QUANTIZATION},
-    {"KHR_texture_transform", gltf2::ExtensionFlags::KHR_TEXTURE_TRANSFORM},
-    {"KHR_materials_ior", gltf2::ExtensionFlags::KHR_MATERIALS_IOR},
-    {"KHR_materials_specular", gltf2::ExtensionFlags::KHR_MATERIALS_SPECULAR},
-
+{
+  {"NONE", gltf2::ExtensionFlags::NONE},
+  {"KHR_mesh_quantization", gltf2::ExtensionFlags::KHR_MESH_QUANTIZATION},
+  {"KHR_texture_transform", gltf2::ExtensionFlags::KHR_TEXTURE_TRANSFORM},
+  {"KHR_materials_ior", gltf2::ExtensionFlags::KHR_MATERIALS_IOR},
+  {"KHR_materials_specular", gltf2::ExtensionFlags::KHR_MATERIALS_SPECULAR},
 };
-
 static const unsigned int EXTENSION_STRING_TABLE_COUNT = sizeof(EXTENSION_STRING_TABLE) / sizeof(EXTENSION_STRING_TABLE[0]);
 
 std::vector<gltf2::Animation> ReadAnimationArray(const json_value_s& j)
@@ -147,9 +131,7 @@ const json::Reader<gltf2::Accessor>& GetAccessorReader()
                                                   .Register(*new json::Property<gltf2::Accessor, gltf2::Ref<gltf2::BufferView>>("bufferView",
                                                                                                                                 gltf2::RefReader<gltf2::Document>::Read<gltf2::BufferView, &gltf2::Document::mBufferViews>,
                                                                                                                                 &gltf2::Accessor::mBufferView))
-                                                  .Register(*new json::Property<gltf2::Accessor, uint32_t>("byteOffset",
-                                                                                                           json::Read::Number<uint32_t>,
-                                                                                                           &gltf2::Accessor::mByteOffset))
+                                                  .Register(*new json::Property<gltf2::Accessor, uint32_t>("byteOffset", json::Read::Number<uint32_t>, &gltf2::Accessor::mByteOffset))
                                                   .Register(*new json::Property<gltf2::Accessor, gltf2::Component::Type>("componentType",
                                                                                                                          json::Read::Enum<gltf2::Component::Type>,
                                                                                                                          &gltf2::Accessor::mComponentType))
@@ -253,7 +235,26 @@ const json::Reader<gltf2::Material>& GetMaterialReader()
   return MATERIAL_READER;
 }
 
-std::map<gltf2::Attribute::Type, gltf2::Ref<gltf2::Accessor>> ReadMeshPrimitiveAttributes(const json_value_s& j)
+std::map<gltf2::Attribute::HashType, gltf2::Ref<gltf2::Accessor>> ReadMeshPrimitiveAttributes(const json_value_s& j)
+{
+  auto&                                                             jsonObject = json::Cast<json_object_s>(j);
+  std::map<gltf2::Attribute::HashType, gltf2::Ref<gltf2::Accessor>> result;
+
+  auto element = jsonObject.start;
+  while(element)
+  {
+    auto jsonString = *element->name;
+
+    gltf2::Attribute::HashType hash = gltf2::Attribute::HashFromString(jsonString.string, jsonString.string_size);
+
+    result[hash] =
+      gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>(*element->value);
+    element = element->next;
+  }
+  return result;
+}
+
+std::map<gltf2::Attribute::Type, gltf2::Ref<gltf2::Accessor>> ReadMeshPrimitiveAttributes2(const json_value_s& j)
 {
   auto&                                                         jsonObject = json::Cast<json_object_s>(j);
   std::map<gltf2::Attribute::Type, gltf2::Ref<gltf2::Accessor>> result;
@@ -261,9 +262,11 @@ std::map<gltf2::Attribute::Type, gltf2::Ref<gltf2::Accessor>> ReadMeshPrimitiveA
   auto element = jsonObject.start;
   while(element)
   {
-    auto jsonString                                                                 = *element->name;
-    result[gltf2::Attribute::FromString(jsonString.string, jsonString.string_size)] = gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>(*element->value);
-    element                                                                         = element->next;
+    auto jsonString = *element->name;
+
+    result[gltf2::Attribute::TargetFromString(jsonString.string, jsonString.string_size)] = gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>(*element->value);
+
+    element = element->next;
   }
   return result;
 }
@@ -278,7 +281,7 @@ std::vector<std::map<gltf2::Attribute::Type, gltf2::Ref<gltf2::Accessor>>> ReadM
   auto element = jsonObject.start;
   while(element)
   {
-    result.push_back(std::move(ReadMeshPrimitiveAttributes(*element->value)));
+    result.push_back(std::move(ReadMeshPrimitiveAttributes2(*element->value)));
     element = element->next;
   }
 
@@ -764,13 +767,155 @@ MeshDefinition::Accessor ConvertMeshPrimitiveAccessor(const gltf2::Accessor& acc
 
   return MeshDefinition::Accessor{
     std::move(MeshDefinition::Blob{bufferViewOffset + accessor.mByteOffset,
-                                   accessor.GetBytesLength(),
-                                   static_cast<uint16_t>(bufferViewStride),
-                                   static_cast<uint16_t>(accessor.GetElementSizeBytes()),
-                                   accessor.mMin,
-                                   accessor.mMax}),
-    std::move(sparseBlob),
-    accessor.mBufferView ? accessor.mBufferView->mBuffer.GetIndex() : 0};
+          accessor.GetBytesLength(),
+          static_cast<uint16_t>(bufferViewStride),
+          static_cast<uint16_t>(accessor.GetElementSizeBytes()),
+          accessor.mMin,
+          accessor.mMax}),
+      std::move(sparseBlob),
+      accessor.mBufferView ? accessor.mBufferView->mBuffer.GetIndex() : 0,
+      accessor.mNormalized};
+}
+
+MeshDefinition::Accessor* GetAccessorFromAttribute(gltf2::Attribute::HashType attributeHash,
+                                                   MeshDefinition& meshDefinition,
+                                                   bool& needNormals, bool& needTangents)
+{
+  MeshDefinition::Accessor* accessorDest{nullptr};
+
+  switch(gltf2::Attribute::TypeFromHash(attributeHash))
+  {
+    case gltf2::Attribute::POSITION:
+    {
+      accessorDest = &meshDefinition.mPositions;
+      break;
+    }
+    case gltf2::Attribute::NORMAL:
+    {
+      accessorDest = &meshDefinition.mNormals;
+      needNormals  = false;
+      break;
+    }
+    case gltf2::Attribute::TANGENT:
+    {
+      accessorDest = &meshDefinition.mTangents;
+      needTangents = false;
+      break;
+    }
+    case gltf2::Attribute::TEXCOORD_N:
+    {
+      meshDefinition.mTexCoords.emplace_back(MeshDefinition::Accessor{});
+      accessorDest = &meshDefinition.mTexCoords.back();
+      break;
+    }
+    case gltf2::Attribute::COLOR_N:
+    {
+      meshDefinition.mColors.emplace_back(MeshDefinition::Accessor{});
+      accessorDest = &meshDefinition.mColors.back();
+      break;
+    }
+    case gltf2::Attribute::JOINTS_N:
+    {
+      meshDefinition.mJoints.emplace_back(MeshDefinition::Accessor{});
+      accessorDest = &meshDefinition.mJoints.back();
+      break;
+    }
+    case gltf2::Attribute::WEIGHTS_N:
+    {
+      meshDefinition.mWeights.emplace_back(MeshDefinition::Accessor{});
+      accessorDest = &meshDefinition.mWeights.back();
+      break;
+    }
+    case gltf2::Attribute::INVALID:
+    {
+      accessorDest = nullptr;
+      break;
+    }
+  }
+  return accessorDest;
+}
+
+void SetFlagsFromComponentType(const gltf2::Accessor& accessor,
+                               gltf2::Attribute::HashType attributeHash,
+                               MeshDefinition& meshDefinition,
+                               bool isQuantized)
+{
+  switch(gltf2::Attribute::TypeFromHash(attributeHash))
+  {
+    case gltf2::Attribute::POSITION:
+    {
+      if(isQuantized)
+      {
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_POSITION;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_POSITION;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_POSITION;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_POSITION;
+      }
+      DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_POSITION) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_POSITION) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_POSITION) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_POSITION))) || accessor.mComponentType == gltf2::Component::FLOAT);
+      break;
+    }
+    case gltf2::Attribute::NORMAL:
+    {
+      if(isQuantized)
+      {
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_NORMAL;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_NORMAL;
+      }
+
+      DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_NORMAL) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_NORMAL))) || accessor.mComponentType == gltf2::Component::FLOAT);
+      break;
+    }
+    case gltf2::Attribute::TANGENT:
+    {
+      if(isQuantized)
+      {
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_TANGENT;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_TANGENT;
+      }
+
+      DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_TANGENT) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_TANGENT))) || accessor.mComponentType == gltf2::Component::FLOAT);
+      break;
+    }
+    case gltf2::Attribute::TEXCOORD_N:
+    {
+      if(isQuantized)
+      {
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_TEXCOORD;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_TEXCOORD;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_TEXCOORD;
+        meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_TEXCOORD;
+      }
+      DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_TEXCOORD) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_TEXCOORD) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_TEXCOORD) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_TEXCOORD))) || accessor.mComponentType == gltf2::Component::FLOAT);
+      break;
+    }
+    case gltf2::Attribute::COLOR_N:
+    {
+      break;
+    }
+    case gltf2::Attribute::JOINTS_N:
+    {
+      meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_JOINT_IDS;
+      meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_JOINT_IDS;
+      DALI_ASSERT_DEBUG(MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_JOINT_IDS) ||
+                        MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_JOINT_IDS) ||
+                        accessor.mComponentType == gltf2::Component::FLOAT);
+      break;
+    }
+    case gltf2::Attribute::WEIGHTS_N:
+    {
+      meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_WEIGHT;
+      meshDefinition.mFlags |= (accessor.mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_WEIGHT;
+      DALI_ASSERT_DEBUG(MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_WEIGHT) ||
+                        MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_WEIGHT) ||
+                        accessor.mComponentType == gltf2::Component::FLOAT);
+
+      break;
+    }
+    case gltf2::Attribute::INVALID:
+    {
+      break;
+    }
+  }
 }
 
 void ConvertMeshes(const gltf2::Document& document, ConversionContext& context)
@@ -796,108 +941,40 @@ void ConvertMeshes(const gltf2::Document& document, ConversionContext& context)
       auto& attribs                 = primitive.mAttributes;
       meshDefinition.mPrimitiveType = GLTF2_TO_DALI_PRIMITIVES[primitive.mMode];
 
-      auto positionIter = attribs.find(gltf2::Attribute::POSITION);
-
+      auto positionIter = attribs.find(gltf2::Attribute::ToHash(gltf2::Attribute::POSITION, false, 0));
       if(positionIter == attribs.end())
       {
         DALI_LOG_ERROR("Primitive mesh dosn't have POSITION atrributes!");
         continue;
       }
 
-      auto& accPositions                    = *positionIter->second;
-      meshDefinition.mPositions             = ConvertMeshPrimitiveAccessor(accPositions);
-      meshDefinition.mPositions.mNormalized = accPositions.mNormalized;
-
-      if(isQuantized)
-      {
-        meshDefinition.mFlags |= (accPositions.mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_POSITION;
-        meshDefinition.mFlags |= (accPositions.mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_POSITION;
-        meshDefinition.mFlags |= (accPositions.mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_POSITION;
-        meshDefinition.mFlags |= (accPositions.mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_POSITION;
-      }
-
-      DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_POSITION) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_POSITION) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_POSITION) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_POSITION))) || accPositions.mComponentType == gltf2::Component::FLOAT);
+      auto& positionsAccessor = *positionIter->second;
 
       // glTF2 support vector4 tangent for mesh.
       // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview
       meshDefinition.mTangentType = Property::VECTOR4;
 
-      const bool needNormalsTangents = accPositions.mType == gltf2::AccessorType::VEC3;
-      for(auto& attributeMapping : ATTRIBUTE_MAPPINGS)
+      bool needNormals  = (positionsAccessor.mType == gltf2::AccessorType::VEC3);
+      bool needTangents = (positionsAccessor.mType == gltf2::AccessorType::VEC3);
+
+      for(const auto& [attributeHash, accessor] : attribs)
       {
-        auto iFind = attribs.find(attributeMapping.mType);
-        if(iFind != attribs.end())
+        MeshDefinition::Accessor* accessorDest = GetAccessorFromAttribute(attributeHash, meshDefinition, needNormals, needTangents);
+        if(accessorDest == nullptr)
         {
-          auto& accessor = meshDefinition.*(attributeMapping.mAccessor);
-          accessor       = ConvertMeshPrimitiveAccessor(*iFind->second);
-
-          if(iFind->first == gltf2::Attribute::NORMAL)
-          {
-            meshDefinition.mNormals.mNormalized = iFind->second->mNormalized;
-
-            if(isQuantized)
-            {
-              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_NORMAL;
-              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_NORMAL;
-            }
-
-            DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_NORMAL) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_NORMAL))) || iFind->second->mComponentType == gltf2::Component::FLOAT);
-          }
-          if(iFind->first == gltf2::Attribute::TANGENT)
-          {
-            meshDefinition.mTangents.mNormalized = iFind->second->mNormalized;
-
-            if(isQuantized)
-            {
-              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_TANGENT;
-              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_TANGENT;
-            }
-
-            DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_TANGENT) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_TANGENT))) || iFind->second->mComponentType == gltf2::Component::FLOAT);
-          }
-          if(iFind->first == gltf2::Attribute::TEXCOORD_0 || iFind->first == gltf2::Attribute::TEXCOORD_1)
-          {
-            meshDefinition.mTexCoords.mNormalized = iFind->second->mNormalized;
-
-            if(isQuantized)
-            {
-              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::BYTE) * MeshDefinition::S8_TEXCOORD;
-              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_TEXCOORD;
-              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::SHORT) * MeshDefinition::S16_TEXCOORD;
-              meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_TEXCOORD;
-            }
-
-            DALI_ASSERT_DEBUG((isQuantized && (MaskMatch(meshDefinition.mFlags, MeshDefinition::S8_TEXCOORD) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_TEXCOORD) || MaskMatch(meshDefinition.mFlags, MeshDefinition::S16_TEXCOORD) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_TEXCOORD))) || iFind->second->mComponentType == gltf2::Component::FLOAT);
-          }
-          if(iFind->first == gltf2::Attribute::JOINTS_0)
-          {
-            meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_JOINT_IDS;
-            meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_JOINT_IDS;
-            DALI_ASSERT_DEBUG(MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_JOINT_IDS) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_JOINT_IDS) || iFind->second->mComponentType == gltf2::Component::FLOAT);
-          }
-          if(iFind->first == gltf2::Attribute::WEIGHTS_0)
-          {
-            meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_SHORT) * MeshDefinition::U16_WEIGHT;
-            meshDefinition.mFlags |= (iFind->second->mComponentType == gltf2::Component::UNSIGNED_BYTE) * MeshDefinition::U8_WEIGHT;
-            DALI_ASSERT_DEBUG(MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_WEIGHT) || MaskMatch(meshDefinition.mFlags, MeshDefinition::U8_WEIGHT) || iFind->second->mComponentType == gltf2::Component::FLOAT);
-          }
+          continue;
         }
-        else if(needNormalsTangents)
-        {
-          switch(attributeMapping.mType)
-          {
-            case gltf2::Attribute::NORMAL:
-              meshDefinition.RequestNormals();
-              break;
-
-            case gltf2::Attribute::TANGENT:
-              meshDefinition.RequestTangents();
-              break;
+        *accessorDest = ConvertMeshPrimitiveAccessor(*accessor);
+        SetFlagsFromComponentType(*accessor, attributeHash, meshDefinition, isQuantized);
+      }
 
-            default:
-              break;
-          }
-        }
+      if(needNormals)
+      {
+        meshDefinition.RequestNormals();
+      }
+      if(needTangents)
+      {
+        meshDefinition.RequestTangents();
       }
 
       if(primitive.mIndices)
index 264f2fa..34f36f7 100644 (file)
@@ -211,7 +211,7 @@ bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source
 }
 
 template<typename T>
-void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath)
+void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath, const std::string& name)
 {
   constexpr auto sizeofBlobUnit = sizeof(T) * 4;
 
@@ -241,11 +241,27 @@ void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Acces
       ++floats;
     }
   }
-  raw.mAttribs.push_back({"aJoints", Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
+  raw.mAttribs.push_back({name, Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
+}
+
+void ReadTypedJointAccessor(MeshDefinition::RawData& raw, uint32_t flags, MeshDefinition::Accessor& accessor, std::iostream& stream, std::string& path, const std::string& name)
+{
+  if(MaskMatch(flags, MeshDefinition::U16_JOINT_IDS))
+  {
+    ReadJointAccessor<uint16_t>(raw, accessor, stream, path, name);
+  }
+  else if(MaskMatch(flags, MeshDefinition::U8_JOINT_IDS))
+  {
+    ReadJointAccessor<uint8_t>(raw, accessor, stream, path, name);
+  }
+  else
+  {
+    ReadJointAccessor<float>(raw, accessor, stream, path, name);
+  }
 }
 
 template<typename T>
-void ReadWeightAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath)
+void ReadWeightAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath, const std::string& name)
 {
   constexpr auto sizeofBlobUnit = sizeof(T) * 4;
 
@@ -276,7 +292,23 @@ void ReadWeightAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Acce
       ++floats;
     }
   }
-  raw.mAttribs.push_back({"aWeights", Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
+  raw.mAttribs.push_back({name, Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
+}
+
+void ReadTypedWeightAccessor(MeshDefinition::RawData& raw, uint32_t flags, MeshDefinition::Accessor& accessor, std::iostream& stream, std::string& path, std::string name)
+{
+  if(MaskMatch(flags, MeshDefinition::U16_WEIGHT))
+  {
+    ReadWeightAccessor<uint16_t>(raw, accessor, stream, path, name);
+  }
+  else if(MaskMatch(flags, MeshDefinition::U8_WEIGHT))
+  {
+    ReadWeightAccessor<uint8_t>(raw, accessor, stream, path, name);
+  }
+  else
+  {
+    ReadWeightAccessor<float>(raw, accessor, stream, path, name);
+  }
 }
 
 template<bool use32BitsIndices, typename IndexProviderType = IndexProvider<use32BitsIndices>>
@@ -827,19 +859,23 @@ MeshDefinition::SparseBlob::SparseBlob(Blob&& indices, Blob&& values, uint32_t c
 
 MeshDefinition::Accessor::Accessor(const MeshDefinition::Blob&       blob,
                                    const MeshDefinition::SparseBlob& sparse,
-                                   Index                             bufferIndex)
+                                   Index                             bufferIndex,
+                                   bool                              normalized)
 : mBlob{blob},
   mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{sparse} : nullptr},
-  mBufferIdx(bufferIndex)
+  mBufferIdx(bufferIndex),
+  mNormalized(normalized)
 {
 }
 
 MeshDefinition::Accessor::Accessor(MeshDefinition::Blob&&       blob,
                                    MeshDefinition::SparseBlob&& sparse,
-                                   Index                        bufferIndex)
+                                   Index                        bufferIndex,
+                                   bool                         normalized)
 : mBlob{std::move(blob)},
   mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{std::move(sparse)} : nullptr},
-  mBufferIdx(bufferIndex)
+  mBufferIdx(bufferIndex),
+  mNormalized(normalized)
 {
 }
 
@@ -929,7 +965,7 @@ bool MeshDefinition::IsQuad() const
 
 bool MeshDefinition::IsSkinned() const
 {
-  return mJoints0.IsDefined() && mWeights0.IsDefined();
+  return !mJoints.empty() && !mWeights.empty();
 }
 
 bool MeshDefinition::HasBlendShapes() const
@@ -1160,31 +1196,30 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
     }
   }
 
-  const auto hasUvs = mTexCoords.IsDefined();
-  if(hasUvs)
+  if(!mTexCoords.empty() && mTexCoords[0].IsDefined())
   {
-    const auto bufferSize = mTexCoords.mBlob.GetBufferSize();
-
+    auto& texCoords = mTexCoords[0];
+    const auto bufferSize = texCoords.mBlob.GetBufferSize();
     uint32_t uvCount;
 
     if(MaskMatch(mFlags, S8_TEXCOORD) || MaskMatch(mFlags, U8_TEXCOORD))
     {
-      DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % (sizeof(uint8_t) * 2) == 0) ||
-                          mTexCoords.mBlob.mStride >= (sizeof(uint8_t) * 2)) &&
+      DALI_ASSERT_ALWAYS(((texCoords.mBlob.mLength % (sizeof(uint8_t) * 2) == 0) ||
+                          texCoords.mBlob.mStride >= (sizeof(uint8_t) * 2)) &&
                          "TexCoords buffer length not a multiple of element size");
       uvCount = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 2));
     }
     else if(MaskMatch(mFlags, S16_TEXCOORD) || MaskMatch(mFlags, U16_TEXCOORD))
     {
-      DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % (sizeof(uint16_t) * 2) == 0) ||
-                          mTexCoords.mBlob.mStride >= (sizeof(uint16_t) * 2)) &&
+      DALI_ASSERT_ALWAYS(((texCoords.mBlob.mLength % (sizeof(uint16_t) * 2) == 0) ||
+                          texCoords.mBlob.mStride >= (sizeof(uint16_t) * 2)) &&
                          "TexCoords buffer length not a multiple of element size");
       uvCount = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 2));
     }
     else
     {
-      DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % sizeof(Vector2) == 0) ||
-                          mTexCoords.mBlob.mStride >= sizeof(Vector2)) &&
+      DALI_ASSERT_ALWAYS(((texCoords.mBlob.mLength % sizeof(Vector2) == 0) ||
+                          texCoords.mBlob.mStride >= sizeof(Vector2)) &&
                          "TexCoords buffer length not a multiple of element size");
       uvCount = static_cast<uint32_t>(bufferSize / sizeof(Vector2));
     }
@@ -1192,13 +1227,13 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
     std::vector<uint8_t> buffer(bufferSize);
 
     std::string path;
-    auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mTexCoords.mBufferIdx], path);
-    if(!ReadAccessor(mTexCoords, stream, buffer.data()))
+    auto&       stream = GetAvailableData(fileStream, meshPath, buffers[texCoords.mBufferIdx], path);
+    if(!ReadAccessor(texCoords, stream, buffer.data()))
     {
       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << path << "'.";
     }
 
-    GetDequantizedData(buffer, 2u, uvCount, mFlags & TEXCOORDS_MASK, mTexCoords.mNormalized);
+    GetDequantizedData(buffer, 2u, uvCount, mFlags & TEXCOORDS_MASK, texCoords.mNormalized);
 
     if(MaskMatch(mFlags, FLIP_UVS_VERTICAL))
     {
@@ -1211,13 +1246,12 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
       }
     }
 
-    if(mTexCoords.mNormalized)
+    if(texCoords.mNormalized)
     {
-      GetDequantizedMinMax(mTexCoords.mBlob.mMin, mTexCoords.mBlob.mMax, mFlags & TEXCOORDS_MASK);
+      GetDequantizedMinMax(texCoords.mBlob.mMin, texCoords.mBlob.mMax, mFlags & TEXCOORDS_MASK);
     }
 
-    mTexCoords.mBlob.ApplyMinMax(static_cast<uint32_t>(uvCount), reinterpret_cast<float*>(buffer.data()));
-
+    texCoords.mBlob.ApplyMinMax(static_cast<uint32_t>(uvCount), reinterpret_cast<float*>(buffer.data()));
     raw.mAttribs.push_back({"aTexCoord", Property::VECTOR2, static_cast<uint32_t>(uvCount), std::move(buffer)});
   }
 
@@ -1297,6 +1331,7 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
             GenerateTangents<true, true, true>,
           },
         }};
+    const bool hasUvs            = !mTexCoords.empty() && mTexCoords[0].IsDefined();
     const bool generateSuccessed = GenerateTangentsFunction[MaskMatch(mFlags, U32_INDICES)][mTangentType == Property::VECTOR3][hasUvs](raw);
     if(!generateSuccessed)
     {
@@ -1304,25 +1339,26 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
     }
   }
 
-  if(mColors.IsDefined())
+  // Only support 1 vertex color
+  if(!mColors.empty() && mColors[0].IsDefined())
   {
-    uint32_t       propertySize = mColors.mBlob.mElementSizeHint;
+    uint32_t       propertySize = mColors[0].mBlob.mElementSizeHint;
     Property::Type propertyType = (propertySize == sizeof(Vector4)) ? Property::VECTOR4 : ((propertySize == sizeof(Vector3)) ? Property::VECTOR3 : Property::NONE);
     if(propertyType != Property::NONE)
     {
-      DALI_ASSERT_ALWAYS(((mColors.mBlob.mLength % propertySize == 0) ||
-                          mColors.mBlob.mStride >= propertySize) &&
+      DALI_ASSERT_ALWAYS(((mColors[0].mBlob.mLength % propertySize == 0) ||
+                          mColors[0].mBlob.mStride >= propertySize) &&
                          "Colors buffer length not a multiple of element size");
-      const auto           bufferSize = mColors.mBlob.GetBufferSize();
+      const auto           bufferSize = mColors[0].mBlob.GetBufferSize();
       std::vector<uint8_t> buffer(bufferSize);
 
       std::string path;
-      auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mColors.mBufferIdx], path);
-      if(!ReadAccessor(mColors, stream, buffer.data()))
+      auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mColors[0].mBufferIdx], path);
+      if(!ReadAccessor(mColors[0], stream, buffer.data()))
       {
         ExceptionFlinger(ASSERT_LOCATION) << "Failed to read colors from '" << path << "'.";
       }
-      mColors.mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast<float*>(buffer.data()));
+      mColors[0].mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast<float*>(buffer.data()));
 
       raw.mAttribs.push_back({"aVertexColor", propertyType, static_cast<uint32_t>(bufferSize / propertySize), std::move(buffer)});
     }
@@ -1342,34 +1378,25 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
 
   if(IsSkinned())
   {
-    std::string pathJoint;
-    auto&       streamJoint = GetAvailableData(fileStream, meshPath, buffers[mJoints0.mBufferIdx], pathJoint);
-    if(MaskMatch(mFlags, U16_JOINT_IDS))
-    {
-      ReadJointAccessor<uint16_t>(raw, mJoints0, streamJoint, pathJoint);
-    }
-    else if(MaskMatch(mFlags, U8_JOINT_IDS))
-    {
-      ReadJointAccessor<uint8_t>(raw, mJoints0, streamJoint, pathJoint);
-    }
-    else
-    {
-      ReadJointAccessor<float>(raw, mJoints0, streamJoint, pathJoint);
-    }
-
-    std::string pathWeight;
-    auto&       streamWeight = GetAvailableData(fileStream, meshPath, buffers[mWeights0.mBufferIdx], pathWeight);
-    if(MaskMatch(mFlags, U16_WEIGHT))
-    {
-      ReadWeightAccessor<uint16_t>(raw, mWeights0, streamWeight, pathWeight);
-    }
-    else if(MaskMatch(mFlags, U8_WEIGHT))
-    {
-      ReadWeightAccessor<uint8_t>(raw, mWeights0, streamWeight, pathWeight);
-    }
-    else
-    {
-      ReadWeightAccessor<float>(raw, mWeights0, streamWeight, pathWeight);
+    int setIndex = 0;
+    for(auto& accessor : mJoints)
+    {
+      std::string        pathJoint;
+      auto&              streamJoint = GetAvailableData(fileStream, meshPath, buffers[accessor.mBufferIdx], pathJoint);
+      std::ostringstream jointName;
+      jointName << "aJoints" << setIndex;
+      ++setIndex;
+      ReadTypedJointAccessor(raw, mFlags, accessor, streamJoint, pathJoint, jointName.str());
+    }
+    setIndex = 0;
+    for(auto& accessor : mWeights)
+    {
+      std::string        pathWeight;
+      auto&              streamWeight = GetAvailableData(fileStream, meshPath, buffers[accessor.mBufferIdx], pathWeight);
+      std::ostringstream weightName;
+      weightName << "aWeights" << setIndex;
+      ++setIndex;
+      ReadTypedWeightAccessor(raw, mFlags, accessor, streamWeight, pathWeight, weightName.str());
     }
   }
 
index d1016b5..1f3bca5 100644 (file)
@@ -198,10 +198,13 @@ struct DALI_SCENE3D_API MeshDefinition
 
     Accessor(const MeshDefinition::Blob&       blob,
              const MeshDefinition::SparseBlob& sparse,
-             Index                             bufferIndex = INVALID_INDEX);
+             Index                             bufferIndex = INVALID_INDEX,
+             bool                              normalized = false);
+
     Accessor(MeshDefinition::Blob&&       blob,
              MeshDefinition::SparseBlob&& sparse,
-             Index                        bufferIndex = INVALID_INDEX);
+             Index                        bufferIndex = INVALID_INDEX,
+             bool                         normalized = false);
 
     bool IsDefined() const
     {
@@ -311,12 +314,12 @@ public: // DATA
   std::string              mUri; // When the mesh data is loaded from embedded resources, this URI is used as a data stream.
   Accessor                 mIndices;
   Accessor                 mPositions;
-  Accessor                 mNormals; // data can be generated based on positions
-  Accessor                 mTexCoords;
-  Accessor                 mColors;
+  Accessor                 mNormals;  // data can be generated based on positions
   Accessor                 mTangents; // data can be generated based on normals and texCoords (the latter isn't mandatory; the results will be better if available)
-  Accessor                 mJoints0;
-  Accessor                 mWeights0;
+  std::vector<Accessor>    mTexCoords;
+  std::vector<Accessor>    mColors;
+  std::vector<Accessor>    mJoints;
+  std::vector<Accessor>    mWeights;
   Property::Type           mTangentType{Property::VECTOR3};
 
   Blob                    mBlendShapeHeader;
index 49ff885..e9975f4 100644 (file)
@@ -20,6 +20,7 @@
 
 // EXTERNAL INCLUDES
 #include <dali/public-api/object/property-array.h>
+#include <regex>
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
@@ -44,7 +45,7 @@ ShaderDefinition::ShaderDefinition(const ShaderDefinition& other)
 {
 }
 
-void ShaderDefinition::ApplyDefine(std::string& shaderCode, const std::string& definevar)
+void ApplyDefine(std::string& shaderCode, const std::string& definevar)
 {
   const std::string IF_1 = "#if 1";
 
@@ -79,6 +80,26 @@ void ShaderDefinition::ApplyDefine(std::string& shaderCode, const std::string& d
   }
 }
 
+void RedefineMacro(std::string& shaderCode, const std::string& macro, const std::string& value)
+{
+  std::string definition = "#define " + macro;
+  std::size_t found      = shaderCode.find(definition);
+  if(found != std::string::npos)
+  {
+    std::size_t insertionPoint = found + definition.length();
+
+    // Automatically insert line-continuation character into value
+    std::regex                 re("\n");
+    std::sregex_token_iterator first{value.begin(), value.end(), re, -1}, last;
+    for(auto i = first; i != last; ++i)
+    {
+      std::string line = std::string("\\\n") + (*i).str();
+      shaderCode.insert(insertionPoint, line);
+      insertionPoint += line.length();
+    }
+  }
+}
+
 ShaderDefinition::RawData
 ShaderDefinition::LoadRaw(const std::string& shadersPath) const
 {
@@ -117,6 +138,12 @@ ShaderDefinition::LoadRaw(const std::string& shadersPath) const
       ApplyDefine(raw.mFragmentShaderSource, definevar);
       ApplyDefine(raw.mShadowVertexShaderSource, definevar);
     }
+    for(const auto& macroDef : mMacros)
+    {
+      RedefineMacro(raw.mVertexShaderSource, macroDef.macro, macroDef.definition);
+      RedefineMacro(raw.mFragmentShaderSource, macroDef.macro, macroDef.definition);
+      RedefineMacro(raw.mShadowVertexShaderSource, macroDef.macro, macroDef.definition);
+    }
   }
 
   return raw;
index 0fe83d3..f39ac5f 100644 (file)
@@ -24,6 +24,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/public-api/loader/renderer-state.h>
+#include <dali-scene3d/public-api/loader/shader-option.h>
 
 namespace Dali::Scene3D::Loader
 {
@@ -44,17 +45,12 @@ struct DALI_SCENE3D_API ShaderDefinition
     std::string mShadowFragmentShaderSource;
   };
 
-  /*
-   * @brief Apply the defines values to shader.
-   */
-  static void ApplyDefine(std::string& shaderCode, const std::string& definevar);
-
   ShaderDefinition() = default;
 
   ShaderDefinition(const ShaderDefinition& other);
   ShaderDefinition& operator=(const ShaderDefinition& other);
 
-  ShaderDefinition(ShaderDefinition&&)            = default;
+  ShaderDefinition(ShaderDefinition&&) = default;
   ShaderDefinition& operator=(ShaderDefinition&&) = default;
 
   /*
@@ -76,12 +72,13 @@ public: // DATA
   std::shared_ptr<RawData> mRawData;
   RendererState::Type      mRendererState = RendererState::NONE;
 
-  std::string              mVertexShaderPath;
-  std::string              mFragmentShaderPath;
-  std::vector<std::string> mDefines;
-  std::vector<std::string> mHints;
-  Property::Map            mUniforms;
-  bool                     mUseBuiltInShader{false};
+  std::string                                mVertexShaderPath;
+  std::string                                mFragmentShaderPath;
+  std::vector<std::string>                   mDefines;
+  std::vector<ShaderOption::MacroDefinition> mMacros;
+  std::vector<std::string>                   mHints;
+  Property::Map                              mUniforms;
+  bool                                       mUseBuiltInShader{false};
 };
 
 } // namespace Dali::Scene3D::Loader
index c3ef1ce..edb7b64 100644 (file)
@@ -38,6 +38,9 @@ namespace
 static constexpr uint32_t INDEX_FOR_LIGHT_CONSTRAINT_TAG  = 10;
 static constexpr uint32_t INDEX_FOR_SHADOW_CONSTRAINT_TAG = 100;
 
+static const char* ADD_EXTRA_SKINNING_ATTRIBUTES{"ADD_EXTRA_SKINNING_ATTRIBUTES"};
+static const char* ADD_EXTRA_WEIGHTS{"ADD_EXTRA_WEIGHTS"};
+
 ShaderOption MakeOption(const MaterialDefinition& materialDef, const MeshDefinition& meshDef)
 {
   ShaderOption option;
@@ -109,6 +112,27 @@ ShaderOption MakeOption(const MaterialDefinition& materialDef, const MeshDefinit
   if(meshDef.IsSkinned())
   {
     option.AddOption(ShaderOption::Type::SKINNING);
+
+    // Add options for ADD_EXTRA_SKINNING_ATTRIBUTES and ADD_EXTRA_WEIGHTS:
+    size_t numberOfSets = meshDef.mJoints.size();
+    if(numberOfSets > 1)
+    {
+      std::ostringstream attributes;
+      std::ostringstream weights;
+      for(size_t i = 1; i < numberOfSets; ++i)
+      {
+        attributes << "in vec4 aJoints" << i << ";\n";
+        attributes << "in vec4 aWeights" << i << ";\n";
+
+        weights << "bone +=\n"
+                << "uBone[int(aJoints" << i << ".x)] * aWeights" << i << ".x +\n"
+                << "uBone[int(aJoints" << i << ".y)] * aWeights" << i << ".y +\n"
+                << "uBone[int(aJoints" << i << ".z)] * aWeights" << i << ".z +\n"
+                << "uBone[int(aJoints" << i << ".w)] * aWeights" << i << ".w;\n";
+      }
+      option.AddMacroDefinition(ADD_EXTRA_SKINNING_ATTRIBUTES, attributes.str());
+      option.AddMacroDefinition(ADD_EXTRA_WEIGHTS, weights.str());
+    }
   }
 
   if(MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
@@ -116,7 +140,7 @@ ShaderOption MakeOption(const MaterialDefinition& materialDef, const MeshDefinit
     option.AddOption(ShaderOption::Type::FLIP_UVS_VERTICAL);
   }
 
-  if(meshDef.mColors.IsDefined())
+  if(!meshDef.mColors.empty() && meshDef.mColors[0].IsDefined())
   {
     option.AddOption(ShaderOption::Type::COLOR_ATTRIBUTE);
   }
@@ -199,6 +223,7 @@ Dali::Shader ShaderManager::ProduceShader(const ShaderOption& shaderOption)
     shaderDef.mUseBuiltInShader = true;
 
     shaderOption.GetDefines(shaderDef.mDefines);
+    shaderDef.mMacros                  = shaderOption.GetMacroDefinitions();
     shaderDef.mUniforms["uCubeMatrix"] = Matrix::IDENTITY;
 
     shaderMap[hash] = mImpl->mShaders.size();
@@ -396,14 +421,12 @@ void ShaderManager::SetShadowConstraintToShader(Dali::Shader shader)
   std::string       shadowViewProjectionPropertyName(Scene3D::Internal::Light::GetShadowViewProjectionMatrixUniformName());
   auto              shadowViewProjectionPropertyIndex = shader.RegisterProperty(shadowViewProjectionPropertyName, Matrix::IDENTITY);
   Dali::CameraActor shadowLightCamera                 = Dali::Scene3D::Internal::GetImplementation(mImpl->mShadowLight).GetCamera();
-  auto tempViewProjectionMatrixIndex = shadowLightCamera.GetPropertyIndex("tempViewProjectionMatrix");
+  auto              tempViewProjectionMatrixIndex     = shadowLightCamera.GetPropertyIndex("tempViewProjectionMatrix");
   if(tempViewProjectionMatrixIndex != Dali::Property::INVALID_INDEX)
   {
     tempViewProjectionMatrixIndex = shadowLightCamera.RegisterProperty("tempViewProjectionMatrix", Matrix::IDENTITY);
   }
-  Dali::Constraint shadowViewProjectionConstraint = Dali::Constraint::New<Matrix>(shader, shadowViewProjectionPropertyIndex, [](Matrix& output, const PropertyInputContainer& inputs)
-                                                                                  {
-                                                                                    output = inputs[0]->GetMatrix(); });
+  Dali::Constraint shadowViewProjectionConstraint = Dali::Constraint::New<Matrix>(shader, shadowViewProjectionPropertyIndex, [](Matrix& output, const PropertyInputContainer& inputs) { output = inputs[0]->GetMatrix(); });
   shadowViewProjectionConstraint.AddSource(Source{shadowLightCamera, tempViewProjectionMatrixIndex});
   shadowViewProjectionConstraint.ApplyPost();
   shadowViewProjectionConstraint.SetTag(INDEX_FOR_SHADOW_CONSTRAINT_TAG);
index 85489ab..d09195d 100644 (file)
@@ -19,6 +19,7 @@
 #include <dali-scene3d/public-api/loader/shader-option.h>
 
 // EXTERNAL INCLUDES
+#include <algorithm>
 #include <string>
 
 namespace Dali::Scene3D::Loader
@@ -48,8 +49,39 @@ static constexpr std::string_view OPTION_KEYWORD[] =
     "MORPH_VERSION_2_0",
 };
 static constexpr uint32_t NUMBER_OF_OPTIONS = sizeof(OPTION_KEYWORD) / sizeof(OPTION_KEYWORD[0]);
+
+inline void HashString(std::uint64_t& hash, const char* string)
+{
+  char c;
+  while((c = *string++))
+  {
+    hash = hash * 33 + c;
+  }
+}
 } // namespace
 
+ShaderOption::ShaderOption(const ShaderOption& rhs)
+{
+  mOptionHash = rhs.mOptionHash;
+  for(auto& macroDef : rhs.mMacros)
+  {
+    mMacros.emplace_back(macroDef);
+  }
+}
+
+ShaderOption& ShaderOption::operator=(const ShaderOption& rhs)
+{
+  if(this != &rhs)
+  {
+    mOptionHash = rhs.mOptionHash;
+    for(auto& macroDef : rhs.mMacros)
+    {
+      mMacros.emplace_back(macroDef);
+    }
+  }
+  return *this;
+}
+
 void ShaderOption::SetTransparency()
 {
   mOptionHash |= (1 << NUMBER_OF_OPTIONS);
@@ -60,9 +92,38 @@ void ShaderOption::AddOption(Type shaderOptionType)
   mOptionHash |= (1 << static_cast<uint32_t>(shaderOptionType));
 }
 
+void ShaderOption::AddMacroDefinition(std::string macro, std::string definition)
+{
+  auto iter = std::find_if(mMacros.begin(), mMacros.end(), [macro](ShaderOption::MacroDefinition& md) { return md.macro == macro; });
+  if(iter != mMacros.end())
+  {
+    iter->definition = definition;
+  }
+  else
+  {
+    mMacros.emplace_back(MacroDefinition{macro, definition});
+  }
+}
+
+const std::vector<ShaderOption::MacroDefinition>& ShaderOption::GetMacroDefinitions() const
+{
+  return mMacros;
+}
+
 uint64_t ShaderOption::GetOptionHash() const
 {
-  return mOptionHash;
+  uint64_t optionHash = mOptionHash;
+  if(!mMacros.empty())
+  {
+    uint64_t hash = 5381;
+    for(auto& macroDef : mMacros)
+    {
+      HashString(hash, macroDef.macro.c_str());
+      HashString(hash, macroDef.definition.c_str());
+    }
+    optionHash |= (hash << 32 & 0xFFFFFFFF00000000);
+  }
+  return optionHash;
 }
 
 void ShaderOption::GetDefines(std::vector<std::string>& defines) const
index e9d95c2..18fad44 100644 (file)
@@ -20,6 +20,7 @@
 // EXTERNAL INCLUDER
 #include <dali/public-api/common/vector-wrapper.h>
 #include <memory>
+#include <string>
 #include <string_view>
 
 // INTERNAL INCLUDES
@@ -27,7 +28,6 @@
 
 namespace Dali::Scene3D::Loader
 {
-
 class DALI_SCENE3D_API ShaderOption
 {
 public:
@@ -54,6 +54,16 @@ public:
     MORPH_VERSION_2_0
   };
 
+  struct MacroDefinition
+  {
+    std::string macro;
+    std::string definition;
+  };
+
+  ShaderOption() = default;
+  ShaderOption(const ShaderOption& rhs);
+  ShaderOption& operator=(const ShaderOption& rhs);
+
 public:
   /**
    * @brief Sets transparency option.
@@ -69,6 +79,11 @@ public:
   void AddOption(Type shaderOptionType);
 
   /**
+   * Enables empty preprocessor definitions to be defined to a value
+   */
+  void AddMacroDefinition(std::string macro, std::string definition);
+
+  /**
    * @brief Retrieves current shader option hash
    *
    * @return Hash value of currently added options.
@@ -90,8 +105,15 @@ public:
    */
   static std::string_view GetDefineKeyword(Type shaderOptionType);
 
+  /**
+   * Get any macro definitions
+   */
+  const std::vector<MacroDefinition>& GetMacroDefinitions() const;
+
 private:
   uint64_t mOptionHash{0u};
+
+  std::vector<MacroDefinition> mMacros;
 };
 
 } // namespace Dali::Scene3D::Loader