Merge "Enable multiple primitives in a mesh(glTF)" into devel/master
authorSeungho BAEK <sbsh.baek@samsung.com>
Tue, 29 Nov 2022 06:35:48 +0000 (06:35 +0000)
committerGerrit Code Review <gerrit@review>
Tue, 29 Nov 2022 06:35:48 +0000 (06:35 +0000)
13 files changed:
automated-tests/resources/simpleMultiplePrimitiveTest.gltf [new file with mode: 0644]
automated-tests/src/dali-scene3d/utc-Dali-Gltf2Loader.cpp
automated-tests/src/dali-scene3d/utc-Dali-Model.cpp
automated-tests/src/dali-scene3d/utc-Dali-NodeDefinition.cpp
automated-tests/src/dali-scene3d/utc-Dali-ShaderDefinitionFactory.cpp
dali-scene3d/public-api/loader/dli-loader.cpp
dali-scene3d/public-api/loader/gltf2-loader.cpp
dali-scene3d/public-api/loader/material-definition.h
dali-scene3d/public-api/loader/node-definition.cpp
dali-scene3d/public-api/loader/node-definition.h
dali-scene3d/public-api/loader/scene-definition.cpp
dali-scene3d/public-api/loader/shader-definition-factory.cpp
dali-scene3d/public-api/loader/shader-definition-factory.h

diff --git a/automated-tests/resources/simpleMultiplePrimitiveTest.gltf b/automated-tests/resources/simpleMultiplePrimitiveTest.gltf
new file mode 100644 (file)
index 0000000..9345fbe
--- /dev/null
@@ -0,0 +1,109 @@
+{\r
+  "scene" : 0,\r
+  "scenes" : [\r
+    {\r
+      "nodes" : [ 0 ]\r
+    }\r
+  ],\r
+\r
+  "nodes" : [\r
+    {\r
+      "name" : "rootNode",\r
+      "mesh" : 0,\r
+      "rotation" : [ 0.0, 0.0, 0.0, 1.0 ]\r
+    }\r
+  ],\r
+\r
+  "meshes" : [\r
+    {\r
+      "primitives" : [\r
+        {\r
+          "attributes" : {\r
+            "POSITION" : 1\r
+          },\r
+          "indices" : 0\r
+        },\r
+        {\r
+          "attributes" : {\r
+            "POSITION" : 1\r
+          },\r
+          "indices" : 0\r
+        }\r
+      ]\r
+    }\r
+  ],\r
+\r
+  "buffers" : [\r
+    {\r
+      "uri" : "simpleTriangle.bin",\r
+      "byteLength" : 44\r
+    },\r
+    {\r
+      "uri" : "animation.bin",\r
+      "byteLength" : 100\r
+    }\r
+  ],\r
+\r
+  "bufferViews" : [\r
+    {\r
+      "buffer" : 0,\r
+      "byteOffset" : 0,\r
+      "byteLength" : 6,\r
+      "target" : 34963\r
+    },\r
+    {\r
+      "buffer" : 0,\r
+      "byteOffset" : 8,\r
+      "byteLength" : 36,\r
+      "target" : 34962\r
+    },\r
+    {\r
+      "buffer" : 1,\r
+      "byteOffset" : 0,\r
+      "byteLength" : 100\r
+    }\r
+  ],\r
+\r
+  "accessors" : [\r
+    {\r
+      "bufferView" : 0,\r
+      "byteOffset" : 0,\r
+      "componentType" : 5123,\r
+      "count" : 3,\r
+      "type" : "SCALAR",\r
+      "max" : [ 2 ],\r
+      "min" : [ 0 ]\r
+    },\r
+    {\r
+      "bufferView" : 1,\r
+      "byteOffset" : 0,\r
+      "componentType" : 5126,\r
+      "count" : 3,\r
+      "type" : "VEC3",\r
+      "max" : [ 1.0, 1.0, 0.0 ],\r
+      "min" : [ 0.0, 0.0, 0.0 ]\r
+    },\r
+    {\r
+      "bufferView" : 2,\r
+      "byteOffset" : 0,\r
+      "componentType" : 5126,\r
+      "count" : 5,\r
+      "type" : "SCALAR",\r
+      "max" : [ 1.0 ],\r
+      "min" : [ 0.0 ]\r
+    },\r
+    {\r
+      "bufferView" : 2,\r
+      "byteOffset" : 20,\r
+      "componentType" : 5126,\r
+      "count" : 5,\r
+      "type" : "VEC4",\r
+      "max" : [ 0.0, 0.0, 1.0, 1.0 ],\r
+      "min" : [ 0.0, 0.0, 0.0, -0.707 ]\r
+    }\r
+  ],\r
+\r
+  "asset" : {\r
+    "version" : "2.0"\r
+  }\r
+}\r
index a9ba508..b48ec38 100644 (file)
@@ -168,7 +168,7 @@ int UtcDaliGltfLoaderSuccess1(void)
   DALI_TEST_EQUAL(2u, materials.size());
   const MaterialDefinition materialGroundTruth[]{
     {MaterialDefinition::ALBEDO | MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
-       MaterialDefinition::NORMAL | MaterialDefinition::TRANSPARENCY |
+       MaterialDefinition::NORMAL |
        (0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT),
      0,
      Color::WHITE,
@@ -181,6 +181,7 @@ int UtcDaliGltfLoaderSuccess1(void)
      true,
      false,
      true,
+     false,
      {
        {MaterialDefinition::ALBEDO,
         {"AnimatedCube_BaseColor.png",
@@ -209,6 +210,7 @@ int UtcDaliGltfLoaderSuccess1(void)
      true,
      true,
      true,
+     false,
      {
        {MaterialDefinition::ALBEDO,
         {"AnimatedCube_BaseColor.png",
@@ -386,9 +388,9 @@ int UtcDaliGltfLoaderSuccessShort(void)
 
         void Start(NodeDefinition& n) override
         {
-          if(n.mRenderable)
+          for(auto& renderable : n.mRenderables)
           {
-            n.mRenderable->RegisterResources(receiver);
+            renderable->RegisterResources(receiver);
           }
         }
 
index 25d2624..b52aa62 100644 (file)
@@ -47,9 +47,10 @@ const bool DEFAULT_MODEL_CHILDREN_FOCUSABLE = false;
  * Donated by Norbert Nopper for glTF testing.
  * Take from https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/AnimatedCube
  */
-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_DLI_FILE_NAME                 = TEST_RESOURCE_DIR "/arc.dli";
+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_MULTIPLE_PRIMITIVE_FILE_NAME = TEST_RESOURCE_DIR "/simpleMultiplePrimitiveTest.gltf";
+const char* TEST_DLI_FILE_NAME                     = TEST_RESOURCE_DIR "/arc.dli";
 /**
  * For the diffuse and specular cube map texture.
  * These textures are based off version of Wave engine sample
@@ -828,6 +829,26 @@ int UtcDaliModelAnimation02(void)
   END_TEST;
 }
 
+int UtcDaliModelMultiplePrimitives(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_MULTIPLE_PRIMITIVE_FILE_NAME);
+  model.SetProperty(Dali::Actor::Property::SIZE, Vector2(50, 50));
+
+  application.GetScene().Add(model);
+
+  application.SendNotification();
+  application.Render();
+
+  Actor actor = model.FindChildByName("rootNode");
+
+  DALI_TEST_EQUALS(0, actor.GetChildCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(2, actor.GetRendererCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
 // For ResourceReady
 namespace
 {
index 835fdbf..415e2e1 100644 (file)
 // Enable debug log for test coverage
 #define DEBUG_ENABLED 1
 
-#include "dali-scene3d/public-api/loader/node-definition.h"
-#include "dali-scene3d/public-api/loader/view-projection.h"
-#include <toolkit-test-application.h>
 #include <dali-test-suite-utils.h>
+#include <toolkit-test-application.h>
 #include <string_view>
+#include "dali-scene3d/public-api/loader/node-definition.h"
+#include "dali-scene3d/public-api/loader/view-projection.h"
 
 using namespace Dali;
 using namespace Dali::Scene3D::Loader;
@@ -35,20 +35,19 @@ struct Context
   ResourceBundle resources;
 
   ViewProjection viewProjection;
-  Transforms transforms { MatrixStack{}, viewProjection };
+  Transforms     transforms{MatrixStack{}, viewProjection};
 
-  NodeDefinition::CreateParams createParams {
+  NodeDefinition::CreateParams createParams{
     resources,
-    transforms
-  };
+    transforms};
 };
 
-}
+} // namespace
 
 int UtcDaliConstraintDefinitionsCompare(void)
 {
-  ConstraintDefinition cd1{ "orientation", 0 };
-  ConstraintDefinition cd2{ "position", 1 };
+  ConstraintDefinition cd1{"orientation", 0};
+  ConstraintDefinition cd2{"position", 1};
 
   DALI_TEST_CHECK(cd1 < cd2);
   DALI_TEST_CHECK(!(cd2 < cd1));
@@ -58,9 +57,9 @@ int UtcDaliConstraintDefinitionsCompare(void)
   DALI_TEST_CHECK(cd1 == cd1);
   DALI_TEST_CHECK(cd2 == cd2);
 
-  ConstraintDefinition cd3{ "position", 0 };
-  ConstraintDefinition cd4{ "scale", 1 };
-  ConstraintDefinition cd5{ "position", 1 };
+  ConstraintDefinition cd3{"position", 0};
+  ConstraintDefinition cd4{"scale", 1};
+  ConstraintDefinition cd5{"position", 1};
   DALI_TEST_CHECK(cd2 != cd3);
   DALI_TEST_CHECK(cd2 != cd4);
   DALI_TEST_CHECK(cd2 == cd5);
@@ -71,13 +70,10 @@ int UtcDaliConstraintDefinitionsCompare(void)
 
 int UtcDaliBlendshapeShaderConfigurationRequestsCompare(void)
 {
-  TestApplication app;
-  BlendshapeShaderConfigurationRequest bsscr1{ "", 0, Shader(nullptr) };
+  TestApplication                      app;
+  BlendshapeShaderConfigurationRequest bsscr1{"", 0, Shader(nullptr)};
 
-  BlendshapeShaderConfigurationRequest bsscr2{ "", 0, Shader::New(
-    "void main(){ gl_Position = vec4(0.); }",
-    "void main(){ gl_FragColor = vec4(1.); }"
-  ) };
+  BlendshapeShaderConfigurationRequest bsscr2{"", 0, Shader::New("void main(){ gl_Position = vec4(0.); }", "void main(){ gl_FragColor = vec4(1.); }")};
 
   DALI_TEST_CHECK(bsscr1 < bsscr2);
   DALI_TEST_CHECK(!(bsscr2 < bsscr1));
@@ -89,8 +85,8 @@ int UtcDaliBlendshapeShaderConfigurationRequestsCompare(void)
 
 int UtcDaliNodeDefinitionExtrasCompare(void)
 {
-  NodeDefinition::Extra e1{ "alpha", Vector3::XAXIS * 2.f };
-  NodeDefinition::Extra e2{ "beta", 8 };
+  NodeDefinition::Extra e1{"alpha", Vector3::XAXIS * 2.f};
+  NodeDefinition::Extra e2{"beta", 8};
 
   DALI_TEST_CHECK(e1 < e2);
   DALI_TEST_CHECK(!(e1 < e1));
@@ -103,21 +99,21 @@ int UtcDaliNodeDefinitionExtrasCompare(void)
 int UtcDaliNodeDefinitionProperties(void)
 {
   TestApplication testApp;
-  NodeDefinition nodeDef{
+  NodeDefinition  nodeDef{
     "testRootNode",
-    Vector3{ -100.f, 100.f, -500.f },
-    Quaternion{ Radian(Degree(45.f)), Vector3::ZAXIS },
-    Vector3{ 2.f, 4.f, 8.f },
-    Vector3{ 100.f, 50.f, 25.f },
+    Vector3{-100.f, 100.f, -500.f},
+    Quaternion{Radian(Degree(45.f)), Vector3::ZAXIS},
+    Vector3{2.f, 4.f, 8.f},
+    Vector3{100.f, 50.f, 25.f},
     false,
   };
 
   Quaternion frobnicateFactor(0.f, 1.f, 2.f, 3.f);
   frobnicateFactor.Normalize(); // because it will be (by DALi) once it's set as a property.
-  nodeDef.mExtras.push_back(NodeDefinition::Extra{ "frobnicateFactor", frobnicateFactor });
+  nodeDef.mExtras.push_back(NodeDefinition::Extra{"frobnicateFactor", frobnicateFactor});
 
   Context ctx;
-  auto actor = nodeDef.CreateActor(ctx.createParams);
+  auto    actor = nodeDef.CreateActor(ctx.createParams);
   DALI_TEST_EQUAL(nodeDef.mName, actor.GetProperty(Actor::Property::NAME).Get<std::string>());
   DALI_TEST_EQUAL(nodeDef.mPosition, actor.GetProperty(Actor::Property::POSITION).Get<Vector3>());
   DALI_TEST_EQUAL(nodeDef.mOrientation, actor.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>());
@@ -142,30 +138,30 @@ int UtcDaliNodeDefinitionRenderableRegisterResources(void)
 {
   NodeDefinition nodeDef;
 
-  auto renderable = new NodeDefinition::Renderable();
-  renderable->mShaderIdx = 0;
-  nodeDef.mRenderable.reset(renderable);
+  std::unique_ptr<NodeDefinition::Renderable> renderable = std::unique_ptr<NodeDefinition::Renderable>(new NodeDefinition::Renderable());
+  nodeDef.mRenderables.push_back(std::move(renderable));
+  nodeDef.mRenderables[0]->mShaderIdx = 0;
 
   struct : IResourceReceiver
   {
     std::vector<Index> shaders;
-    uint32_t otherResources = 0;
+    uint32_t           otherResources = 0;
 
     void Register(ResourceType::Value type, Index id) override
     {
       switch(type)
       {
-      case ResourceType::Shader:
-        shaders.push_back(id);
-        break;
+        case ResourceType::Shader:
+          shaders.push_back(id);
+          break;
 
-      default:
-        ++otherResources;
+        default:
+          ++otherResources;
       }
     }
   } resourceReceiver;
 
-  nodeDef.mRenderable->RegisterResources(resourceReceiver);
+  nodeDef.mRenderables[0]->RegisterResources(resourceReceiver);
   DALI_TEST_EQUAL(1u, resourceReceiver.shaders.size());
   DALI_TEST_EQUAL(0, resourceReceiver.shaders[0]);
   DALI_TEST_EQUAL(0, resourceReceiver.otherResources);
@@ -177,32 +173,32 @@ int UtcDaliNodeDefinitionRenderableReflectResources(void)
 {
   NodeDefinition nodeDef;
 
-  auto renderable = new NodeDefinition::Renderable();
-  renderable->mShaderIdx = 0;
-  nodeDef.mRenderable.reset(renderable);
+  std::unique_ptr<NodeDefinition::Renderable> renderable = std::unique_ptr<NodeDefinition::Renderable>(new NodeDefinition::Renderable());
+  nodeDef.mRenderables.push_back(std::move(renderable));
+  nodeDef.mRenderables[0]->mShaderIdx = 0;
 
   struct : IResourceReflector
   {
     std::vector<Index*> shaders;
-    uint32_t otherResources = 0;
+    uint32_t            otherResources = 0;
 
     void Reflect(ResourceType::Value type, Index& id) override
     {
       switch(type)
       {
-      case ResourceType::Shader:
-        shaders.push_back(&id);
-        break;
+        case ResourceType::Shader:
+          shaders.push_back(&id);
+          break;
 
-      default:
-        ++otherResources;
+        default:
+          ++otherResources;
       }
     }
   } resourceReflector;
 
-  nodeDef.mRenderable->ReflectResources(resourceReflector);
+  nodeDef.mRenderables[0]->ReflectResources(resourceReflector);
   DALI_TEST_EQUAL(1u, resourceReflector.shaders.size());
-  DALI_TEST_EQUAL(&renderable->mShaderIdx, resourceReflector.shaders[0]);
+  DALI_TEST_EQUAL(&nodeDef.mRenderables[0]->mShaderIdx, resourceReflector.shaders[0]);
   DALI_TEST_EQUAL(0, resourceReflector.otherResources);
 
   END_TEST;
@@ -211,17 +207,17 @@ int UtcDaliNodeDefinitionRenderableReflectResources(void)
 int UtcDaliNodeDefinitionRenderable(void)
 {
   TestApplication testApp;
-  NodeDefinition nodeDef;
+  NodeDefinition  nodeDef;
 
-  auto renderable = new NodeDefinition::Renderable();
-  renderable->mShaderIdx = 0;
-  nodeDef.mRenderable.reset(renderable);
+  std::unique_ptr<NodeDefinition::Renderable> renderable = std::unique_ptr<NodeDefinition::Renderable>(new NodeDefinition::Renderable());
+  nodeDef.mRenderables.push_back(std::move(renderable));
+  nodeDef.mRenderables[0]->mShaderIdx = 0;
 
-  Context ctx;
-  const auto VSH = "void main() { gl_Position = vec4(0.); }";
-  const auto FSH = "void main() { gl_FragColor = vec4(1.); }";
-  auto shader = Shader::New(VSH, FSH);
-  ctx.resources.mShaders.push_back({ ShaderDefinition{}, shader });
+  Context    ctx;
+  const auto VSH    = "void main() { gl_Position = vec4(0.); }";
+  const auto FSH    = "void main() { gl_FragColor = vec4(1.); }";
+  auto       shader = Shader::New(VSH, FSH);
+  ctx.resources.mShaders.push_back({ShaderDefinition{}, shader});
 
   auto actor = nodeDef.CreateActor(ctx.createParams);
   DALI_TEST_EQUAL(1, actor.GetRendererCount());
index b8e6357..10aadbf 100644 (file)
@@ -91,9 +91,9 @@ int UtcDaliShaderDefinitionFactoryProduceShaderInvalid(void)
   Context ctx;
 
   NodeDefinition nodeDef;
-  nodeDef.mRenderable.reset(new NodeDefinition::Renderable());
+  std::unique_ptr<NodeDefinition::Renderable> renderable = std::unique_ptr<NodeDefinition::Renderable>(new NodeDefinition::Renderable());
+  nodeDef.mRenderables.push_back(std::move(renderable));
 
-  DALI_TEST_EQUAL(INVALID_INDEX, ctx.factory.ProduceShader(nodeDef));
   DALI_TEST_CHECK(ctx.resources.mShaders.empty());
 
   END_TEST;
@@ -250,12 +250,14 @@ int UtcDaliShaderDefinitionFactoryProduceShader(void)
 
   for(auto& ps : permSets)
   {
-    auto modelNode          = new ModelNode();
-    modelNode->mMeshIdx     = 0;
-    modelNode->mMaterialIdx = 0;
+    auto modelRenderable          = new ModelRenderable();
+    modelRenderable->mMeshIdx     = 0;
+    modelRenderable->mMaterialIdx = 0;
 
     NodeDefinition nodeDef;
-    nodeDef.mRenderable.reset(modelNode);
+    std::unique_ptr<NodeDefinition::Renderable> renderable;
+    renderable.reset(modelRenderable);
+    nodeDef.mRenderables.push_back(std::move(renderable));
 
     auto&            meshDef     = NewMeshDefinition(ctx.resources);
     auto&            materialDef = NewMaterialDefinition(ctx.resources);
@@ -270,35 +272,38 @@ int UtcDaliShaderDefinitionFactoryProduceShader(void)
       rendererState = (rendererState | p->rendererStateSet) & ~p->rendererStateClear;
     }
 
-    auto shaderIdx = ctx.factory.ProduceShader(nodeDef);
-    DALI_TEST_EQUAL(ps.shaderIdx, shaderIdx);
+    for(auto& renderable : nodeDef.mRenderables)
+    {
+      auto shaderIdx = ctx.factory.ProduceShader(*renderable);
+      DALI_TEST_EQUAL(ps.shaderIdx, shaderIdx);
 
-    auto& shaderDef = ctx.resources.mShaders[shaderIdx].first;
-    DALI_TEST_EQUAL(shaderDef.mRendererState, rendererState);
+      auto& shaderDef = ctx.resources.mShaders[shaderIdx].first;
+      DALI_TEST_EQUAL(shaderDef.mRendererState, rendererState);
 
-    uint32_t definesUnmatched = shaderDef.mDefines.size();
-    for(auto& define : shaderDef.mDefines)
-    {
-      auto iFind = defines.find(define);
-      if(iFind != defines.end())
-      {
-        defines.erase(iFind);
-        --definesUnmatched;
-      }
-      else
+      uint32_t definesUnmatched = shaderDef.mDefines.size();
+      for(auto& define : shaderDef.mDefines)
       {
-        break;
+        auto iFind = defines.find(define);
+        if(iFind != defines.end())
+        {
+          defines.erase(iFind);
+          --definesUnmatched;
+        }
+        else
+        {
+          break;
+        }
       }
-    }
 
-    DALI_TEST_CHECK(defines.empty());
-    DALI_TEST_EQUAL(0, definesUnmatched);
+      DALI_TEST_CHECK(defines.empty());
+      DALI_TEST_EQUAL(0, definesUnmatched);
 
-    auto uMaxLOD = shaderDef.mUniforms["uMaxLOD"];
-    DALI_TEST_EQUAL(uMaxLOD.GetType(), Property::FLOAT);
+      auto uMaxLOD = shaderDef.mUniforms["uMaxLOD"];
+      DALI_TEST_EQUAL(uMaxLOD.GetType(), Property::FLOAT);
 
-    auto uCubeMatrix = shaderDef.mUniforms["uCubeMatrix"];
-    DALI_TEST_EQUAL(uCubeMatrix.GetType(), Property::MATRIX);
+      auto uCubeMatrix = shaderDef.mUniforms["uCubeMatrix"];
+      DALI_TEST_EQUAL(uCubeMatrix.GetType(), Property::MATRIX);
+    }
 
     ClearMeshesAndMaterials(ctx.resources);
   }
index 133e62c..70c59d2 100644 (file)
@@ -145,7 +145,7 @@ RendererState::Type ReadRendererState(const TreeNode& tnRendererState)
 }
 
 ///@brief Reads arc properties.
-void ReadArcField(const TreeNode* eArc, ArcNode& arc)
+void ReadArcField(const TreeNode* eArc, ArcRenderable& arc)
 {
   ReadBool(eArc->GetChild("antiAliasing"), arc.mAntiAliasing);
   ReadInt(eArc->GetChild("arcCaps"), arc.mArcCaps);
@@ -301,7 +301,7 @@ void ParseProperties(const Toolkit::TreeNode& node, Property::Array& array)
   }
 }
 
-} //namespace
+} // namespace
 
 struct DliLoader::Impl
 {
@@ -485,7 +485,8 @@ void DliLoader::Impl::ParseScene(LoadParams& params)
 
 void DliLoader::Impl::ParseSceneInternal(Index iScene, const Toolkit::TreeNode* tnScenes, const Toolkit::TreeNode* tnNodes, LoadParams& params)
 {
-  auto getSceneRootIdx = [tnScenes, tnNodes](Index iScene) {
+  auto getSceneRootIdx = [tnScenes, tnNodes](Index iScene)
+  {
     auto tn = GetNthChild(tnScenes, iScene); // now a "scene" object
     if(!tn)
     {
@@ -568,7 +569,8 @@ void DliLoader::Impl::ParseSkeletons(const TreeNode* skeletons, SceneDefinition&
         uint32_t                   jointCount = 0;
         std::function<void(Index)> visitFn;
         auto&                      ibms = mInverseBindMatrices;
-        visitFn                         = [&](Index id) {
+        visitFn                         = [&](Index id)
+        {
           auto node = scene.GetNode(id);
           jointCount += ibms.find(id) != ibms.end();
 
@@ -587,7 +589,8 @@ void DliLoader::Impl::ParseSkeletons(const TreeNode* skeletons, SceneDefinition&
 
         skeleton.mJoints.reserve(jointCount);
 
-        visitFn = [&](Index id) {
+        visitFn = [&](Index id)
+        {
           auto iFind = ibms.find(id);
           if(iFind != ibms.end() && skeleton.mJoints.size() < Skinning::MAX_JOINTS)
           {
@@ -934,7 +937,7 @@ void DliLoader::Impl::ParseMaterials(const TreeNode* materials, ConvertColorCode
       }
     }
 
-    //TODO : need to consider AGIF
+    // TODO : need to consider AGIF
     std::vector<std::string> texturePaths;
     std::string              texturePath;
     if(ReadString(node.GetChild("albedoMap"), texturePath))
@@ -1077,9 +1080,8 @@ void DliLoader::Impl::ParseNodes(const TreeNode* const nodes, Index index, LoadP
 
     virtual unsigned int Resolve(Index iDli) override
     {
-      auto iFind = std::lower_bound(mIndices.begin(), mIndices.end(), iDli, [](const Entry& idx, Index iDli) {
-        return idx.iDli < iDli;
-      });
+      auto iFind = std::lower_bound(mIndices.begin(), mIndices.end(), iDli, [](const Entry& idx, Index iDli)
+                                    { return idx.iDli < iDli; });
       DALI_ASSERT_ALWAYS(iFind != mIndices.end());
       return iFind->iScene;
     }
@@ -1159,7 +1161,7 @@ void DliLoader::Impl::ParseNodesInternal(const TreeNode* const nodes, Index inde
     else // something renderable maybe
     {
       std::unique_ptr<NodeDefinition::Renderable> renderable;
-      ModelNode*                                  modelNode = nullptr; // no ownership, aliasing renderable for the right type.
+      ModelRenderable*                            modelRenderable = nullptr; // no ownership, aliasing renderable for the right type.
 
       const TreeNode* eRenderable = nullptr;
       if((eRenderable = node->GetChild("model")))
@@ -1171,10 +1173,10 @@ void DliLoader::Impl::ParseNodesInternal(const TreeNode* const nodes, Index inde
           ExceptionFlinger(ASSERT_LOCATION) << "node " << nodeDef.mName << ": Missing mesh definition.";
         }
 
-        modelNode = new ModelNode();
-        renderable.reset(modelNode);
+        modelRenderable = new ModelRenderable();
+        renderable.reset(modelRenderable);
 
-        resourceIds.push_back({ResourceType::Mesh, eMesh, modelNode->mMeshIdx});
+        resourceIds.push_back({ResourceType::Mesh, eMesh, modelRenderable->mMeshIdx});
       }
       else if((eRenderable = node->GetChild("arc")))
       {
@@ -1185,13 +1187,13 @@ void DliLoader::Impl::ParseNodesInternal(const TreeNode* const nodes, Index inde
           ExceptionFlinger(ASSERT_LOCATION) << "node " << nodeDef.mName << ": Missing mesh definition.";
         }
 
-        auto arcNode = new ArcNode;
-        renderable.reset(arcNode);
-        modelNode = arcNode;
+        auto arcRenderable = new ArcRenderable;
+        renderable.reset(arcRenderable);
+        modelRenderable = arcRenderable;
 
-        resourceIds.push_back({ResourceType::Mesh, eMesh, arcNode->mMeshIdx});
+        resourceIds.push_back({ResourceType::Mesh, eMesh, arcRenderable->mMeshIdx});
 
-        ReadArcField(eRenderable, *arcNode);
+        ReadArcField(eRenderable, *arcRenderable);
       }
 
       if(renderable && eRenderable != nullptr) // process common properties of all renderables + register payload
@@ -1205,22 +1207,22 @@ void DliLoader::Impl::ParseNodesInternal(const TreeNode* const nodes, Index inde
         }
 
         // color
-        if(modelNode)
+        if(modelRenderable)
         {
-          modelNode->mMaterialIdx = 0; // must offer default of 0
-          auto eMaterial          = eRenderable->GetChild("material");
+          modelRenderable->mMaterialIdx = 0; // must offer default of 0
+          auto eMaterial                = eRenderable->GetChild("material");
           if(eMaterial)
           {
-            resourceIds.push_back({ResourceType::Material, eMaterial, modelNode->mMaterialIdx});
+            resourceIds.push_back({ResourceType::Material, eMaterial, modelRenderable->mMaterialIdx});
           }
 
-          if(!ReadColorCodeOrColor(eRenderable, modelNode->mColor, params.input.mConvertColorCode))
+          if(!ReadColorCodeOrColor(eRenderable, modelRenderable->mColor, params.input.mConvertColorCode))
           {
-            ReadColorCodeOrColor(node, modelNode->mColor, params.input.mConvertColorCode);
+            ReadColorCodeOrColor(node, modelRenderable->mColor, params.input.mConvertColorCode);
           }
         }
 
-        nodeDef.mRenderable = std::move(renderable);
+        nodeDef.mRenderables.push_back(std::move(renderable));
       }
     }
 
@@ -1426,9 +1428,8 @@ void DliLoader::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadParams&
     AnimationDefinition animDef;
     ReadString(tnAnim.GetChild(NAME), animDef.mName);
 
-    auto       iFind     = std::lower_bound(definitions.begin(), definitions.end(), animDef, [](const AnimationDefinition& ad0, const AnimationDefinition& ad1) {
-      return ad0.mName < ad1.mName;
-    });
+    auto       iFind     = std::lower_bound(definitions.begin(), definitions.end(), animDef, [](const AnimationDefinition& ad0, const AnimationDefinition& ad1)
+                                  { return ad0.mName < ad1.mName; });
     const bool overwrite = iFind != definitions.end() && iFind->mName == animDef.mName;
     if(overwrite)
     {
@@ -1556,10 +1557,10 @@ void DliLoader::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadParams&
 
           animProp.mKeyFrames = KeyFrames::New();
 
-          //In binary animation file only is saved the position, rotation, scale and blend shape weight keys.
-          //so, if it is vector3 we assume is position or scale keys, if it is vector4 we assume is rotation,
-          // otherwise are blend shape weight keys.
-          // TODO support for binary header with size information
+          // In binary animation file only is saved the position, rotation, scale and blend shape weight keys.
+          // so, if it is vector3 we assume is position or scale keys, if it is vector4 we assume is rotation,
+          //  otherwise are blend shape weight keys.
+          //  TODO support for binary header with size information
           Property::Type propType = Property::FLOAT; // assume blend shape weights
           if(animProp.mPropertyName == "orientation")
           {
@@ -1570,8 +1571,8 @@ void DliLoader::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadParams&
             propType = Property::VECTOR3;
           }
 
-          //alphafunction is reserved for future implementation
-          // NOTE: right now we're just using AlphaFunction::LINEAR.
+          // alphafunction is reserved for future implementation
+          //  NOTE: right now we're just using AlphaFunction::LINEAR.
           unsigned char dummyAlphaFunction;
 
           float           progress;
@@ -1694,9 +1695,8 @@ void DliLoader::Impl::ParseAnimationGroups(const Toolkit::TreeNode* tnAnimationG
       continue;
     }
 
-    auto iFind = std::lower_bound(animGroups.begin(), animGroups.end(), groupName, [](const AnimationGroupDefinition& group, const std::string& name) {
-      return group.mName < name;
-    });
+    auto iFind = std::lower_bound(animGroups.begin(), animGroups.end(), groupName, [](const AnimationGroupDefinition& group, const std::string& name)
+                                  { return group.mName < name; });
     if(iFind != animGroups.end() && iFind->mName == groupName)
     {
       mOnError(FormatString("Animation group with name '%s' already exists; new entries will be merged.", groupName.c_str()));
index 60f6970..36c1639 100644 (file)
  * limitations under the License.
  *
  */
-#include "dali-scene3d/public-api/loader/gltf2-loader.h"
 #include <fstream>
-#include "dali-scene3d/internal/loader/gltf2-asset.h"
-#include "dali-scene3d/public-api/loader/load-result.h"
-#include "dali-scene3d/public-api/loader/resource-bundle.h"
-#include "dali-scene3d/public-api/loader/scene-definition.h"
-#include "dali-scene3d/public-api/loader/shader-definition-factory.h"
-#include "dali-scene3d/public-api/loader/utils.h"
-#include "dali/public-api/math/quaternion.h"
+#include <dali-scene3d/public-api/loader/gltf2-loader.h>
+#include <dali-scene3d/internal/loader/gltf2-asset.h>
+#include <dali-scene3d/public-api/loader/load-result.h>
+#include <dali-scene3d/public-api/loader/resource-bundle.h>
+#include <dali-scene3d/public-api/loader/scene-definition.h>
+#include <dali-scene3d/public-api/loader/shader-definition-factory.h>
+#include <dali-scene3d/public-api/loader/utils.h>
+#include <dali/public-api/math/quaternion.h>
+#include <dali/integration-api/debug.h>
 
 #define ENUM_STRING_MAPPING(t, x) \
   {                               \
@@ -176,7 +177,8 @@ const auto MATERIAL_READER = std::move(js::Reader<gt::Material>()
                                          .Register(*js::MakeProperty("emissiveTexture", js::ObjectReader<gt::TextureInfo>::Read, &gt::Material::mEmissiveTexture))
                                          .Register(*js::MakeProperty("emissiveFactor", gt::ReadDaliVector<Vector3>, &gt::Material::mEmissiveFactor))
                                          .Register(*js::MakeProperty("alphaMode", gt::ReadStringEnum<gt::AlphaMode>, &gt::Material::mAlphaMode))
-                                         .Register(*js::MakeProperty("alphaCutoff", js::Read::Number<float>, &gt::Material::mAlphaCutoff)));
+                                         .Register(*js::MakeProperty("alphaCutoff", js::Read::Number<float>, &gt::Material::mAlphaCutoff))
+                                         .Register(*js::MakeProperty("doubleSided", js::Read::Boolean, &gt::Material::mDoubleSided)));
 
 std::map<gt::Attribute::Type, gt::Ref<gt::Accessor>> ReadMeshPrimitiveAttributes(const json_value_s& j)
 {
@@ -425,24 +427,24 @@ TextureDefinition ConvertTextureInfo(const gt::TextureInfo& mm)
   return TextureDefinition{std::string(mm.mTexture->mSource->mUri), ConvertSampler(mm.mTexture->mSampler)};
 }
 
-void ConvertMaterial(const gt::Material& m, decltype(ResourceBundle::mMaterials)& outMaterials)
+void ConvertMaterial(const gt::Material& material, decltype(ResourceBundle::mMaterials)& outMaterials)
 {
   MaterialDefinition matDef;
 
-  auto& pbr = m.mPbrMetallicRoughness;
-  if(m.mAlphaMode != gt::AlphaMode::OPAQUE || pbr.mBaseColorFactor.a < 1.f)
+  auto& pbr = material.mPbrMetallicRoughness;
+  if(pbr.mBaseColorFactor.a < 1.f)
   {
     matDef.mFlags |= MaterialDefinition::TRANSPARENCY;
   }
 
-  if(m.mAlphaMode == gt::AlphaMode::MASK)
+  if(material.mAlphaMode == gt::AlphaMode::MASK)
   {
-    matDef.SetAlphaCutoff(std::min(1.f, std::max(0.f, m.mAlphaCutoff)));
+    matDef.SetAlphaCutoff(std::min(1.f, std::max(0.f, material.mAlphaCutoff)));
   }
 
   matDef.mBaseColorFactor = pbr.mBaseColorFactor;
 
-  matDef.mTextureStages.reserve(!!pbr.mBaseColorTexture + !!pbr.mMetallicRoughnessTexture + !!m.mNormalTexture + !!m.mOcclusionTexture + !!m.mEmissiveTexture);
+  matDef.mTextureStages.reserve(!!pbr.mBaseColorTexture + !!pbr.mMetallicRoughnessTexture + !!material.mNormalTexture + !!material.mOcclusionTexture + !!material.mEmissiveTexture);
   if(pbr.mBaseColorTexture)
   {
     const auto semantic = MaterialDefinition::ALBEDO;
@@ -471,11 +473,11 @@ void ConvertMaterial(const gt::Material& m, decltype(ResourceBundle::mMaterials)
     matDef.mNeedMetallicRoughnessTexture = false;
   }
 
-  matDef.mNormalScale = m.mNormalTexture.mScale;
-  if(m.mNormalTexture)
+  matDef.mNormalScale = material.mNormalTexture.mScale;
+  if(material.mNormalTexture)
   {
     const auto semantic = MaterialDefinition::NORMAL;
-    matDef.mTextureStages.push_back({semantic, ConvertTextureInfo(m.mNormalTexture)});
+    matDef.mTextureStages.push_back({semantic, ConvertTextureInfo(material.mNormalTexture)});
     // TODO: and there had better be one
     matDef.mFlags |= semantic;
   }
@@ -484,31 +486,32 @@ void ConvertMaterial(const gt::Material& m, decltype(ResourceBundle::mMaterials)
     matDef.mNeedNormalTexture = false;
   }
 
-  // TODO: handle doubleSided
-  if(m.mOcclusionTexture)
+  if(material.mOcclusionTexture)
   {
     const auto semantic = MaterialDefinition::OCCLUSION;
-    matDef.mTextureStages.push_back({semantic, ConvertTextureInfo(m.mOcclusionTexture)});
+    matDef.mTextureStages.push_back({semantic, ConvertTextureInfo(material.mOcclusionTexture)});
     // TODO: and there had better be one
     matDef.mFlags |= semantic;
-    matDef.mOcclusionStrength = m.mOcclusionTexture.mStrength;
+    matDef.mOcclusionStrength = material.mOcclusionTexture.mStrength;
   }
 
-  if(m.mEmissiveTexture)
+  if(material.mEmissiveTexture)
   {
     const auto semantic = MaterialDefinition::EMISSIVE;
-    matDef.mTextureStages.push_back({semantic, ConvertTextureInfo(m.mEmissiveTexture)});
+    matDef.mTextureStages.push_back({semantic, ConvertTextureInfo(material.mEmissiveTexture)});
     // TODO: and there had better be one
     matDef.mFlags |= semantic;
-    matDef.mEmissiveFactor = m.mEmissiveFactor;
+    matDef.mEmissiveFactor = material.mEmissiveFactor;
   }
 
+  matDef.mDoubleSided = material.mDoubleSided;
+
   outMaterials.emplace_back(std::move(matDef), TextureSet());
 }
 
-void ConvertMaterials(const gt::Document& doc, ConversionContext& cctx)
+void ConvertMaterials(const gt::Document& doc, ConversionContext& context)
 {
-  auto& outMaterials = cctx.mOutput.mResources.mMaterials;
+  auto& outMaterials = context.mOutput.mResources.mMaterials;
   outMaterials.reserve(doc.mMaterials.size());
 
   for(auto& m : doc.mMaterials)
@@ -570,33 +573,33 @@ MeshDefinition::Accessor ConvertMeshPrimitiveAccessor(const gt::Accessor& acc)
     std::move(sparseBlob)};
 }
 
-void ConvertMeshes(const gt::Document& doc, ConversionContext& cctx)
+void ConvertMeshes(const gt::Document& doc, ConversionContext& context)
 {
   uint32_t meshCount = 0;
-  cctx.mMeshIds.reserve(doc.mMeshes.size());
-  for(auto& m : doc.mMeshes)
+  context.mMeshIds.reserve(doc.mMeshes.size());
+  for(auto& mesh : doc.mMeshes)
   {
-    cctx.mMeshIds.push_back(meshCount);
-    meshCount += m.mPrimitives.size();
+    context.mMeshIds.push_back(meshCount);
+    meshCount += mesh.mPrimitives.size();
   }
 
-  auto& outMeshes = cctx.mOutput.mResources.mMeshes;
+  auto& outMeshes = context.mOutput.mResources.mMeshes;
   outMeshes.reserve(meshCount);
-  for(auto& m : doc.mMeshes)
+  for(auto& mesh : doc.mMeshes)
   {
-    for(auto& p : m.mPrimitives)
+    for(auto& primitive : mesh.mPrimitives)
     {
-      MeshDefinition meshDef;
+      MeshDefinition meshDefinition;
 
-      auto& attribs          = p.mAttributes;
-      meshDef.mUri           = attribs.begin()->second->mBufferView->mBuffer->mUri;
-      meshDef.mPrimitiveType = GLTF2_TO_DALI_PRIMITIVES[p.mMode];
+      auto& attribs          = primitive.mAttributes;
+      meshDefinition.mUri           = attribs.begin()->second->mBufferView->mBuffer->mUri;
+      meshDefinition.mPrimitiveType = GLTF2_TO_DALI_PRIMITIVES[primitive.mMode];
 
       auto& accPositions = *attribs.find(gt::Attribute::POSITION)->second;
-      meshDef.mPositions = ConvertMeshPrimitiveAccessor(accPositions);
+      meshDefinition.mPositions = ConvertMeshPrimitiveAccessor(accPositions);
       // glTF2 support vector4 tangent for mesh.
       // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview
-      meshDef.mTangentType = Property::VECTOR4;
+      meshDefinition.mTangentType = Property::VECTOR4;
 
       const bool needNormalsTangents = accPositions.mType == gt::AccessorType::VEC3;
       for(auto& am : ATTRIBUTE_MAPPINGS)
@@ -604,14 +607,14 @@ void ConvertMeshes(const gt::Document& doc, ConversionContext& cctx)
         auto iFind = attribs.find(am.mType);
         if(iFind != attribs.end())
         {
-          DALI_ASSERT_DEBUG(iFind->second->mBufferView->mBuffer->mUri.compare(meshDef.mUri) == 0);
-          auto& accessor = meshDef.*(am.mAccessor);
+          DALI_ASSERT_DEBUG(iFind->second->mBufferView->mBuffer->mUri.compare(meshDefinition.mUri) == 0);
+          auto& accessor = meshDefinition.*(am.mAccessor);
           accessor       = ConvertMeshPrimitiveAccessor(*iFind->second);
 
           if(iFind->first == gt::Attribute::JOINTS_0)
           {
-            meshDef.mFlags |= (iFind->second->mComponentType == gt::Component::UNSIGNED_SHORT) * MeshDefinition::U16_JOINT_IDS;
-            DALI_ASSERT_DEBUG(MaskMatch(meshDef.mFlags, MeshDefinition::U16_JOINT_IDS) || iFind->second->mComponentType == gt::Component::FLOAT);
+            meshDefinition.mFlags |= (iFind->second->mComponentType == gt::Component::UNSIGNED_SHORT) * MeshDefinition::U16_JOINT_IDS;
+            DALI_ASSERT_DEBUG(MaskMatch(meshDefinition.mFlags, MeshDefinition::U16_JOINT_IDS) || iFind->second->mComponentType == gt::Component::FLOAT);
           }
         }
         else if(needNormalsTangents)
@@ -619,11 +622,11 @@ void ConvertMeshes(const gt::Document& doc, ConversionContext& cctx)
           switch(am.mType)
           {
             case gt::Attribute::NORMAL:
-              meshDef.RequestNormals();
+              meshDefinition.RequestNormals();
               break;
 
             case gt::Attribute::TANGENT:
-              meshDef.RequestTangents();
+              meshDefinition.RequestTangents();
               break;
 
             default:
@@ -632,18 +635,18 @@ void ConvertMeshes(const gt::Document& doc, ConversionContext& cctx)
         }
       }
 
-      if(p.mIndices)
+      if(primitive.mIndices)
       {
-        meshDef.mIndices = ConvertMeshPrimitiveAccessor(*p.mIndices);
-        meshDef.mFlags |= (p.mIndices->mComponentType == gt::Component::UNSIGNED_INT) * MeshDefinition::U32_INDICES;
-        DALI_ASSERT_DEBUG(MaskMatch(meshDef.mFlags, MeshDefinition::U32_INDICES) || p.mIndices->mComponentType == gt::Component::UNSIGNED_SHORT);
+        meshDefinition.mIndices = ConvertMeshPrimitiveAccessor(*primitive.mIndices);
+        meshDefinition.mFlags |= (primitive.mIndices->mComponentType == gt::Component::UNSIGNED_INT) * MeshDefinition::U32_INDICES;
+        DALI_ASSERT_DEBUG(MaskMatch(meshDefinition.mFlags, MeshDefinition::U32_INDICES) || primitive.mIndices->mComponentType == gt::Component::UNSIGNED_SHORT);
       }
 
-      if(!p.mTargets.empty())
+      if(!primitive.mTargets.empty())
       {
-        meshDef.mBlendShapes.reserve(p.mTargets.size());
-        meshDef.mBlendShapeVersion = BlendShapes::Version::VERSION_2_0;
-        for(const auto& target : p.mTargets)
+        meshDefinition.mBlendShapes.reserve(primitive.mTargets.size());
+        meshDefinition.mBlendShapeVersion = BlendShapes::Version::VERSION_2_0;
+        for(const auto& target : primitive.mTargets)
         {
           MeshDefinition::BlendShape blendShape;
 
@@ -664,44 +667,44 @@ void ConvertMeshes(const gt::Document& doc, ConversionContext& cctx)
             blendShape.tangents = ConvertMeshPrimitiveAccessor(*it->second);
           }
 
-          if(!m.mWeights.empty())
+          if(!mesh.mWeights.empty())
           {
-            blendShape.weight = m.mWeights[meshDef.mBlendShapes.size()];
+            blendShape.weight = mesh.mWeights[meshDefinition.mBlendShapes.size()];
           }
 
-          meshDef.mBlendShapes.push_back(std::move(blendShape));
+          meshDefinition.mBlendShapes.push_back(std::move(blendShape));
         }
       }
 
-      outMeshes.push_back({std::move(meshDef), MeshGeometry{}});
+      outMeshes.push_back({std::move(meshDefinition), MeshGeometry{}});
     }
   }
 }
 
-ModelNode* MakeModelNode(const gt::Mesh::Primitive& prim, ConversionContext& cctx)
+ModelRenderable* MakeModelRenderable(const gt::Mesh::Primitive& prim, ConversionContext& context)
 {
-  auto modelNode = new ModelNode();
+  auto modelRenderable = new ModelRenderable();
 
-  modelNode->mShaderIdx = 0; // TODO: further thought
+  modelRenderable->mShaderIdx = 0; // TODO: further thought
 
   auto materialIdx = prim.mMaterial.GetIndex();
   if(INVALID_INDEX == materialIdx)
   {
     // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#default-material
-    if(INVALID_INDEX == cctx.mDefaultMaterial)
+    if(INVALID_INDEX == context.mDefaultMaterial)
     {
-      auto& outMaterials    = cctx.mOutput.mResources.mMaterials;
-      cctx.mDefaultMaterial = outMaterials.size();
+      auto& outMaterials    = context.mOutput.mResources.mMaterials;
+      context.mDefaultMaterial = outMaterials.size();
 
       ConvertMaterial(gt::Material{}, outMaterials);
     }
 
-    materialIdx = cctx.mDefaultMaterial;
+    materialIdx = context.mDefaultMaterial;
   }
 
-  modelNode->mMaterialIdx = materialIdx;
+  modelRenderable->mMaterialIdx = materialIdx;
 
-  return modelNode;
+  return modelRenderable;
 }
 
 void ConvertCamera(const gt::Camera& camera, CameraParameters& camParams)
@@ -725,9 +728,9 @@ void ConvertCamera(const gt::Camera& camera, CameraParameters& camParams)
   }
 }
 
-void ConvertNode(gt::Node const& node, const Index gltfIdx, Index parentIdx, ConversionContext& cctx, bool isMRendererModel)
+void ConvertNode(gt::Node const& node, const Index gltfIdx, Index parentIdx, ConversionContext& context, bool isMRendererModel)
 {
-  auto& output    = cctx.mOutput;
+  auto& output    = context.mOutput;
   auto& scene     = output.mScene;
   auto& resources = output.mResources;
 
@@ -762,42 +765,27 @@ void ConvertNode(gt::Node const& node, const Index gltfIdx, Index parentIdx, Con
     ExceptionFlinger(ASSERT_LOCATION) << "Node name '" << node.mName << "' is not unique; scene is invalid.";
   }
 
-  cctx.mNodeIndices.RegisterMapping(gltfIdx, idx);
+  context.mNodeIndices.RegisterMapping(gltfIdx, idx);
 
   Index skeletonIdx = node.mSkin ? node.mSkin.GetIndex() : INVALID_INDEX;
-  if(node.mMesh && !node.mMesh->mPrimitives.empty())
+  if(node.mMesh)
   {
-    auto& mesh = *node.mMesh;
-
-    auto iPrim          = mesh.mPrimitives.begin();
-    auto modelNode      = MakeModelNode(*iPrim, cctx);
-    auto meshIdx        = cctx.mMeshIds[node.mMesh.GetIndex()];
-    modelNode->mMeshIdx = meshIdx;
-
-    weakNode->mRenderable.reset(modelNode);
-
-    DALI_ASSERT_DEBUG(resources.mMeshes[meshIdx].first.mSkeletonIdx == INVALID_INDEX ||
-                      resources.mMeshes[meshIdx].first.mSkeletonIdx == skeletonIdx);
-    resources.mMeshes[meshIdx].first.mSkeletonIdx = skeletonIdx;
-
-    // As does model-exporter, we'll create anonymous child nodes for additional mesh( primitiv)es.
-    while(++iPrim != mesh.mPrimitives.end())
+    auto&    mesh           = *node.mMesh;
+    uint32_t primitiveCount = mesh.mPrimitives.size();
+    auto     meshIdx        = context.mMeshIds[node.mMesh.GetIndex()];
+    weakNode->mRenderables.reserve(primitiveCount);
+    for(uint32_t i = 0; i < primitiveCount; ++i)
     {
-      std::unique_ptr<NodeDefinition> child{new NodeDefinition};
-      child->mParentIdx = idx;
+      std::unique_ptr<NodeDefinition::Renderable> renderable;
+      auto modelRenderable      = MakeModelRenderable(mesh.mPrimitives[i], context);
+      modelRenderable->mMeshIdx = meshIdx + i;
 
-      auto childModel = MakeModelNode(*iPrim, cctx);
+      DALI_ASSERT_DEBUG(resources.mMeshes[modelRenderable->mMeshIdx].first.mSkeletonIdx == INVALID_INDEX ||
+                        resources.mMeshes[modelRenderable->mMeshIdx].first.mSkeletonIdx == skeletonIdx);
+      resources.mMeshes[modelRenderable->mMeshIdx].first.mSkeletonIdx = skeletonIdx;
 
-      ++meshIdx;
-      childModel->mMeshIdx = meshIdx;
-
-      child->mRenderable.reset(childModel);
-
-      scene.AddNode(std::move(child));
-
-      DALI_ASSERT_DEBUG(resources.mMeshes[meshIdx].first.mSkeletonIdx == INVALID_INDEX ||
-                        resources.mMeshes[meshIdx].first.mSkeletonIdx == skeletonIdx);
-      resources.mMeshes[meshIdx].first.mSkeletonIdx = skeletonIdx;
+      renderable.reset(modelRenderable);
+      weakNode->mRenderables.push_back(std::move(renderable));
     }
   }
 
@@ -812,13 +800,13 @@ void ConvertNode(gt::Node const& node, const Index gltfIdx, Index parentIdx, Con
 
   for(auto& n : node.mChildren)
   {
-    ConvertNode(*n, n.GetIndex(), idx, cctx, isMRendererModel);
+    ConvertNode(*n, n.GetIndex(), idx, context, isMRendererModel);
   }
 }
 
-void ConvertSceneNodes(const gt::Scene& scene, ConversionContext& cctx, bool isMRendererModel)
+void ConvertSceneNodes(const gt::Scene& scene, ConversionContext& context, bool isMRendererModel)
 {
-  auto& outScene = cctx.mOutput.mScene;
+  auto& outScene = context.mOutput.mScene;
   Index rootIdx  = outScene.GetNodeCount();
   switch(scene.mNodes.size())
   {
@@ -826,7 +814,7 @@ void ConvertSceneNodes(const gt::Scene& scene, ConversionContext& cctx, bool isM
       break;
 
     case 1:
-      ConvertNode(*scene.mNodes[0], scene.mNodes[0].GetIndex(), INVALID_INDEX, cctx, isMRendererModel);
+      ConvertNode(*scene.mNodes[0], scene.mNodes[0].GetIndex(), INVALID_INDEX, context, isMRendererModel);
       outScene.AddRootNode(rootIdx);
       break;
 
@@ -840,25 +828,25 @@ void ConvertSceneNodes(const gt::Scene& scene, ConversionContext& cctx, bool isM
 
       for(auto& n : scene.mNodes)
       {
-        ConvertNode(*n, n.GetIndex(), rootIdx, cctx, isMRendererModel);
+        ConvertNode(*n, n.GetIndex(), rootIdx, context, isMRendererModel);
       }
       break;
     }
   }
 }
 
-void ConvertNodes(const gt::Document& doc, ConversionContext& cctx, bool isMRendererModel)
+void ConvertNodes(const gt::Document& doc, ConversionContext& context, bool isMRendererModel)
 {
-  ConvertSceneNodes(*doc.mScene, cctx, isMRendererModel);
+  ConvertSceneNodes(*doc.mScene, context, isMRendererModel);
 
   for(uint32_t i = 0, i1 = doc.mScene.GetIndex(); i < i1; ++i)
   {
-    ConvertSceneNodes(doc.mScenes[i], cctx, isMRendererModel);
+    ConvertSceneNodes(doc.mScenes[i], context, isMRendererModel);
   }
 
   for(uint32_t i = doc.mScene.GetIndex() + 1; i < doc.mScenes.size(); ++i)
   {
-    ConvertSceneNodes(doc.mScenes[i], cctx, isMRendererModel);
+    ConvertSceneNodes(doc.mScenes[i], context, isMRendererModel);
   }
 }
 
@@ -946,9 +934,9 @@ float LoadBlendShapeKeyFrames(const std::string& path, const gt::Animation::Chan
   return duration;
 }
 
-void ConvertAnimations(const gt::Document& doc, ConversionContext& cctx)
+void ConvertAnimations(const gt::Document& doc, ConversionContext& context)
 {
-  auto& output = cctx.mOutput;
+  auto& output = context.mOutput;
 
   output.mAnimationDefinitions.reserve(output.mAnimationDefinitions.size() + doc.mAnimations.size());
 
@@ -979,8 +967,8 @@ void ConvertAnimations(const gt::Document& doc, ConversionContext& cctx)
       }
       else
       {
-        Index index = cctx.mNodeIndices.GetRuntimeId(channel.mTarget.mNode.GetIndex());
-        nodeName    = cctx.mOutput.mScene.GetNode(index)->mName;
+        Index index = context.mNodeIndices.GetRuntimeId(channel.mTarget.mNode.GetIndex());
+        nodeName    = context.mOutput.mScene.GetNode(index)->mName;
       }
 
       float duration = 0.f;
@@ -995,7 +983,7 @@ void ConvertAnimations(const gt::Document& doc, ConversionContext& cctx)
           animatedProperty.mPropertyName = POSITION_PROPERTY;
 
           animatedProperty.mKeyFrames = KeyFrames::New();
-          duration                    = LoadKeyFrames<Vector3>(cctx.mPath, channel, animatedProperty.mKeyFrames, channel.mTarget.mPath);
+          duration                    = LoadKeyFrames<Vector3>(context.mPath, channel, animatedProperty.mKeyFrames, channel.mTarget.mPath);
 
           animatedProperty.mTimePeriod = {0.f, duration};
           break;
@@ -1008,7 +996,7 @@ void ConvertAnimations(const gt::Document& doc, ConversionContext& cctx)
           animatedProperty.mPropertyName = ORIENTATION_PROPERTY;
 
           animatedProperty.mKeyFrames = KeyFrames::New();
-          duration                    = LoadKeyFrames<Quaternion>(cctx.mPath, channel, animatedProperty.mKeyFrames, channel.mTarget.mPath);
+          duration                    = LoadKeyFrames<Quaternion>(context.mPath, channel, animatedProperty.mKeyFrames, channel.mTarget.mPath);
 
           animatedProperty.mTimePeriod = {0.f, duration};
           break;
@@ -1021,14 +1009,14 @@ void ConvertAnimations(const gt::Document& doc, ConversionContext& cctx)
           animatedProperty.mPropertyName = SCALE_PROPERTY;
 
           animatedProperty.mKeyFrames = KeyFrames::New();
-          duration                    = LoadKeyFrames<Vector3>(cctx.mPath, channel, animatedProperty.mKeyFrames, channel.mTarget.mPath);
+          duration                    = LoadKeyFrames<Vector3>(context.mPath, channel, animatedProperty.mKeyFrames, channel.mTarget.mPath);
 
           animatedProperty.mTimePeriod = {0.f, duration};
           break;
         }
         case gt::Animation::Channel::Target::WEIGHTS:
         {
-          duration = LoadBlendShapeKeyFrames(cctx.mPath, channel, nodeName, propertyIndex, animationDef.mProperties);
+          duration = LoadBlendShapeKeyFrames(context.mPath, channel, nodeName, propertyIndex, animationDef.mProperties);
 
           break;
         }
@@ -1048,7 +1036,7 @@ void ConvertAnimations(const gt::Document& doc, ConversionContext& cctx)
   }
 }
 
-void ProcessSkins(const gt::Document& doc, ConversionContext& cctx)
+void ProcessSkins(const gt::Document& doc, ConversionContext& context)
 {
   // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skininversebindmatrices
   // If an inverseBindMatrices accessor was provided, we'll load the joint data from the buffer,
@@ -1090,7 +1078,7 @@ void ProcessSkins(const gt::Document& doc, ConversionContext& cctx)
     }
   };
 
-  auto& resources = cctx.mOutput.mResources;
+  auto& resources = context.mOutput.mResources;
   resources.mSkeletons.reserve(doc.mSkins.size());
 
   for(auto& s : doc.mSkins)
@@ -1098,7 +1086,7 @@ void ProcessSkins(const gt::Document& doc, ConversionContext& cctx)
     std::unique_ptr<IInverseBindMatrixProvider> ibmProvider;
     if(s.mInverseBindMatrices)
     {
-      ibmProvider.reset(new InverseBindMatrixAccessor(*s.mInverseBindMatrices, cctx.mPath));
+      ibmProvider.reset(new InverseBindMatrixAccessor(*s.mInverseBindMatrices, context.mPath));
     }
     else
     {
@@ -1108,14 +1096,14 @@ void ProcessSkins(const gt::Document& doc, ConversionContext& cctx)
     SkeletonDefinition skeleton;
     if(s.mSkeleton.GetIndex() != INVALID_INDEX)
     {
-      skeleton.mRootNodeIdx = cctx.mNodeIndices.GetRuntimeId(s.mSkeleton.GetIndex());
+      skeleton.mRootNodeIdx = context.mNodeIndices.GetRuntimeId(s.mSkeleton.GetIndex());
     }
 
     skeleton.mJoints.resize(s.mJoints.size());
     auto iJoint = skeleton.mJoints.begin();
     for(auto& j : s.mJoints)
     {
-      iJoint->mNodeIdx = cctx.mNodeIndices.GetRuntimeId(j.GetIndex());
+      iJoint->mNodeIdx = context.mNodeIndices.GetRuntimeId(j.GetIndex());
 
       ibmProvider->Provide(iJoint->mInverseBindMatrix);
 
@@ -1128,12 +1116,16 @@ void ProcessSkins(const gt::Document& doc, ConversionContext& cctx)
 
 void ProduceShaders(ShaderDefinitionFactory& shaderFactory, SceneDefinition& scene)
 {
-  for(size_t i0 = 0, i1 = scene.GetNodeCount(); i0 != i1; ++i0)
+  uint32_t nodeCount = scene.GetNodeCount();
+  for(uint32_t i = 0; i < nodeCount; ++i)
   {
-    auto nodeDef = scene.GetNode(i0);
-    if(auto renderable = nodeDef->mRenderable.get())
+    auto nodeDef = scene.GetNode(i);
+    for(auto& renderable : nodeDef->mRenderables)
     {
-      renderable->mShaderIdx = shaderFactory.ProduceShader(*nodeDef);
+      if(shaderFactory.ProduceShader(*renderable) == INVALID_INDEX)
+      {
+        DALI_LOG_ERROR("Fail to produce shader\n");
+      }
     }
   }
 }
@@ -1166,12 +1158,12 @@ void SetObjectReaders()
   js::SetObjectReader(SCENE_READER);
 }
 
-void SetDefaultEnvironmentMap(const gt::Document& doc, ConversionContext& cctx)
+void SetDefaultEnvironmentMap(const gt::Document& doc, ConversionContext& context)
 {
   EnvironmentDefinition envDef;
   envDef.mUseBrdfTexture = true;
   envDef.mIblIntensity   = Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
-  cctx.mOutput.mResources.mEnvironmentMaps.push_back({std::move(envDef), EnvironmentDefinition::Textures()});
+  context.mOutput.mResources.mEnvironmentMaps.push_back({std::move(envDef), EnvironmentDefinition::Textures()});
 }
 
 } // namespace
@@ -1222,18 +1214,18 @@ void LoadGltfScene(const std::string& url, ShaderDefinitionFactory& shaderFactor
   DOCUMENT_READER.Read(rootObj, doc);
 
   auto              path = url.substr(0, url.rfind('/') + 1);
-  ConversionContext cctx{params, path, INVALID_INDEX};
+  ConversionContext context{params, path, INVALID_INDEX};
 
-  ConvertMaterials(doc, cctx);
-  ConvertMeshes(doc, cctx);
-  ConvertNodes(doc, cctx, isMRendererModel);
-  ConvertAnimations(doc, cctx);
-  ProcessSkins(doc, cctx);
+  ConvertMaterials(doc, context);
+  ConvertMeshes(doc, context);
+  ConvertNodes(doc, context, isMRendererModel);
+  ConvertAnimations(doc, context);
+  ProcessSkins(doc, context);
   ProduceShaders(shaderFactory, params.mScene);
   params.mScene.EnsureUniqueSkinningShaderInstances(params.mResources);
 
   // Set Default Environment map
-  SetDefaultEnvironmentMap(doc, cctx);
+  SetDefaultEnvironmentMap(doc, context);
 }
 
 } // namespace Loader
index d96d888..e45b69b 100644 (file)
@@ -234,6 +234,7 @@ public: // DATA
   bool mNeedAlbedoTexture            = true;
   bool mNeedMetallicRoughnessTexture = true;
   bool mNeedNormalTexture            = true;
+  bool mDoubleSided                  = false;
 
   std::vector<TextureStage> mTextureStages;
 };
index c83fc44..87fc246 100644 (file)
@@ -77,9 +77,9 @@ Actor NodeDefinition::CreateActor(CreateParams& params) const
 
   actor.RegisterProperty(ORIGINAL_MATRIX_PROPERTY_NAME, GetLocalSpace(), Property::AccessMode::READ_ONLY);
 
-  if(mRenderable)
+  for(auto& renderable : mRenderables)
   {
-    mRenderable->OnCreate(*this, params, actor);
+    renderable->OnCreate(*this, params, actor);
   }
 
   for(auto& e : mExtras)
@@ -114,20 +114,38 @@ std::string_view NodeDefinition::GetIblYDirectionUniformName()
 
 bool NodeDefinition::GetExtents(const ResourceBundle& resources, Vector3& min, Vector3& max) const
 {
-  if(mRenderable)
+  if(mRenderables.empty())
   {
-    if(!mRenderable->GetExtents(resources, min, max))
+    return false;
+  }
+
+  bool useModelExtents = false;
+  for(auto& renderable : mRenderables)
+  {
+    Vector3 renderableMin(Vector3::ONE * MAXFLOAT), renderableMax(-Vector3::ONE * MAXFLOAT);
+    if(!renderable->GetExtents(resources, renderableMin, renderableMax))
     {
-      // If the renderable node don't have mesh accessor, use size to compute extents.
-      min = -mSize / 2.0f;
-      max = mSize / 2.0f;
+      useModelExtents = false;
+      break;
     }
-    return true;
+    useModelExtents = true;
+    min.x           = std::min(min.x, renderableMin.x);
+    min.y           = std::min(min.y, renderableMin.y);
+    min.z           = std::min(min.z, renderableMin.z);
+    max.x           = std::max(max.x, renderableMax.x);
+    max.y           = std::max(max.y, renderableMax.y);
+    max.z           = std::max(max.z, renderableMax.z);
   }
-  return false;
+  if(!useModelExtents)
+  {
+    // If the renderable node don't have mesh accessor, use size to compute extents.
+    min = -mSize / 2.0f;
+    max = mSize / 2.0f;
+  }
+  return true;
 }
 
-bool ModelNode::GetExtents(const ResourceBundle& resources, Vector3& min, Vector3& max) const
+bool ModelRenderable::GetExtents(const ResourceBundle& resources, Vector3& min, Vector3& max) const
 {
   auto&    mesh    = resources.mMeshes[mMeshIdx];
   uint32_t minSize = mesh.first.mPositions.mBlob.mMin.size();
@@ -146,21 +164,21 @@ bool ModelNode::GetExtents(const ResourceBundle& resources, Vector3& min, Vector
   return false;
 }
 
-void ModelNode::RegisterResources(IResourceReceiver& receiver) const
+void ModelRenderable::RegisterResources(IResourceReceiver& receiver) const
 {
   Renderable::RegisterResources(receiver);
   receiver.Register(ResourceType::Mesh, mMeshIdx);
   receiver.Register(ResourceType::Material, mMaterialIdx);
 }
 
-void ModelNode::ReflectResources(IResourceReflector& reflector)
+void ModelRenderable::ReflectResources(IResourceReflector& reflector)
 {
   Renderable::ReflectResources(reflector);
   reflector.Reflect(ResourceType::Mesh, mMeshIdx);
   reflector.Reflect(ResourceType::Material, mMaterialIdx);
 }
 
-void ModelNode::OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams& params, Actor& actor) const
+void ModelRenderable::OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams& params, Actor& actor) const
 {
   DALI_ASSERT_DEBUG(mMeshIdx != INVALID_INDEX);
   Renderable::OnCreate(node, params, actor);
@@ -168,7 +186,7 @@ void ModelNode::OnCreate(const NodeDefinition& node, NodeDefinition::CreateParam
   auto& resources = params.mResources;
   auto& mesh      = resources.mMeshes[mMeshIdx];
 
-  auto     renderer = actor.GetRendererAt(0);
+  auto     renderer = actor.GetRendererAt(actor.GetRendererCount() - 1u);
   Geometry geometry = mesh.second.geometry;
   renderer.SetGeometry(geometry);
 
@@ -203,29 +221,25 @@ void ModelNode::OnCreate(const NodeDefinition& node, NodeDefinition::CreateParam
     textures = newTextureSet;
   }
 
-  renderer.SetTextures(textures);
-
-  actor.SetProperty(Actor::Property::COLOR, mColor);
-
-  actor.RegisterProperty("uHasVertexColor", static_cast<float>(mesh.first.mColors.IsDefined()));
+  renderer.RegisterProperty("uHasVertexColor", static_cast<float>(mesh.first.mColors.IsDefined()));
 
   auto& matDef = resources.mMaterials[mMaterialIdx].first;
-  actor.RegisterProperty("uColorFactor", matDef.mBaseColorFactor);
-  actor.RegisterProperty("uMetallicFactor", matDef.mMetallic);
-  actor.RegisterProperty("uRoughnessFactor", matDef.mRoughness);
-  actor.RegisterProperty("uNormalScale", matDef.mNormalScale);
+  renderer.RegisterProperty("uColorFactor", matDef.mBaseColorFactor);
+  renderer.RegisterProperty("uMetallicFactor", matDef.mMetallic);
+  renderer.RegisterProperty("uRoughnessFactor", matDef.mRoughness);
+  renderer.RegisterProperty("uNormalScale", matDef.mNormalScale);
   if(matDef.mFlags & MaterialDefinition::OCCLUSION)
   {
-    actor.RegisterProperty("uOcclusionStrength", matDef.mOcclusionStrength);
+    renderer.RegisterProperty("uOcclusionStrength", matDef.mOcclusionStrength);
   }
   if(matDef.mFlags & MaterialDefinition::EMISSIVE)
   {
-    actor.RegisterProperty("uEmissiveFactor", matDef.mEmissiveFactor);
+    renderer.RegisterProperty("uEmissiveFactor", matDef.mEmissiveFactor);
   }
 
   Index envIdx = matDef.mEnvironmentIdx;
-  actor.RegisterProperty(IBL_INTENSITY_STRING.data(), resources.mEnvironmentMaps[envIdx].first.mIblIntensity);
-  actor.RegisterProperty(IBL_Y_DIRECTION.data(), resources.mEnvironmentMaps[envIdx].first.mYDirection);
+  renderer.RegisterProperty(IBL_INTENSITY_STRING.data(), resources.mEnvironmentMaps[envIdx].first.mIblIntensity);
+  renderer.RegisterProperty(IBL_Y_DIRECTION.data(), resources.mEnvironmentMaps[envIdx].first.mYDirection);
 
   float opaque      = 0.0f;
   float mask        = 0.0f;
@@ -241,14 +255,18 @@ void ModelNode::OnCreate(const NodeDefinition& node, NodeDefinition::CreateParam
       mask = 1.0f;
     }
   }
-  actor.RegisterProperty("uOpaque", opaque);
-  actor.RegisterProperty("uMask", mask);
-  actor.RegisterProperty("uAlphaThreshold", alphaCutoff);
+  renderer.RegisterProperty("uOpaque", opaque);
+  renderer.RegisterProperty("uMask", mask);
+  renderer.RegisterProperty("uAlphaThreshold", alphaCutoff);
+
+  renderer.SetTextures(textures);
+
+  actor.SetProperty(Actor::Property::COLOR, mColor);
 }
 
-void ArcNode::OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams& params, Actor& actor) const
+void ArcRenderable::OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams& params, Actor& actor) const
 {
-  ModelNode::OnCreate(node, params, actor);
+  ModelRenderable::OnCreate(node, params, actor);
 
   actor.RegisterProperty("antiAliasing", mAntiAliasing ? 1 : 0);
   actor.RegisterProperty("arcCaps", mArcCaps);
@@ -263,13 +281,13 @@ void ArcNode::OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams&
   actor.RegisterProperty("endAngle", endPolar);
 }
 
-void ArcNode::GetEndVectorWithDiffAngle(float startAngle, float diffAngle, Vector2& endVector)
+void ArcRenderable::GetEndVectorWithDiffAngle(float startAngle, float diffAngle, Vector2& endVector)
 {
   float endAngle = 0.f;
 
   if(diffAngle <= 0.001f)
   {
-    //0.001 is used to ensure is empty arc when startAngle = endAngle + 360 * N
+    // 0.001 is used to ensure is empty arc when startAngle = endAngle + 360 * N
     endAngle = startAngle + 0.001f;
   }
   else if(diffAngle >= 360.f)
index a86d7e9..fde74e2 100644 (file)
@@ -261,7 +261,7 @@ public: // DATA
 
   bool mIsVisible = true;
 
-  std::unique_ptr<Renderable>              mRenderable;
+  std::vector<std::unique_ptr<Renderable>> mRenderables;
   std::unique_ptr<CustomizationDefinition> mCustomization;
   std::vector<Extra>                       mExtras;
   std::vector<ConstraintDefinition>        mConstraints;
@@ -270,7 +270,7 @@ public: // DATA
   Index              mParentIdx = INVALID_INDEX;
 };
 
-class DALI_SCENE3D_API ModelNode : public NodeDefinition::Renderable
+class DALI_SCENE3D_API ModelRenderable : public NodeDefinition::Renderable
 {
 public: // DATA
   Vector4 mColor       = Color::WHITE;
@@ -287,7 +287,7 @@ public: // METHODS
 /**
  * @brief Parameters for an Arc node.
  */
-class DALI_SCENE3D_API ArcNode : public ModelNode
+class DALI_SCENE3D_API ArcRenderable : public ModelRenderable
 {
 public: // DATA
   bool  mAntiAliasing      = true;
@@ -306,4 +306,4 @@ public: // METHODS
 } // namespace Scene3D
 } // namespace Dali
 
-#endif //DALI_SCENE3D_LOADER_NODE_DEFINITION_H_
+#endif // DALI_SCENE3D_LOADER_NODE_DEFINITION_H_
index 6e26c0f..a117e1e 100644 (file)
@@ -453,9 +453,9 @@ void SceneDefinition::CountResourceRefs(Index iNode, const Customization::Choice
 
     void Start(const NodeDefinition& n)
     {
-      if(n.mRenderable)
+      for(auto& renderable : n.mRenderables)
       {
-        n.mRenderable->RegisterResources(counter);
+        renderable->RegisterResources(counter);
       }
     }
 
@@ -526,7 +526,7 @@ void SceneDefinition::GetCustomizationOptions(const Customization::Choices& choi
 
 NodeDefinition* SceneDefinition::AddNode(std::unique_ptr<NodeDefinition>&& nodeDef)
 {
-  if(FindNode(nodeDef->mName))
+  if(!nodeDef->mName.empty() && FindNode(nodeDef->mName))
   {
     return nullptr;
   }
@@ -991,10 +991,10 @@ void SceneDefinition::EnsureUniqueSkinningShaderInstances(ResourceBundle& resour
   std::map<Index, std::map<Index, std::vector<Index*>>> skinningShaderUsers;
   for(auto& node : mNodes)
   {
-    if(node->mRenderable)
+    for(auto& renderable : node->mRenderables)
     {
       ResourceReflector reflector;
-      node->mRenderable->ReflectResources(reflector);
+      renderable->ReflectResources(reflector);
 
       if(reflector.iMesh)
       {
@@ -1045,21 +1045,21 @@ void SceneDefinition::ConfigureSkinningShaders(const ResourceBundle&
 
   SortAndDeduplicateSkinningRequests(requests);
 
-  for(auto& i : requests)
+  for(auto& request : requests)
   {
-    auto& skeleton = resources.mSkeletons[i.mSkeletonIdx];
+    auto& skeleton = resources.mSkeletons[request.mSkeletonIdx];
     if(skeleton.mJoints.empty())
     {
-      LOGD(("Skeleton %d has no joints.", i.mSkeletonIdx));
+      LOGD(("Skeleton %d has no joints.", request.mSkeletonIdx));
       continue;
     }
 
     Index boneIdx = 0;
-    for(auto& j : skeleton.mJoints)
+    for(auto& joint : skeleton.mJoints)
     {
-      auto  node  = GetNode(j.mNodeIdx);
+      auto  node  = GetNode(joint.mNodeIdx);
       Actor actor = rootActor.FindChildByName(node->mName);
-      ConfigureBoneMatrix(j.mInverseBindMatrix, actor, i.mShader, boneIdx);
+      ConfigureBoneMatrix(joint.mInverseBindMatrix, actor, request.mShader, boneIdx);
     }
   }
 }
@@ -1129,10 +1129,10 @@ void SceneDefinition::EnsureUniqueBlendShapeShaderInstances(ResourceBundle& reso
   std::map<Index, std::map<std::string, std::vector<Index*>>> blendShapeShaderUsers;
   for(auto& node : mNodes)
   {
-    if(node->mRenderable)
+    for(auto& renderable : node->mRenderables)
     {
       ResourceReflector reflector;
-      node->mRenderable->ReflectResources(reflector);
+      renderable->ReflectResources(reflector);
 
       if(reflector.iMesh)
       {
index 59dd3b4..ec41ef4 100644 (file)
@@ -68,7 +68,7 @@ void RetrieveBlendShapeComponents(const std::vector<MeshDefinition::BlendShape>&
   }
 }
 
-uint64_t HashNode(const NodeDefinition& nodeDef, const MaterialDefinition& materialDef, const MeshDefinition& meshDef)
+uint64_t HashNode(const MaterialDefinition& materialDef, const MeshDefinition& meshDef)
 {
   Hash hash;
 
@@ -173,145 +173,155 @@ void ShaderDefinitionFactory::SetResources(ResourceBundle& resources)
   mImpl->mShaderMap.clear();
 }
 
-Index ShaderDefinitionFactory::ProduceShader(const NodeDefinition& nodeDef)
+Index ShaderDefinitionFactory::ProduceShader(NodeDefinition::Renderable& renderable)
 {
-  DALI_ASSERT_DEBUG(nodeDef.mRenderable);
+  auto& resources = *mImpl->mResources;
 
-  auto&            resources = *mImpl->mResources;
   ResourceReceiver receiver{resources};
-  nodeDef.mRenderable->RegisterResources(receiver);
+  renderable.RegisterResources(receiver);
+
   if(!(receiver.mMeshDef && receiver.mMaterialDef))
   {
+    renderable.mShaderIdx = INVALID_INDEX;
     return INVALID_INDEX;
   }
 
   auto&    shaderMap = mImpl->mShaderMap;
-  uint64_t hash      = HashNode(nodeDef, *receiver.mMaterialDef, *receiver.mMeshDef);
+  uint64_t hash      = HashNode(*receiver.mMaterialDef, *receiver.mMeshDef);
   auto     iFind     = shaderMap.find(hash);
   if(iFind != shaderMap.end())
   {
-    return iFind->second;
-  }
-
-  ShaderDefinition shaderDef;
-  shaderDef.mUseBuiltInShader = true;
-  shaderDef.mRendererState    = RendererState::DEPTH_TEST | RendererState::DEPTH_WRITE | RendererState::CULL_BACK;
-
-  auto&      materialDef     = *receiver.mMaterialDef;
-  const bool hasTransparency = MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY);
-  if(hasTransparency)
-  {
-    // TODO: this requires more granularity
-    shaderDef.mRendererState = (shaderDef.mRendererState | RendererState::ALPHA_BLEND) & ~RendererState::DEPTH_WRITE;
+    renderable.mShaderIdx = iFind->second;
   }
-
-  if(hasTransparency ||
-     !materialDef.CheckTextures(MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC) ||
-     !materialDef.CheckTextures(MaterialDefinition::NORMAL | MaterialDefinition::ROUGHNESS))
-
+  else
   {
-    shaderDef.mDefines.push_back("THREE_TEX");
+    ShaderDefinition shaderDef;
+    shaderDef.mUseBuiltInShader = true;
+    shaderDef.mRendererState    = RendererState::DEPTH_TEST | RendererState::DEPTH_WRITE;
 
-    // For the glTF, each of basecolor, metallic_roughness, normal texture is not essential.
-    if(MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO))
+    auto& materialDef = *receiver.mMaterialDef;
+    if(!materialDef.mDoubleSided)
     {
-      shaderDef.mDefines.push_back("BASECOLOR_TEX");
+      shaderDef.mRendererState |= RendererState::CULL_BACK;
     }
 
-    if(materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS))
+    const bool hasTransparency = MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY);
+    if(hasTransparency)
     {
-      shaderDef.mDefines.push_back("METALLIC_ROUGHNESS_TEX");
+      // TODO: this requires more granularity
+      shaderDef.mRendererState = (shaderDef.mRendererState | RendererState::ALPHA_BLEND) & ~RendererState::DEPTH_WRITE;
     }
 
-    if(MaskMatch(materialDef.mFlags, MaterialDefinition::NORMAL))
-    {
-      shaderDef.mDefines.push_back("NORMAL_TEX");
-    }
-  }
+    if(hasTransparency ||
+       !materialDef.CheckTextures(MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC) ||
+       !materialDef.CheckTextures(MaterialDefinition::NORMAL | MaterialDefinition::ROUGHNESS))
 
-  if(materialDef.GetAlphaCutoff() > 0.f)
-  {
-    shaderDef.mDefines.push_back("ALPHA_TEST");
-  }
+    {
+      shaderDef.mDefines.push_back("THREE_TEX");
 
-  if(MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
-  {
-    shaderDef.mDefines.push_back("SSS");
-  }
+      // For the glTF, each of basecolor, metallic_roughness, normal texture is not essential.
+      if(MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO))
+      {
+        shaderDef.mDefines.push_back("BASECOLOR_TEX");
+      }
 
-  if(MaskMatch(materialDef.mFlags, MaterialDefinition::OCCLUSION))
-  {
-    shaderDef.mDefines.push_back("OCCLUSION");
-  }
+      if(materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS))
+      {
+        shaderDef.mDefines.push_back("METALLIC_ROUGHNESS_TEX");
+      }
 
-  if(MaskMatch(materialDef.mFlags, MaterialDefinition::EMISSIVE))
-  {
-    shaderDef.mDefines.push_back("EMISSIVE");
-  }
+      if(MaskMatch(materialDef.mFlags, MaterialDefinition::NORMAL))
+      {
+        shaderDef.mDefines.push_back("NORMAL_TEX");
+      }
+    }
 
-  if(MaskMatch(materialDef.mFlags, MaterialDefinition::GLTF_CHANNELS))
-  {
-    shaderDef.mDefines.push_back("GLTF_CHANNELS");
-  }
+    if(materialDef.GetAlphaCutoff() > 0.f)
+    {
+      shaderDef.mDefines.push_back("ALPHA_TEST");
+    }
 
-  const auto& meshDef = *receiver.mMeshDef;
-  if(meshDef.IsSkinned())
-  {
-    shaderDef.mDefines.push_back("SKINNING");
-  }
+    if(MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
+    {
+      shaderDef.mDefines.push_back("SSS");
+    }
 
-  if(MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
-  {
-    shaderDef.mDefines.push_back("FLIP_V");
-  }
+    if(MaskMatch(materialDef.mFlags, MaterialDefinition::OCCLUSION))
+    {
+      shaderDef.mDefines.push_back("OCCLUSION");
+    }
 
-  if(meshDef.HasBlendShapes())
-  {
-    bool hasPositions = false;
-    bool hasNormals   = false;
-    bool hasTangents  = false;
-    RetrieveBlendShapeComponents(meshDef.mBlendShapes, hasPositions, hasNormals, hasTangents);
+    if(MaskMatch(materialDef.mFlags, MaterialDefinition::EMISSIVE))
+    {
+      shaderDef.mDefines.push_back("EMISSIVE");
+    }
 
-    if(hasPositions)
+    if(MaskMatch(materialDef.mFlags, MaterialDefinition::GLTF_CHANNELS))
     {
-      shaderDef.mDefines.push_back("MORPH_POSITION");
+      shaderDef.mDefines.push_back("GLTF_CHANNELS");
     }
 
-    if(hasNormals)
+    const auto& meshDef = *receiver.mMeshDef;
+    if(meshDef.IsSkinned())
     {
-      shaderDef.mDefines.push_back("MORPH_NORMAL");
+      shaderDef.mDefines.push_back("SKINNING");
     }
 
-    if(hasTangents)
+    if(MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
     {
-      shaderDef.mDefines.push_back("MORPH_TANGENT");
+      shaderDef.mDefines.push_back("FLIP_V");
     }
 
-    if(hasPositions || hasNormals || hasTangents)
+    if(meshDef.HasBlendShapes())
     {
-      shaderDef.mDefines.push_back("MORPH");
+      bool hasPositions = false;
+      bool hasNormals   = false;
+      bool hasTangents  = false;
+      RetrieveBlendShapeComponents(meshDef.mBlendShapes, hasPositions, hasNormals, hasTangents);
 
-      if(BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
+      if(hasPositions)
+      {
+        shaderDef.mDefines.push_back("MORPH_POSITION");
+      }
+
+      if(hasNormals)
       {
-        shaderDef.mDefines.push_back("MORPH_VERSION_2_0");
+        shaderDef.mDefines.push_back("MORPH_NORMAL");
+      }
+
+      if(hasTangents)
+      {
+        shaderDef.mDefines.push_back("MORPH_TANGENT");
+      }
+
+      if(hasPositions || hasNormals || hasTangents)
+      {
+        shaderDef.mDefines.push_back("MORPH");
+
+        if(BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
+        {
+          shaderDef.mDefines.push_back("MORPH_VERSION_2_0");
+        }
       }
     }
-  }
 
-  if(meshDef.mTangentType == Property::VECTOR4)
-  {
-    shaderDef.mDefines.push_back("VEC4_TANGENT");
-  }
+    if(meshDef.mTangentType == Property::VECTOR4)
+    {
+      shaderDef.mDefines.push_back("VEC4_TANGENT");
+    }
 
-  shaderDef.mUniforms["uMaxLOD"]     = 6.f;
-  shaderDef.mUniforms["uCubeMatrix"] = Matrix::IDENTITY;
+    shaderDef.mUniforms["uMaxLOD"]     = 6.f;
+    shaderDef.mUniforms["uCubeMatrix"] = Matrix::IDENTITY;
 
-  Index result    = resources.mShaders.size();
-  shaderMap[hash] = result;
+    Index result    = resources.mShaders.size();
+    shaderMap[hash] = result;
 
-  resources.mShaders.emplace_back(std::move(shaderDef), Shader());
+    resources.mShaders.emplace_back(std::move(shaderDef), Shader());
+
+    renderable.mShaderIdx = result;
+  }
 
-  return result;
+  return renderable.mShaderIdx;
 }
 
 } // namespace Loader
index 8c4434a..b7e9195 100644 (file)
@@ -20,6 +20,7 @@
 // INTERNAL INCLUDES
 #include "dali-scene3d/public-api/api.h"
 #include "dali-scene3d/public-api/loader/index.h"
+#include <dali-scene3d/public-api/loader/node-definition.h>
 
 // EXTERNAL INCLUDER
 #include <memory>
@@ -51,7 +52,7 @@ public:
    *  already existing in the ResourceBundle are ignored), otherwise the index of the previously
    *  created shader will be returned.
    */
-  Index ProduceShader(const NodeDefinition& nodeDef);
+  Index ProduceShader(NodeDefinition::Renderable& renderable);
 
 private:
   struct Impl;