Asynchronous loading of Scene3D resources 11/284211/35
authorseunghobaek <sbsh.baek@samsung.com>
Fri, 11 Nov 2022 12:50:25 +0000 (21:50 +0900)
committerseungho baek <sbsh.baek@samsung.com>
Thu, 12 Jan 2023 05:07:51 +0000 (14:07 +0900)
Change-Id: Idd19cbcfc1bdcfb7e8cd9d2ea6691a9060bcfb78
Signed-off-by: seunghobaek <sbsh.baek@samsung.com>
24 files changed:
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-SceneView.cpp
dali-scene3d/internal/common/environment-map-load-task.cpp [new file with mode: 0644]
dali-scene3d/internal/common/environment-map-load-task.h [new file with mode: 0644]
dali-scene3d/internal/common/model-load-task.cpp [new file with mode: 0644]
dali-scene3d/internal/common/model-load-task.h [new file with mode: 0644]
dali-scene3d/internal/controls/model/model-impl.cpp
dali-scene3d/internal/controls/model/model-impl.h
dali-scene3d/internal/controls/scene-view/scene-view-impl.cpp
dali-scene3d/internal/controls/scene-view/scene-view-impl.h
dali-scene3d/internal/file.list
dali-scene3d/public-api/controls/scene-view/scene-view.h
dali-scene3d/public-api/loader/animation-definition.cpp
dali-scene3d/public-api/loader/cube-loader.cpp
dali-scene3d/public-api/loader/environment-definition.cpp
dali-scene3d/public-api/loader/environment-definition.h
dali-scene3d/public-api/loader/gltf2-loader.cpp
dali-scene3d/public-api/loader/gltf2-loader.h
dali-scene3d/public-api/loader/material-definition.h
dali-scene3d/public-api/loader/mesh-definition.h
dali-scene3d/public-api/loader/resource-bundle.cpp
dali-scene3d/public-api/loader/resource-bundle.h
dali-scene3d/public-api/loader/shader-definition.h

index a6256f5..c1fcf61 100644 (file)
@@ -55,7 +55,8 @@ namespace
 {
 struct Context
 {
-  ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) {
+  ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type)
+  {
     return TEST_RESOURCE_DIR "/";
   };
 
@@ -102,6 +103,7 @@ int UtcDaliGltfLoaderFailedToLoad(void)
   ShaderDefinitionFactory sdf;
   sdf.SetResources(ctx.resources);
 
+  InitializeGltfLoader();
   DALI_TEST_THROW(LoadGltfScene("non-existent.gltf", sdf, ctx.loadResult),
                   std::runtime_error,
                   ExceptionMessageStartsWith{"Failed to load"});
@@ -130,6 +132,7 @@ int UtcDaliGltfLoaderFailedToParse(void)
   ShaderDefinitionFactory sdf;
   sdf.SetResources(ctx.resources);
 
+  InitializeGltfLoader();
   DALI_TEST_THROW(LoadGltfScene(TEST_RESOURCE_DIR "/invalid.gltf", sdf, ctx.loadResult),
                   std::runtime_error,
                   ExceptionMessageStartsWith{"Failed to parse"});
@@ -173,6 +176,7 @@ int UtcDaliGltfLoaderSuccess1(void)
   ShaderDefinitionFactory sdf;
   sdf.SetResources(ctx.resources);
 
+  InitializeGltfLoader();
   LoadGltfScene(TEST_RESOURCE_DIR "/AnimatedCube.gltf", sdf, ctx.loadResult);
 
   DALI_TEST_EQUAL(1u, ctx.scene.GetRoots().size());
@@ -195,105 +199,155 @@ int UtcDaliGltfLoaderSuccess1(void)
   auto& materials = ctx.resources.mMaterials;
   DALI_TEST_EQUAL(2u, materials.size());
   const MaterialDefinition materialGroundTruth[]{
-    {MaterialDefinition::ALBEDO | MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
-       MaterialDefinition::NORMAL | MaterialDefinition::SPECULAR | MaterialDefinition::SPECULAR_COLOR |
-       (0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT),
-     0,
-     Color::WHITE,
-     1.f,
-     0.f,
-     Vector4(1.000, 0.766, 0.336, 1.0),
-     1.f,
-     1.f,
-     Vector3(0.2, 0.1, 0.0),
-     0.0f,
-     0.5f,
-     Vector3(0, 0, 1),
-     true,
-     false,
-     true,
-     false,
-     true,
-     true,
-     {
-       {MaterialDefinition::ALBEDO,
-        {"AnimatedCube_BaseColor.png",
-         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-         ImageDimensions(256, 256),
-         SamplingMode::BOX_THEN_NEAREST}},
-       {MaterialDefinition::NORMAL,
-        {"AnimatedCube_BaseColor.png",
-         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-         ImageDimensions(256, 256),
-         SamplingMode::BOX_THEN_NEAREST}},
-       {MaterialDefinition::OCCLUSION,
-        {"AnimatedCube_BaseColor.png",
-         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-         ImageDimensions(256, 256),
-         SamplingMode::BOX_THEN_NEAREST}},
-       {MaterialDefinition::EMISSIVE,
-        {"AnimatedCube_BaseColor.png",
-         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-         ImageDimensions(256, 256),
-         SamplingMode::BOX_THEN_NEAREST}},
-       {MaterialDefinition::SPECULAR,
-        {"AnimatedCube_BaseColor.png",
-         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-         ImageDimensions(256, 256),
-         SamplingMode::BOX_THEN_NEAREST}},
-       {MaterialDefinition::SPECULAR_COLOR,
-        {"AnimatedCube_BaseColor.png",
-         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-         ImageDimensions(256, 256),
-         SamplingMode::BOX_THEN_NEAREST}},
-     }},
-    {MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
-       MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
-       MaterialDefinition::NORMAL | MaterialDefinition::GLTF_CHANNELS,
-     0,
-     Color::WHITE,
-     1.f,
-     0.f,
-     Vector4(1.000, 0.766, 0.336, 1.0),
-     1.f,
-     1.f,
-     Vector3(0.2, 0.1, 0.0),
-     0.04f,
-     1.0f,
-     Vector3::ONE,
-     true,
-     true,
-     true,
-     false,
-     true,
-     false,
-     {
-       {MaterialDefinition::ALBEDO,
-        {"AnimatedCube_BaseColor.png",
-         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-         ImageDimensions(256, 256),
-         SamplingMode::BOX_THEN_NEAREST}},
-       {MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | MaterialDefinition::GLTF_CHANNELS,
-        {"AnimatedCube_MetallicRoughness.png",
-         SamplerFlags::Encode(FilterMode::NEAREST_MIPMAP_LINEAR, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::MIRRORED_REPEAT),
-         ImageDimensions(256, 256),
-         SamplingMode::NEAREST}},
-       {MaterialDefinition::NORMAL,
-        {"AnimatedCube_BaseColor.png",
-         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-         ImageDimensions(256, 256),
-         SamplingMode::BOX_THEN_NEAREST}},
-       {MaterialDefinition::OCCLUSION,
-        {"AnimatedCube_BaseColor.png",
-         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-         ImageDimensions(256, 256),
-         SamplingMode::BOX_THEN_NEAREST}},
-       {MaterialDefinition::EMISSIVE,
-        {"AnimatedCube_BaseColor.png",
-         SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
-         ImageDimensions(256, 256),
-         SamplingMode::BOX_THEN_NEAREST}},
-     }},
+    {
+      nullptr,
+      MaterialDefinition::ALBEDO | MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
+        MaterialDefinition::NORMAL | MaterialDefinition::SPECULAR | MaterialDefinition::SPECULAR_COLOR |
+        (0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT),
+      0,
+      Color::WHITE,
+      1.f,
+      0.f,
+      Vector4(1.000, 0.766, 0.336, 1.0),
+      1.f,
+      1.f,
+      Vector3(0.2, 0.1, 0.0),
+      0.0f,
+      0.5f,
+      Vector3(0, 0, 1),
+      true,
+      false,
+      true,
+      false,
+      true,
+      true,
+      {
+        {
+          MaterialDefinition::ALBEDO,
+          {
+            "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+            ImageDimensions(256, 256),
+            SamplingMode::BOX_THEN_NEAREST,
+          },
+        },
+        {
+          MaterialDefinition::NORMAL,
+          {
+            "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+            ImageDimensions(256, 256),
+            SamplingMode::BOX_THEN_NEAREST,
+          },
+        },
+        {
+          MaterialDefinition::OCCLUSION,
+          {
+            "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+            ImageDimensions(256, 256),
+            SamplingMode::BOX_THEN_NEAREST,
+          },
+        },
+        {
+          MaterialDefinition::EMISSIVE,
+          {
+            "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+            ImageDimensions(256, 256),
+            SamplingMode::BOX_THEN_NEAREST,
+          },
+        },
+        {
+          MaterialDefinition::SPECULAR,
+          {
+            "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+            ImageDimensions(256, 256),
+            SamplingMode::BOX_THEN_NEAREST,
+          },
+        },
+        {
+          MaterialDefinition::SPECULAR_COLOR,
+          {
+            "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+            ImageDimensions(256, 256),
+            SamplingMode::BOX_THEN_NEAREST,
+          },
+        },
+      },
+    },
+    {
+      nullptr,
+      MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
+        MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION | MaterialDefinition::NORMAL |
+        MaterialDefinition::GLTF_CHANNELS,
+      0,
+      Color::WHITE,
+      1.f,
+      0.f,
+      Vector4(1.000, 0.766, 0.336, 1.0),
+      1.f,
+      1.f,
+      Vector3(0.2, 0.1, 0.0),
+      0.04f,
+      1.0f,
+      Vector3::ONE,
+      true,
+      true,
+      true,
+      false,
+      true,
+      false,
+      {
+        {
+          MaterialDefinition::ALBEDO,
+          {
+            "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+            ImageDimensions(256, 256),
+            SamplingMode::BOX_THEN_NEAREST,
+          },
+        },
+        {
+          MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | MaterialDefinition::GLTF_CHANNELS,
+          {
+            "AnimatedCube_MetallicRoughness.png",
+            SamplerFlags::Encode(FilterMode::NEAREST_MIPMAP_LINEAR, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::MIRRORED_REPEAT),
+            ImageDimensions(256, 256),
+            SamplingMode::NEAREST,
+          },
+        },
+        {
+          MaterialDefinition::NORMAL,
+          {
+            "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+            ImageDimensions(256, 256),
+            SamplingMode::BOX_THEN_NEAREST,
+          },
+        },
+        {
+          MaterialDefinition::OCCLUSION,
+          {
+            "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+            ImageDimensions(256, 256),
+            SamplingMode::BOX_THEN_NEAREST,
+          },
+        },
+        {
+          MaterialDefinition::EMISSIVE,
+          {
+            "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
+            ImageDimensions(256, 256),
+            SamplingMode::BOX_THEN_NEAREST,
+          },
+        },
+      },
+    },
   };
 
   auto iMaterial = materials.begin();
@@ -344,6 +398,7 @@ int UtcDaliGltfLoaderSuccess1(void)
   using Accessor = MeshDefinition::Accessor;
   const MeshDefinition meshGroundTruth[]{
     {
+      nullptr,
       0,
       Geometry::TRIANGLES,
       "AnimatedCube.bin",
@@ -355,6 +410,7 @@ int UtcDaliGltfLoaderSuccess1(void)
       Accessor{Blob{0, 0}, {}},
     },
     {
+      nullptr,
       0,
       Geometry::TRIANGLES,
       "AnimatedCube.bin",
@@ -407,10 +463,11 @@ int UtcDaliGltfLoaderSuccess1(void)
 
 int UtcDaliGltfLoaderSuccess2(void)
 {
-  Context ctx;
+  Context                 ctx;
   ShaderDefinitionFactory sdf;
   sdf.SetResources(ctx.resources);
 
+  InitializeGltfLoader();
   LoadGltfScene(TEST_RESOURCE_DIR "/AnimatedCubeStride.gltf", sdf, ctx.loadResult);
 
   DALI_TEST_EQUAL(1u, ctx.scene.GetRoots().size());
@@ -437,7 +494,8 @@ int UtcDaliGltfLoaderSuccessShort(void)
   TestApplication app;
 
   const std::string resourcePath = TEST_RESOURCE_DIR "/";
-  auto              pathProvider = [resourcePath](ResourceType::Value) {
+  auto              pathProvider = [resourcePath](ResourceType::Value)
+  {
     return resourcePath;
   };
 
@@ -468,6 +526,7 @@ int UtcDaliGltfLoaderSuccessShort(void)
     sdf.SetResources(resources);
 
     printf("%s\n", modelName);
+    InitializeGltfLoader();
     LoadGltfScene(resourcePath + modelName + ".gltf", sdf, ctx.loadResult);
     DALI_TEST_CHECK(ctx.scene.GetNodeCount() > 0);
 
@@ -529,6 +588,7 @@ int UtcDaliGltfLoaderMRendererTest(void)
   sdf.SetResources(ctx.resources);
   auto& resources = ctx.resources;
 
+  InitializeGltfLoader();
   LoadGltfScene(TEST_RESOURCE_DIR "/MRendererTest.gltf", sdf, ctx.loadResult);
 
   auto& scene = ctx.scene;
@@ -583,7 +643,6 @@ int UtcDaliGltfLoaderMRendererTest(void)
   END_TEST;
 }
 
-
 int UtcDaliGltfLoaderAnimationLoadingTest(void)
 {
   Context ctx;
@@ -592,6 +651,7 @@ int UtcDaliGltfLoaderAnimationLoadingTest(void)
   sdf.SetResources(ctx.resources);
   auto& resources = ctx.resources;
 
+  InitializeGltfLoader();
   LoadGltfScene(TEST_RESOURCE_DIR "/BoxAnimated.gltf", sdf, ctx.loadResult);
 
   auto& scene = ctx.scene;
index db7a5e6..5d2c344 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <dali-toolkit/devel-api/focus-manager/keyboard-focus-manager-devel.h>
 #include <dali/integration-api/events/touch-event-integ.h>
+#include <toolkit-event-thread-callback.h>
 
 #include <dali-scene3d/public-api/controls/model/model.h>
 
@@ -93,6 +94,19 @@ void TestFocusChangedCallback(Actor, Actor)
   gFocusChangedCallBackCalled = true;
 }
 
+// For ResourceReady
+static bool gOnRelayoutCallBackCalled = false;
+void        OnRelayoutCallback(Actor actor)
+{
+  gOnRelayoutCallBackCalled = true;
+}
+
+static bool gResourceReadyCalled = false;
+void        OnResourceReady(Control control)
+{
+  gResourceReadyCalled = true;
+}
+
 } // namespace
 
 // Negative test case for a method
@@ -251,15 +265,23 @@ int UtcDaliModelOnScene01(void)
   ToolkitTestApplication application;
 
   Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
-
   application.GetScene().Add(model);
 
+  gResourceReadyCalled = false;
+  model.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   uint32_t modelCount = model.GetModelRoot().GetChildCount();
   DALI_TEST_EQUALS(1, modelCount, TEST_LOCATION);
-
   END_TEST;
 }
 
@@ -268,12 +290,21 @@ int UtcDaliModelOnScene02(void)
   ToolkitTestApplication application;
 
   Scene3D::Model model = Scene3D::Model::New(TEST_DLI_FILE_NAME);
-
   application.GetScene().Add(model);
 
+  gResourceReadyCalled = false;
+  model.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   uint32_t modelCount = model.GetModelRoot().GetChildCount();
   DALI_TEST_EQUALS(1, modelCount, TEST_LOCATION);
 
@@ -313,7 +344,20 @@ int UtcDaliModelGetNaturalSize(void)
   Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
 
   Vector3 naturalSize = model.GetNaturalSize();
+  DALI_TEST_EQUALS(Vector3::ZERO, naturalSize, TEST_LOCATION);
+
+  application.GetScene().Add(model);
 
+  gResourceReadyCalled = false;
+  model.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
+  naturalSize = model.GetNaturalSize();
   DALI_TEST_EQUALS(Vector3(2, 2, 2), naturalSize, TEST_LOCATION);
 
   Actor root = model.GetModelRoot();
@@ -327,12 +371,21 @@ int UtcDaliModelSetImageBasedLightSource01(void)
   ToolkitTestApplication application;
 
   Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
-
   application.GetScene().Add(model);
 
+  gResourceReadyCalled = false;
+  model.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   Actor meshActor = model.FindChildByName("AnimatedCube");
   DALI_TEST_CHECK(meshActor);
 
@@ -345,14 +398,27 @@ int UtcDaliModelSetImageBasedLightSource01(void)
   Texture diffuseTexture  = textureSet.GetTexture(7u);
   Texture specularTexture = textureSet.GetTexture(8u);
 
+  gResourceReadyCalled = false;
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
   model.SetImageBasedLightSource(TEST_DIFFUSE_TEXTURE, TEST_SPECULAR_TEXTURE);
 
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   Texture newDiffuseTexture  = textureSet.GetTexture(7u);
   Texture newSpecularTexture = textureSet.GetTexture(8u);
 
   DALI_TEST_NOT_EQUALS(diffuseTexture, newDiffuseTexture, 0.0f, TEST_LOCATION);
   DALI_TEST_NOT_EQUALS(specularTexture, newSpecularTexture, 0.0f, TEST_LOCATION);
 
+  model.Unparent();
+  model.Reset();
   END_TEST;
 }
 
@@ -361,12 +427,21 @@ int UtcDaliModelSetImageBasedLightSource02(void)
   ToolkitTestApplication application;
 
   Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
-
   application.GetScene().Add(model);
 
+  gResourceReadyCalled = false;
+  model.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   Actor meshActor = model.FindChildByName("AnimatedCube");
   DALI_TEST_CHECK(meshActor);
 
@@ -379,6 +454,7 @@ int UtcDaliModelSetImageBasedLightSource02(void)
   Texture diffuseTexture  = textureSet.GetTexture(7u);
   Texture specularTexture = textureSet.GetTexture(8u);
 
+  // if url is empty, loading is not requested.
   model.SetImageBasedLightSource("", "");
 
   Texture newDiffuseTexture  = textureSet.GetTexture(7u);
@@ -395,12 +471,21 @@ int UtcDaliModelSetImageBasedLightSource03(void)
   ToolkitTestApplication application;
 
   Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
-
   application.GetScene().Add(model);
 
+  gResourceReadyCalled = false;
+  model.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   Actor meshActor = model.FindChildByName("AnimatedCube");
   DALI_TEST_CHECK(meshActor);
 
@@ -413,8 +498,19 @@ int UtcDaliModelSetImageBasedLightSource03(void)
   Texture diffuseTexture  = textureSet.GetTexture(7u);
   Texture specularTexture = textureSet.GetTexture(8u);
 
+  gResourceReadyCalled = false;
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
   model.SetImageBasedLightSource("dummy.ktx", "dummy.ktx");
 
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   Texture newDiffuseTexture  = textureSet.GetTexture(7u);
   Texture newSpecularTexture = textureSet.GetTexture(8u);
 
@@ -424,6 +520,29 @@ int UtcDaliModelSetImageBasedLightSource03(void)
   END_TEST;
 }
 
+int UtcDaliModelSetImageBasedLightSource04(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  model.SetImageBasedLightSource(TEST_DIFFUSE_TEXTURE, TEST_SPECULAR_TEXTURE);
+  application.GetScene().Add(model);
+
+  gResourceReadyCalled = false;
+  model.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+  END_TEST;
+}
+
 int UtcDaliModelImageBasedFactor(void)
 {
   ToolkitTestApplication application;
@@ -453,12 +572,21 @@ int UtcDaliModelChildrenSensitive01(void)
   // Allow children actor's event before on scene.
   view.SetChildrenSensitive(true);
   DALI_TEST_EQUALS(view.GetChildrenSensitive(), true, TEST_LOCATION);
-
   application.GetScene().Add(view);
 
+  gResourceReadyCalled = false;
+  view.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   Actor meshActor = view.FindChildByName("AnimatedCube");
   DALI_TEST_CHECK(meshActor);
 
@@ -541,12 +669,21 @@ int UtcDaliModelChildrenSensitive02(void)
   // Block children actor's event before on scene.
   view.SetChildrenSensitive(false);
   DALI_TEST_EQUALS(view.GetChildrenSensitive(), false, TEST_LOCATION);
-
   application.GetScene().Add(view);
 
+  gResourceReadyCalled = false;
+  view.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   Actor meshActor = view.FindChildByName("AnimatedCube");
   DALI_TEST_CHECK(meshActor);
 
@@ -612,12 +749,21 @@ int UtcDaliModelChildrenFocusable01(void)
   // Allow children actor's focus before on scene.
   view.SetChildrenFocusable(true);
   DALI_TEST_EQUALS(view.GetChildrenFocusable(), true, TEST_LOCATION);
-
   application.GetScene().Add(view);
 
+  gResourceReadyCalled = false;
+  view.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   Actor meshActor = view.FindChildByName("AnimatedCube");
   DALI_TEST_CHECK(meshActor);
 
@@ -711,12 +857,21 @@ int UtcDaliModelModelChildrenFocusable02(void)
   // Block children actor's focus before on scene.
   view.SetChildrenFocusable(false);
   DALI_TEST_EQUALS(view.GetChildrenFocusable(), false, TEST_LOCATION);
-
   application.GetScene().Add(view);
 
+  gResourceReadyCalled = false;
+  view.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   Actor meshActor = view.FindChildByName("AnimatedCube");
   DALI_TEST_CHECK(meshActor);
 
@@ -783,12 +938,21 @@ int UtcDaliModelAnimation01(void)
 
   Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
   model.SetProperty(Dali::Actor::Property::SIZE, Vector2(50, 50));
-
   application.GetScene().Add(model);
 
+  gResourceReadyCalled = false;
+  model.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   uint32_t animationCount = model.GetAnimationCount();
   DALI_TEST_EQUALS(1, animationCount, TEST_LOCATION);
 
@@ -808,9 +972,16 @@ int UtcDaliModelAnimation02(void)
 
   Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_ANIMATION_TEST_FILE_NAME);
   model.SetProperty(Dali::Actor::Property::SIZE, Vector2(50, 50));
-
   application.GetScene().Add(model);
 
+  gResourceReadyCalled = false;
+  model.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
   application.SendNotification();
   application.Render();
 
@@ -836,12 +1007,21 @@ int UtcDaliModelAnimation03(void)
 
   Scene3D::Model model = Scene3D::Model::New(TEST_DLI_EXERCISE_FILE_NAME);
   model.SetProperty(Dali::Actor::Property::SIZE, Vector2(50, 50));
-
   application.GetScene().Add(model);
 
+  gResourceReadyCalled = false;
+  model.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   uint32_t animationCount = model.GetAnimationCount();
   DALI_TEST_EQUALS(18, animationCount, TEST_LOCATION);
 
@@ -861,9 +1041,16 @@ int UtcDaliModelMultiplePrimitives(void)
 
   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);
 
+  gResourceReadyCalled = false;
+  model.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
   application.SendNotification();
   application.Render();
 
@@ -882,9 +1069,16 @@ int UtcDaliModelColorMode(void)
   Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
   model.SetProperty(Dali::Actor::Property::SIZE, Vector2(50, 50));
   model.SetProperty(Dali::Actor::Property::COLOR, Color::RED);
-
   application.GetScene().Add(model);
 
+  gResourceReadyCalled = false;
+  model.ResourceReadySignal().Connect(&OnResourceReady);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
   application.SendNotification();
   application.Render();
 
@@ -897,23 +1091,6 @@ int UtcDaliModelColorMode(void)
 
   END_TEST;
 }
-
-// For ResourceReady
-namespace
-{
-static bool gOnRelayoutCallBackCalled = false;
-void        OnRelayoutCallback(Actor actor)
-{
-  gOnRelayoutCallBackCalled = true;
-}
-
-static bool gResourceReadyCalled = false;
-void        OnResourceReady(Control control)
-{
-  gResourceReadyCalled = true;
-}
-} // namespace
-
 int UtcDaliModelResourceReady(void)
 {
   ToolkitTestApplication application;
@@ -935,6 +1112,10 @@ int UtcDaliModelResourceReady(void)
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
   DALI_TEST_EQUALS(gOnRelayoutCallBackCalled, false, TEST_LOCATION);
   DALI_TEST_EQUALS(model.IsResourceReady(), true, TEST_LOCATION);
   DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
index f1b6bd7..ed3af58 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <dali-scene3d/public-api/controls/model/model.h>
 #include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
+#include <toolkit-event-thread-callback.h>
 
 using namespace Dali;
 using namespace Dali::Toolkit;
@@ -70,8 +71,11 @@ const char* TEST_GLTF_FILE_NAME = TEST_RESOURCE_DIR "/AnimatedCube.gltf";
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
-const char* TEST_DIFFUSE_TEXTURE  = TEST_RESOURCE_DIR "/forest_irradiance.ktx";
-const char* TEST_SPECULAR_TEXTURE = TEST_RESOURCE_DIR "/forest_radiance.ktx";
+const char* TEST_EQUIRECTANGULAR_TEXTURE = TEST_RESOURCE_DIR "/application-icon-20.png";
+const char* TEST_DIFFUSE_TEXTURE         = TEST_RESOURCE_DIR "/forest_irradiance.ktx";
+const char* TEST_SPECULAR_TEXTURE        = TEST_RESOURCE_DIR "/forest_radiance.ktx";
+const char* TEST_DIFFUSE_TEXTURE2        = TEST_RESOURCE_DIR "//forest_irradiance.ktx";
+const char* TEST_SPECULAR_TEXTURE2       = TEST_RESOURCE_DIR "//forest_radiance.ktx";
 
 Dali::Texture GetDiffuseTexture(Dali::Scene3D::Model model)
 {
@@ -114,6 +118,20 @@ Dali::Texture GetSpecularTexture(Dali::Scene3D::Model model)
 
   return texture;
 }
+
+
+// For ResourceReady
+static bool gOnRelayoutCallBackCalled = false;
+void OnRelayoutCallback(Actor actor)
+{
+  gOnRelayoutCallBackCalled = true;
+}
+
+static bool gResourceReadyCalled = false;
+void OnResourceReady(Control control)
+{
+  gResourceReadyCalled = true;
+}
 } // namespace
 
 // Negative test case for a method
@@ -422,6 +440,7 @@ int UtcDaliSceneViewImageBasedLight01(void)
   ToolkitTestApplication application;
 
   Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.ResourceReadySignal().Connect(OnResourceReady);
   view.SetProperty(Dali::Actor::Property::SIZE, Vector2(100, 100));
 
   application.GetScene().Add(view);
@@ -435,25 +454,59 @@ int UtcDaliSceneViewImageBasedLight01(void)
   view.Add(modelView1);
   view.Add(modelView2);
 
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
   DALI_TEST_NOT_EQUALS(GetDiffuseTexture(modelView1), GetDiffuseTexture(modelView2), 0.0f, TEST_LOCATION);
   DALI_TEST_NOT_EQUALS(GetSpecularTexture(modelView1), GetSpecularTexture(modelView2), 0.0f, TEST_LOCATION);
   DALI_TEST_NOT_EQUALS(GetDiffuseTexture(modelView1), GetDiffuseTexture(modelView3), 0.0f, TEST_LOCATION);
   DALI_TEST_NOT_EQUALS(GetSpecularTexture(modelView1), GetSpecularTexture(modelView3), 0.0f, TEST_LOCATION);
 
+  gResourceReadyCalled = false;
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
   view.SetImageBasedLightSource(TEST_DIFFUSE_TEXTURE, TEST_SPECULAR_TEXTURE);
 
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   DALI_TEST_EQUALS(GetDiffuseTexture(modelView1), GetDiffuseTexture(modelView2), TEST_LOCATION);
   DALI_TEST_EQUALS(GetSpecularTexture(modelView1), GetSpecularTexture(modelView2), TEST_LOCATION);
   DALI_TEST_NOT_EQUALS(GetDiffuseTexture(modelView1), GetDiffuseTexture(modelView3), 0.0f, TEST_LOCATION);
   DALI_TEST_NOT_EQUALS(GetSpecularTexture(modelView1), GetSpecularTexture(modelView3), 0.0f, TEST_LOCATION);
 
   view.Add(modelView3);
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
 
   DALI_TEST_EQUALS(GetDiffuseTexture(modelView1), GetDiffuseTexture(modelView3), TEST_LOCATION);
   DALI_TEST_EQUALS(GetSpecularTexture(modelView1), GetSpecularTexture(modelView3), TEST_LOCATION);
 
   view.Remove(modelView1);
-  view.SetImageBasedLightSource(TEST_DIFFUSE_TEXTURE, TEST_SPECULAR_TEXTURE);
+
+  gResourceReadyCalled = false;
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+  view.SetImageBasedLightSource(TEST_DIFFUSE_TEXTURE2, TEST_SPECULAR_TEXTURE2);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
 
   DALI_TEST_NOT_EQUALS(GetDiffuseTexture(modelView1), GetDiffuseTexture(modelView2), 0.0f, TEST_LOCATION);
   DALI_TEST_NOT_EQUALS(GetSpecularTexture(modelView1), GetSpecularTexture(modelView2), 0.0f, TEST_LOCATION);
@@ -470,6 +523,7 @@ int UtcDaliSceneViewImageBasedLight02(void)
   ToolkitTestApplication application;
 
   Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.ResourceReadySignal().Connect(OnResourceReady);
   view.SetProperty(Dali::Actor::Property::SIZE, Vector2(100, 100));
 
   application.GetScene().Add(view);
@@ -478,13 +532,43 @@ int UtcDaliSceneViewImageBasedLight02(void)
   application.Render();
 
   Scene3D::Model modelView1 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  modelView1.ResourceReadySignal().Connect(OnResourceReady);
   view.Add(modelView1);
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
 
+  gResourceReadyCalled = false;
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
   view.SetImageBasedLightSource(TEST_DIFFUSE_TEXTURE, TEST_SPECULAR_TEXTURE);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   Dali::Texture diffuseTexture = GetDiffuseTexture(modelView1);
   Dali::Texture specularTexture = GetSpecularTexture(modelView1);
 
-  modelView1.SetImageBasedLightSource(TEST_SPECULAR_TEXTURE, TEST_DIFFUSE_TEXTURE);
+  gResourceReadyCalled = false;
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+  modelView1.SetImageBasedLightSource(TEST_DIFFUSE_TEXTURE2, TEST_SPECULAR_TEXTURE2);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   DALI_TEST_NOT_EQUALS(GetDiffuseTexture(modelView1), diffuseTexture, 0.0f, TEST_LOCATION);
   DALI_TEST_NOT_EQUALS(GetSpecularTexture(modelView1), specularTexture, 0.0f, TEST_LOCATION);
   diffuseTexture = GetDiffuseTexture(modelView1);
@@ -508,7 +592,7 @@ int UtcDaliSceneViewImageBasedLight03(void)
 
   Scene3D::SceneView view = Scene3D::SceneView::New();
   view.SetProperty(Dali::Actor::Property::SIZE, Vector2(100, 100));
-
+  view.ResourceReadySignal().Connect(OnResourceReady);
   application.GetScene().Add(view);
 
   application.SendNotification();
@@ -516,12 +600,29 @@ int UtcDaliSceneViewImageBasedLight03(void)
 
   Scene3D::Model modelView1 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
   view.Add(modelView1);
-
   modelView1.SetImageBasedLightSource(TEST_SPECULAR_TEXTURE, TEST_DIFFUSE_TEXTURE);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
   Dali::Texture diffuseTexture = GetDiffuseTexture(modelView1);
   Dali::Texture specularTexture = GetSpecularTexture(modelView1);
 
-  view.SetImageBasedLightSource(TEST_DIFFUSE_TEXTURE, TEST_SPECULAR_TEXTURE);
+  gResourceReadyCalled = false;
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+  view.SetImageBasedLightSource(TEST_DIFFUSE_TEXTURE2, TEST_SPECULAR_TEXTURE2);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
   DALI_TEST_EQUALS(GetDiffuseTexture(modelView1), diffuseTexture, TEST_LOCATION);
   DALI_TEST_EQUALS(GetSpecularTexture(modelView1), specularTexture, TEST_LOCATION);
 
@@ -609,22 +710,6 @@ int UtcDaliSceneViewUseFramebuffer02(void)
   END_TEST;
 }
 
-// For ResourceReady
-namespace
-{
-static bool gOnRelayoutCallBackCalled = false;
-void OnRelayoutCallback(Actor actor)
-{
-  gOnRelayoutCallBackCalled = true;
-}
-
-static bool gResourceReadyCalled = false;
-void OnResourceReady(Control control)
-{
-  gResourceReadyCalled = true;
-}
-}
-
 int UtcDaliSceneViewResourceReady(void)
 {
   ToolkitTestApplication application;
@@ -658,6 +743,9 @@ int UtcDaliSceneViewResourceReady(void)
 
   application.SendNotification();
   application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
 
   DALI_TEST_EQUALS(gOnRelayoutCallBackCalled, false, TEST_LOCATION);
   DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
@@ -673,28 +761,110 @@ int UtcDaliSceneViewSetSkybox(void)
   Scene3D::SceneView view = Scene3D::SceneView::New();
   view.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f));
   view.ResourceReadySignal().Connect(OnResourceReady);
-  // SceneView::IsResourceReady() returns true by default.
-  DALI_TEST_EQUALS(view.IsResourceReady(), true, TEST_LOCATION);
+  application.GetScene().Add(view);
 
-  // Sanity check
-  DALI_TEST_CHECK(!gResourceReadyCalled);
+  application.SendNotification();
+  application.Render();
+
+  uint32_t childCount = view.GetChildAt(0u).GetChildCount();
+  view.SetSkybox(TEST_SPECULAR_TEXTURE);
+
+  gResourceReadyCalled = false;
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
 
+  DALI_TEST_EQUALS(view.GetChildAt(0u).GetChildCount(), childCount + 1, TEST_LOCATION);
+
+  view.Unparent();
+  view.Reset();
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewSetSkyboxEquirectangular(void)
+{
+  ToolkitTestApplication application;
+
+  gResourceReadyCalled = false;
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f));
+  view.ResourceReadySignal().Connect(OnResourceReady);
   application.GetScene().Add(view);
 
   application.SendNotification();
   application.Render();
 
-  DALI_TEST_EQUALS(view.IsResourceReady(), true, TEST_LOCATION);
+  uint32_t childCount = view.GetChildAt(0u).GetChildCount();
+  view.SetSkybox(TEST_EQUIRECTANGULAR_TEXTURE, Scene3D::SceneView::SkyboxType::EQUIRECTANGULAR);
+
+  gResourceReadyCalled = false;
   DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
 
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(view.GetChildAt(0u).GetChildCount(), childCount + 1, TEST_LOCATION);
+
+  view.Unparent();
+  view.Reset();
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewSetSkyboxEmpty(void)
+{
+  ToolkitTestApplication application;
+
   gResourceReadyCalled = false;
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f));
+  view.ResourceReadySignal().Connect(OnResourceReady);
+  application.GetScene().Add(view);
 
-  view.SetSkybox(TEST_SPECULAR_TEXTURE);
+  application.SendNotification();
+  application.Render();
+
+  uint32_t childCount = view.GetChildAt(0u).GetChildCount();
+  view.SetSkybox("");
+  DALI_TEST_EQUALS(view.GetChildAt(0u).GetChildCount(), childCount, TEST_LOCATION);
+
+  view.Unparent();
+  view.Reset();
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewSetSkyboxEquirectangularEmpty(void)
+{
+  ToolkitTestApplication application;
+
+  gResourceReadyCalled = false;
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f));
+  view.ResourceReadySignal().Connect(OnResourceReady);
+  application.GetScene().Add(view);
 
   application.SendNotification();
   application.Render();
 
-  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+  uint32_t childCount = view.GetChildAt(0u).GetChildCount();
+  view.SetSkybox("", Scene3D::SceneView::SkyboxType::EQUIRECTANGULAR);
+  DALI_TEST_EQUALS(view.GetChildAt(0u).GetChildCount(), childCount, TEST_LOCATION);
+
+  view.Unparent();
+  view.Reset();
 
   END_TEST;
 }
@@ -729,6 +899,30 @@ int UtcDaliSceneViewSetSkyboxOrientation(void)
   END_TEST;
 }
 
+int UtcDaliSceneViewSetImageBasedLightAndSkybox(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.ResourceReadySignal().Connect(OnResourceReady);
+  view.SetProperty(Dali::Actor::Property::SIZE, Vector2(100, 100));
+  view.SetImageBasedLightSource(TEST_DIFFUSE_TEXTURE, TEST_SPECULAR_TEXTURE);
+  view.SetSkybox(TEST_SPECULAR_TEXTURE);
+  application.GetScene().Add(view);
+
+  // Check SceneView needs 3 trigger to load both of image based light and skybox.
+  gResourceReadyCalled = false;
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcDaliSceneViewCreateAndRemoveRenderTask(void)
 {
   ToolkitTestApplication application;
diff --git a/dali-scene3d/internal/common/environment-map-load-task.cpp b/dali-scene3d/internal/common/environment-map-load-task.cpp
new file mode 100644 (file)
index 0000000..82d8646
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-scene3d/internal/common/environment-map-load-task.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/loader/cube-map-loader.h>
+
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+
+EnvironmentMapLoadTask::EnvironmentMapLoadTask(const std::string& environmentMapUrl, CallbackBase* callback)
+: AsyncTask(callback),
+  mEnvironmentMapUrl(environmentMapUrl),
+  mIsReady(true),
+  mHasSucceeded(false)
+{
+}
+
+EnvironmentMapLoadTask::~EnvironmentMapLoadTask()
+{
+}
+
+void EnvironmentMapLoadTask::Process()
+{
+  mHasSucceeded = Scene3D::Loader::LoadCubeMapData(mEnvironmentMapUrl, mEnvironmentMapPixelData);
+}
+
+bool EnvironmentMapLoadTask::IsReady()
+{
+  return mIsReady;
+}
+
+bool EnvironmentMapLoadTask::HasSucceeded() const
+{
+  return mHasSucceeded;
+}
+
+Dali::Scene3D::Loader::CubeData EnvironmentMapLoadTask::GetEnvironmentMap() const
+{
+  Dali::Scene3D::Loader::CubeData environmentMapPixelData;
+  if(mIsReady && mHasSucceeded && !mEnvironmentMapPixelData.data.empty())
+  {
+    environmentMapPixelData = mEnvironmentMapPixelData;
+  }
+  return environmentMapPixelData;
+}
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
diff --git a/dali-scene3d/internal/common/environment-map-load-task.h b/dali-scene3d/internal/common/environment-map-load-task.h
new file mode 100644 (file)
index 0000000..8b1ac7c
--- /dev/null
@@ -0,0 +1,100 @@
+#ifndef DALI_SCENE3D_ENVIRONMENT_MAP_LOAD_TASK_H
+#define DALI_SCENE3D_ENVIRONMENT_MAP_LOAD_TASK_H
+
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/images/pixel-data.h>
+#include <dali/public-api/adaptor-framework/async-task-manager.h>
+#include <memory>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/loader/load-result.h>
+#include <dali-scene3d/public-api/loader/scene-definition.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+class EnvironmentMapLoadTask;
+typedef IntrusivePtr<EnvironmentMapLoadTask> EnvironmentMapLoadTaskPtr;
+
+class EnvironmentMapLoadTask : public AsyncTask
+{
+public:
+  /**
+   * Constructor
+   * @param[in] environmentMapUrl The url of the environment map image file.
+   * @param[in] callback The callback that is called when the operation is completed.
+   */
+  EnvironmentMapLoadTask(const std::string& environmentMapUrl, CallbackBase* callback);
+
+  /**
+   * Destructor.
+   */
+  ~EnvironmentMapLoadTask();
+
+  /**
+   * Process the task
+   */
+  void Process() override;
+
+  /**
+   * Whether the task is ready to process.
+   * @return True if the task is ready to process.
+   */
+  bool IsReady() override;
+
+  /**
+   * Whether the task has succeeded.
+   * @return True if the task has succeeded.
+   */
+  bool HasSucceeded() const;
+
+  /**
+   * Retrieves loaded Environment Map
+   * @return CubeData loaded from url.
+   *
+   * TODO: Supports Equirectangular environment map
+   */
+  Dali::Scene3D::Loader::CubeData GetEnvironmentMap() const;
+
+private:
+  // Undefined
+  EnvironmentMapLoadTask(const EnvironmentMapLoadTask& task) = delete;
+
+  // Undefined
+  EnvironmentMapLoadTask& operator=(const EnvironmentMapLoadTask& task) = delete;
+
+private:
+  std::string                     mEnvironmentMapUrl;
+  Dali::Scene3D::Loader::CubeData mEnvironmentMapPixelData;
+
+  bool mIsReady;
+  bool mHasSucceeded;
+};
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_ENVIRONMENT_MAP_LOAD_TASK_H
diff --git a/dali-scene3d/internal/common/model-load-task.cpp b/dali-scene3d/internal/common/model-load-task.cpp
new file mode 100644 (file)
index 0000000..bfd9256
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-scene3d/internal/common/model-load-task.h>
+
+// EXTERNAL INCLUDES
+#include <filesystem>
+#include <dali/integration-api/debug.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/loader/animation-definition.h>
+#include <dali-scene3d/public-api/loader/camera-parameters.h>
+#include <dali-scene3d/public-api/loader/cube-map-loader.h>
+#include <dali-scene3d/public-api/loader/dli-loader.h>
+#include <dali-scene3d/public-api/loader/gltf2-loader.h>
+#include <dali-scene3d/public-api/loader/light-parameters.h>
+#include <dali-scene3d/public-api/loader/node-definition.h>
+#include <dali-scene3d/public-api/loader/shader-definition-factory.h>
+
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+namespace
+{
+static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
+
+static constexpr std::string_view OBJ_EXTENSION      = ".obj";
+static constexpr std::string_view GLTF_EXTENSION     = ".gltf";
+static constexpr std::string_view DLI_EXTENSION      = ".dli";
+static constexpr std::string_view METADATA_EXTENSION = "metadata";
+} // namespace
+
+ModelLoadTask::ModelLoadTask(const std::string& modelUrl, const std::string& resourceDirectoryUrl, CallbackBase* callback)
+: AsyncTask(callback),
+  mModelUrl(modelUrl),
+  mResourceDirectoryUrl(resourceDirectoryUrl),
+  mHasSucceeded(false)
+{
+}
+
+ModelLoadTask::~ModelLoadTask()
+{
+}
+
+void ModelLoadTask::Process()
+{
+  std::filesystem::path modelUrl(mModelUrl);
+  if(mResourceDirectoryUrl.empty())
+  {
+    mResourceDirectoryUrl = std::string(modelUrl.parent_path()) + "/";
+  }
+  std::string extension = modelUrl.extension();
+  std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+
+  Dali::Scene3D::Loader::ResourceBundle::PathProvider pathProvider = [&](Dali::Scene3D::Loader::ResourceType::Value type)
+  {
+    return mResourceDirectoryUrl;
+  };
+  mAnimations.clear();
+
+  std::filesystem::path metaDataUrl = modelUrl;
+  metaDataUrl.replace_extension(METADATA_EXTENSION.data());
+
+  Dali::Scene3D::Loader::LoadSceneMetadata(metaDataUrl.c_str(), mMetaData);
+
+  Dali::Scene3D::Loader::LoadResult result{mResources, mScene, mMetaData, mAnimations, mAnimGroups, mCameraParameters, mLights};
+
+  if(extension == DLI_EXTENSION)
+  {
+    Dali::Scene3D::Loader::DliLoader              loader;
+    Dali::Scene3D::Loader::DliLoader::InputParams input{
+      pathProvider(Dali::Scene3D::Loader::ResourceType::Mesh),
+      nullptr,
+      {},
+      {},
+      nullptr,
+      {}};
+    Dali::Scene3D::Loader::DliLoader::LoadParams loadParams{input, result};
+    if(!loader.LoadScene(mModelUrl, loadParams))
+    {
+      DALI_LOG_ERROR("Failed to load scene from '%s': %s\n", mModelUrl.c_str(), loader.GetParseError().c_str());
+      return;
+    }
+  }
+  else if(extension == GLTF_EXTENSION)
+  {
+    Dali::Scene3D::Loader::ShaderDefinitionFactory sdf;
+    sdf.SetResources(mResources);
+    Dali::Scene3D::Loader::LoadGltfScene(mModelUrl, sdf, result);
+  }
+  else
+  {
+    DALI_LOG_ERROR("Unsupported model type.\n");
+    return;
+  }
+
+  for(auto iRoot : mScene.GetRoots())
+  {
+    mResourceRefCounts.push_back(mResources.CreateRefCounter());
+    mScene.CountResourceRefs(iRoot, mResourceChoices, mResourceRefCounts.back());
+    mResources.CountEnvironmentReferences(mResourceRefCounts.back());
+
+    mResources.LoadRawResources(mResourceRefCounts.back(), pathProvider);
+
+    // glTF Mesh is defined in right hand coordinate system, with positive Y for Up direction.
+    // Because DALi uses left hand system, Y direciton will be flipped for environment map sampling.
+    for(auto&& env : mResources.mEnvironmentMaps)
+    {
+      env.first.mYDirection = Y_DIRECTION;
+    }
+  }
+  mHasSucceeded = true;
+}
+
+bool ModelLoadTask::IsReady()
+{
+  return true;
+}
+
+bool ModelLoadTask::HasSucceeded() const
+{
+  return mHasSucceeded;
+}
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
diff --git a/dali-scene3d/internal/common/model-load-task.h b/dali-scene3d/internal/common/model-load-task.h
new file mode 100644 (file)
index 0000000..0e68e06
--- /dev/null
@@ -0,0 +1,103 @@
+#ifndef DALI_SCENE3D_MODEL_LOAD_TASK_H
+#define DALI_SCENE3D_MODEL_LOAD_TASK_H
+
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <dali/public-api/images/pixel-data.h>
+#include <memory>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/loader/load-result.h>
+#include <dali-scene3d/public-api/loader/scene-definition.h>
+#include <dali/public-api/adaptor-framework/async-task-manager.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+class ModelLoadTask;
+typedef IntrusivePtr<ModelLoadTask> ModelLoadTaskPtr;
+
+class ModelLoadTask : public AsyncTask
+{
+public:
+  /**
+   * Constructor
+   * @param[in] modelUrl model file path.(e.g., glTF, and DLI).
+   * @param[in] resourceDirectoryUrl resource file path that includes binary, image etc.
+   * @param[in] callback The callback that is called when the operation is completed.
+   */
+  ModelLoadTask(const std::string& modelUrl, const std::string& resourceDirectoryUrl, CallbackBase* callback);
+
+  /**
+   * Destructor.
+   */
+  ~ModelLoadTask();
+
+  /**
+   * Process the task
+   */
+  void Process() override;
+
+  /**
+   * Whether the task is ready to process.
+   * @return True if the task is ready to process.
+   */
+  bool IsReady() override;
+
+  /**
+   * Whether the task has succeeded.
+   * @return True if the task has succeeded.
+   */
+  bool HasSucceeded() const;
+
+private:
+  // Undefined
+  ModelLoadTask(const ModelLoadTask& task) = delete;
+
+  // Undefined
+  ModelLoadTask& operator=(const ModelLoadTask& task) = delete;
+
+public:
+  std::string mModelUrl;
+  std::string mResourceDirectoryUrl;
+
+  Dali::Scene3D::Loader::ResourceBundle                        mResources;
+  Dali::Scene3D::Loader::SceneDefinition                       mScene;
+  Dali::Scene3D::Loader::SceneMetadata                         mMetaData;
+  std::vector<Dali::Scene3D::Loader::AnimationGroupDefinition> mAnimGroups;
+  std::vector<Dali::Scene3D::Loader::CameraParameters>         mCameraParameters;
+  std::vector<Dali::Scene3D::Loader::LightParameters>          mLights;
+  std::vector<Dali::Scene3D::Loader::AnimationDefinition>      mAnimations;
+
+  Dali::Scene3D::Loader::Customization::Choices         mResourceChoices;
+  std::vector<Dali::Scene3D::Loader::ResourceRefCounts> mResourceRefCounts;
+  bool                                                  mHasSucceeded;
+};
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_MODEL_LOAD_TASK_H
index bed5f34..d936271 100644 (file)
@@ -27,6 +27,7 @@
 #include <dali/public-api/math/math-utils.h>
 #include <dali/public-api/object/type-registry-helper.h>
 #include <dali/public-api/object/type-registry.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <filesystem>
 
 // INTERNAL INCLUDES
@@ -70,11 +71,6 @@ static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
 static constexpr bool DEFAULT_MODEL_CHILDREN_SENSITIVE = false;
 static constexpr bool DEFAULT_MODEL_CHILDREN_FOCUSABLE = false;
 
-static constexpr std::string_view KTX_EXTENSION  = ".ktx";
-static constexpr std::string_view OBJ_EXTENSION  = ".obj";
-static constexpr std::string_view GLTF_EXTENSION = ".gltf";
-static constexpr std::string_view DLI_EXTENSION  = ".dli";
-
 struct BoundingVolume
 {
   void Init()
@@ -188,12 +184,16 @@ Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUr
   mModelChildrenSensitive(DEFAULT_MODEL_CHILDREN_SENSITIVE),
   mModelChildrenFocusable(DEFAULT_MODEL_CHILDREN_FOCUSABLE),
   mModelResourceReady(false),
-  mIBLResourceReady(true)
+  mIblDiffuseResourceReady(true),
+  mIblSpecularResourceReady(true),
+  mIblDiffuseDirty(false),
+  mIblSpecularDirty(false)
 {
 }
 
 Model::~Model()
 {
+  ResetResourceTasks();
 }
 
 Dali::Scene3D::Model Model::New(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
@@ -251,17 +251,98 @@ bool Model::GetChildrenFocusable() const
 
 void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::string& specularUrl, float scaleFactor)
 {
-  mIBLResourceReady       = false;
-  Texture diffuseTexture  = (!diffuseUrl.empty()) ? Dali::Scene3D::Loader::LoadCubeMap(diffuseUrl) : Texture();
-  Texture specularTexture = (!specularUrl.empty()) ? Dali::Scene3D::Loader::LoadCubeMap(specularUrl) : Texture();
-  SetImageBasedLightTexture(diffuseTexture, specularTexture, scaleFactor);
-  mIBLResourceReady = true;
+  bool needIblReset = false;
+  bool isOnScene = Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE);
+  if(mDiffuseIblUrl != diffuseUrl)
+  {
+    mDiffuseIblUrl = diffuseUrl;
+    if(mDiffuseIblUrl.empty())
+    {
+      needIblReset             = true;
+    }
+    else
+    {
+      mIblDiffuseDirty         = true;
+      mIblDiffuseResourceReady = false;
+    }
+  }
+
+  if(mSpecularIblUrl != specularUrl)
+  {
+    mSpecularIblUrl = specularUrl;
+    if(mSpecularIblUrl.empty())
+    {
+      needIblReset              = true;
+    }
+    else
+    {
+      mIblSpecularDirty         = true;
+      mIblSpecularResourceReady = false;
+    }
+  }
+
+  // If one or both of diffuse url and specular url are empty,
+  // we don't need to request to load texture.
+  if(needIblReset)
+  {
+    if(mIblDiffuseLoadTask)
+    {
+      Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
+      mIblDiffuseLoadTask.Reset();
+    }
+
+    if(mIblSpecularLoadTask)
+    {
+      Dali::AsyncTaskManager::Get().RemoveTask(mIblSpecularLoadTask);
+      mIblSpecularLoadTask.Reset();
+    }
+
+    mIblDiffuseDirty          = false;
+    mIblSpecularDirty         = false;
+    mIblDiffuseResourceReady  = true;
+    mIblSpecularResourceReady = true;
+
+    mDiffuseTexture.Reset();
+    mSpecularTexture.Reset();
+    UpdateImageBasedLightTexture();
+  }
+  else
+  {
+    if(isOnScene && mIblDiffuseDirty)
+    {
+      if(mIblDiffuseLoadTask)
+      {
+        Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
+        mIblDiffuseLoadTask.Reset();
+      }
+      mIblDiffuseLoadTask = new EnvironmentMapLoadTask(mDiffuseIblUrl, MakeCallback(this, &Model::OnIblDiffuseLoadComplete));
+      Dali::AsyncTaskManager::Get().AddTask(mIblDiffuseLoadTask);
+      mIblDiffuseDirty = false;
+    }
+
+    if(isOnScene && mIblSpecularDirty)
+    {
+      if(mIblSpecularLoadTask)
+      {
+        Dali::AsyncTaskManager::Get().RemoveTask(mIblSpecularLoadTask);
+        mIblSpecularLoadTask.Reset();
+      }
+      mIblSpecularLoadTask = new EnvironmentMapLoadTask(mSpecularIblUrl, MakeCallback(this, &Model::OnIblSpecularLoadComplete));
+      Dali::AsyncTaskManager::Get().AddTask(mIblSpecularLoadTask);
+      mIblSpecularDirty = false;
+    }
+  }
+
+  if(!Dali::Equals(mIblScaleFactor, scaleFactor))
+  {
+    mIblScaleFactor = scaleFactor;
+    UpdateImageBasedLightScaleFactor();
+  }
 
-  // If Model resource is already ready, then set resource ready.
-  // If Model resource is still not ready, wait for model resource ready.
+  // If diffuse and specular textures are already loaded, emits resource ready signal here.
   if(IsResourceReady())
   {
-    SetResourceReady(false);
+    Control::SetResourceReady(false);
   }
 }
 
@@ -336,9 +417,16 @@ void Model::OnInitialize()
 
 void Model::OnSceneConnection(int depth)
 {
-  if(!mModelRoot)
+  if(!mModelLoadTask && !mModelRoot)
+  {
+    Scene3D::Loader::InitializeGltfLoader();
+    mModelLoadTask = new ModelLoadTask(mModelUrl, mResourceDirectoryUrl, MakeCallback(this, &Model::OnModelLoadComplete));
+    Dali::AsyncTaskManager::Get().AddTask(mModelLoadTask);
+  }
+  // If diffuse and specular url is not valid, IBL does not need to be loaded.
+  if(!mDiffuseIblUrl.empty() && !mSpecularIblUrl.empty())
   {
-    LoadModel();
+    SetImageBasedLightSource(mDiffuseIblUrl, mSpecularIblUrl, mIblScaleFactor);
   }
 
   Actor parent = Self().GetParent();
@@ -372,7 +460,8 @@ Vector3 Model::GetNaturalSize()
 {
   if(!mModelRoot)
   {
-    LoadModel();
+    DALI_LOG_ERROR("Model is still not loaded.\n");
+    return Vector3::ZERO;
   }
 
   return mNaturalSize;
@@ -400,121 +489,187 @@ void Model::OnRelayout(const Vector2& size, RelayoutContainer& container)
 
 bool Model::IsResourceReady() const
 {
-  return mModelResourceReady && mIBLResourceReady;
+  return mModelResourceReady && mIblDiffuseResourceReady && mIblSpecularResourceReady;
 }
 
-void Model::LoadModel()
+void Model::ScaleModel()
 {
-  std::filesystem::path modelUrl(mModelUrl);
-  if(mResourceDirectoryUrl.empty())
+  if(mModelRoot)
   {
-    mResourceDirectoryUrl = std::string(modelUrl.parent_path()) + "/";
+    float   scale = 1.0f;
+    Vector3 size  = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
+    if(size.x > 0.0f && size.y > 0.0f)
+    {
+      scale = MAXFLOAT;
+      scale = std::min(size.x / mNaturalSize.x, scale);
+      scale = std::min(size.y / mNaturalSize.y, scale);
+    }
+    // Models in glTF and dli are defined as right hand coordinate system.
+    // DALi uses left hand coordinate system. Scaling negative is for change winding order.
+    mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scale);
   }
-  std::string extension = modelUrl.extension();
-  std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+}
 
-  Dali::Scene3D::Loader::ResourceBundle::PathProvider pathProvider = [&](Dali::Scene3D::Loader::ResourceType::Value type) {
-    return mResourceDirectoryUrl;
-  };
+void Model::FitModelPosition()
+{
+  if(mModelRoot)
+  {
+    // Loaded model pivot is not the model center.
+    mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+    mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
+  }
+}
 
-  Dali::Scene3D::Loader::ResourceBundle                        resources;
-  Dali::Scene3D::Loader::SceneDefinition                       scene;
-  std::vector<Dali::Scene3D::Loader::AnimationGroupDefinition> animGroups;
-  std::vector<Dali::Scene3D::Loader::CameraParameters>         cameraParameters;
-  std::vector<Dali::Scene3D::Loader::LightParameters>          lights;
+void Model::CollectRenderableActor(Actor actor)
+{
+  uint32_t rendererCount = actor.GetRendererCount();
+  if(rendererCount)
+  {
+    mRenderableActors.push_back(actor);
+  }
 
-  std::vector<Dali::Scene3D::Loader::AnimationDefinition> animations;
-  animations.clear();
+  uint32_t childrenCount = actor.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
+  {
+    CollectRenderableActor(actor.GetChildAt(i));
+  }
+}
 
-  Dali::Scene3D::Loader::SceneMetadata metaData;
+void Model::UpdateImageBasedLightTexture()
+{
+  Dali::Texture currentDiffuseTexture  = (mDiffuseTexture && mSpecularTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
+  Dali::Texture currentSpecularTexture = (mDiffuseTexture && mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture;
+  float         currentIblScaleFactor  = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
 
-  std::filesystem::path metaDataUrl = modelUrl;
-  metaDataUrl.replace_extension("metadata");
+  if(!currentDiffuseTexture || !currentSpecularTexture)
+  {
+    currentDiffuseTexture  = mDefaultDiffuseTexture;
+    currentSpecularTexture = mDefaultSpecularTexture;
+    currentIblScaleFactor  = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
+  }
 
-  Dali::Scene3D::Loader::LoadSceneMetadata(metaDataUrl.c_str(), metaData);
+  for(auto&& actor : mRenderableActors)
+  {
+    Actor renderableActor = actor.GetHandle();
+    if(renderableActor)
+    {
+      uint32_t rendererCount = renderableActor.GetRendererCount();
+      for(uint32_t i = 0; i < rendererCount; ++i)
+      {
+        Dali::Renderer renderer = renderableActor.GetRendererAt(i);
+        if(renderer)
+        {
+          Dali::TextureSet textures = renderer.GetTextures();
+          if(textures)
+          {
+            uint32_t textureCount = textures.GetTextureCount();
+            // EnvMap requires at least 2 texture, diffuse and specular
+            if(textureCount > 2u)
+            {
+              textures.SetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE, currentDiffuseTexture);
+              textures.SetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE, currentSpecularTexture);
+            }
+          }
+        }
+      }
+      renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIblScaleFactor);
+    }
+  }
+}
 
-  Dali::Scene3D::Loader::LoadResult output{resources, scene, metaData, animations, animGroups, cameraParameters, lights};
+void Model::UpdateImageBasedLightScaleFactor()
+{
+  if((!mDiffuseTexture || !mSpecularTexture) &&
+     (!mSceneDiffuseTexture || !mSceneSpecularTexture))
+  {
+    return;
+  }
 
-  if(extension == DLI_EXTENSION)
+  float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
+  for(auto&& actor : mRenderableActors)
   {
-    Dali::Scene3D::Loader::DliLoader              loader;
-    Dali::Scene3D::Loader::DliLoader::InputParams input{
-      pathProvider(Dali::Scene3D::Loader::ResourceType::Mesh),
-      nullptr,
-      {},
-      {},
-      nullptr,
-      {}};
-    Dali::Scene3D::Loader::DliLoader::LoadParams loadParams{input, output};
-    if(!loader.LoadScene(mModelUrl, loadParams))
+    Actor renderableActor = actor.GetHandle();
+    if(renderableActor)
     {
-      Dali::Scene3D::Loader::ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << mModelUrl << "': " << loader.GetParseError();
+      renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIblScaleFactor);
     }
   }
-  else if(extension == GLTF_EXTENSION)
-  {
-    Dali::Scene3D::Loader::ShaderDefinitionFactory sdf;
-    sdf.SetResources(resources);
-    Dali::Scene3D::Loader::LoadGltfScene(mModelUrl, sdf, output);
+}
 
-    resources.mEnvironmentMaps.push_back({});
+void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
+{
+  if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
+  {
+    mSceneDiffuseTexture  = diffuseTexture;
+    mSceneSpecularTexture = specularTexture;
+    mSceneIblScaleFactor  = scaleFactor;
+    // If Model IBL is not set, use SceneView's IBL.
+    if(!mDiffuseTexture || !mSpecularTexture)
+    {
+      UpdateImageBasedLightTexture();
+    }
   }
-  else
+}
+
+void Model::NotifyImageBasedLightScaleFactor(float scaleFactor)
+{
+  mSceneIblScaleFactor = scaleFactor;
+  if(mSceneDiffuseTexture && mSceneSpecularTexture)
   {
-    DALI_LOG_ERROR("Unsupported model type.\n");
+    UpdateImageBasedLightScaleFactor();
   }
+}
 
-  Dali::Scene3D::Loader::Transforms                   xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
-  Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, {}, {}, {}};
-  Dali::Scene3D::Loader::Customization::Choices       choices;
+void Model::OnModelLoadComplete()
+{
+  if(!mModelLoadTask->HasSucceeded())
+  {
+    ResetResourceTasks();
+    return;
+  }
 
   mModelRoot = Actor::New();
   mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
 
-  BoundingVolume AABB;
-  for(auto iRoot : scene.GetRoots())
+  BoundingVolume                                      AABB;
+  auto*                                               resources = &(mModelLoadTask->mResources);
+  auto*                                               scene     = &(mModelLoadTask->mScene);
+  Dali::Scene3D::Loader::Transforms                   xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
+  Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{*resources, xforms, {}, {}, {}};
+  uint32_t rootCount = 0u;
+  for(auto iRoot : scene->GetRoots())
   {
-    auto resourceRefs = resources.CreateRefCounter();
-    scene.CountResourceRefs(iRoot, choices, resourceRefs);
-    resources.CountEnvironmentReferences(resourceRefs);
-
-    resources.LoadResources(resourceRefs, pathProvider);
+    resources->GenerateResources(mModelLoadTask->mResourceRefCounts[rootCount]);
 
-    // glTF Mesh is defined in right hand coordinate system, with positive Y for Up direction.
-    // Because DALi uses left hand system, Y direciton will be flipped for environment map sampling.
-    for(auto&& env : resources.mEnvironmentMaps)
+    if(auto actor = scene->CreateNodes(iRoot, mModelLoadTask->mResourceChoices, nodeParams))
     {
-      env.first.mYDirection = Y_DIRECTION;
-    }
-
-    if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
-    {
-      scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
-      scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
-      ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
+      scene->ConfigureSkeletonJoints(iRoot, resources->mSkeletons, actor);
+      scene->ConfigureSkinningShaders(*resources, actor, std::move(nodeParams.mSkinnables));
+      ConfigureBlendShapeShaders(*resources, *scene, actor, std::move(nodeParams.mBlendshapeRequests));
 
-      scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
+      scene->ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
 
       mModelRoot.Add(actor);
     }
 
-    AddModelTreeToAABB(AABB, scene, choices, iRoot, nodeParams, Matrix::IDENTITY);
+    AddModelTreeToAABB(AABB, *scene, mModelLoadTask->mResourceChoices, iRoot, nodeParams, Matrix::IDENTITY);
+    rootCount++;
   }
 
-  if(!resources.mEnvironmentMaps.empty())
+  if(!resources->mEnvironmentMaps.empty())
   {
-    mDefaultDiffuseTexture  = resources.mEnvironmentMaps.front().second.mDiffuse;
-    mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular;
+    mDefaultDiffuseTexture  = resources->mEnvironmentMaps.front().second.mDiffuse;
+    mDefaultSpecularTexture = resources->mEnvironmentMaps.front().second.mSpecular;
   }
 
-  if(!animations.empty())
+  if(!mModelLoadTask->mAnimations.empty())
   {
     auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property)
     {
       Dali::Actor actor;
       if(property.mNodeIndex != Scene3D::Loader::INVALID_INDEX)
       {
-        auto* node = scene.GetNode(property.mNodeIndex);
+        auto* node = scene->GetNode(property.mNodeIndex);
         if(node != nullptr)
         {
           actor = mModelRoot.FindChildById(node->mNodeId);
@@ -528,7 +683,7 @@ void Model::LoadModel()
     };
 
     mAnimations.clear();
-    for(auto&& animation : animations)
+    for(auto&& animation : mModelLoadTask->mAnimations)
     {
       Dali::Animation anim = animation.ReAnimate(getActor);
 
@@ -538,6 +693,7 @@ void Model::LoadModel()
 
   mRenderableActors.clear();
   CollectRenderableActor(mModelRoot);
+
   UpdateImageBasedLightTexture();
   UpdateImageBasedLightScaleFactor();
 
@@ -563,129 +719,64 @@ void Model::LoadModel()
 
   mModelResourceReady = true;
 
-  Control::SetResourceReady(false);
-}
-
-void Model::ScaleModel()
-{
-  if(mModelRoot)
+  if(IsResourceReady())
   {
-    float   scale = 1.0f;
-    Vector3 size  = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
-    if(size.x > 0.0f && size.y > 0.0f)
-    {
-      scale = MAXFLOAT;
-      scale = std::min(size.x / mNaturalSize.x, scale);
-      scale = std::min(size.y / mNaturalSize.y, scale);
-    }
-    // Models in glTF and dli are defined as right hand coordinate system.
-    // DALi uses left hand coordinate system. Scaling negative is for change winding order.
-    mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scale);
+    Control::SetResourceReady(false);
   }
+  mModelLoadTask.Reset();
 }
 
-void Model::FitModelPosition()
+void Model::OnIblDiffuseLoadComplete()
 {
-  if(mModelRoot)
+  mDiffuseTexture = (mIblDiffuseLoadTask->HasSucceeded()) ? mIblDiffuseLoadTask->GetEnvironmentMap().CreateTexture() : Texture();
+  mIblDiffuseResourceReady = true;
+  if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
   {
-    // Loaded model pivot is not the model center.
-    mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
-    mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
+    OnIblLoadComplete();
   }
+  mIblDiffuseLoadTask.Reset();
 }
 
-void Model::CollectRenderableActor(Actor actor)
+void Model::OnIblSpecularLoadComplete()
 {
-  uint32_t rendererCount = actor.GetRendererCount();
-  if(rendererCount)
+  mSpecularTexture = (mIblSpecularLoadTask->HasSucceeded()) ? mIblSpecularLoadTask->GetEnvironmentMap().CreateTexture() : Texture();
+  mIblSpecularResourceReady = true;
+  if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
   {
-    mRenderableActors.push_back(actor);
-  }
-
-  uint32_t childrenCount = actor.GetChildCount();
-  for(uint32_t i = 0; i < childrenCount; ++i)
-  {
-    CollectRenderableActor(actor.GetChildAt(i));
+    OnIblLoadComplete();
   }
+  mIblSpecularLoadTask.Reset();
 }
 
-void Model::UpdateImageBasedLightTexture()
+void Model::OnIblLoadComplete()
 {
-  Dali::Texture currentDiffuseTexture  = (mDiffuseTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
-  Dali::Texture currentSpecularTexture = (mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture;
-  float         currentIBLScaleFactor  = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
-  if(!currentDiffuseTexture || !currentSpecularTexture)
-  {
-    currentDiffuseTexture  = mDefaultDiffuseTexture;
-    currentSpecularTexture = mDefaultSpecularTexture;
-    currentIBLScaleFactor  = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
-  }
+  UpdateImageBasedLightTexture();
 
-  for(auto&& actor : mRenderableActors)
+  if(IsResourceReady())
   {
-    Actor renderableActor = actor.GetHandle();
-    if(renderableActor)
-    {
-      uint32_t rendererCount = renderableActor.GetRendererCount();
-      for(uint32_t i = 0; i < rendererCount; ++i)
-      {
-        Dali::Renderer renderer = renderableActor.GetRendererAt(i);
-        if(renderer)
-        {
-          Dali::TextureSet textures = renderer.GetTextures();
-          if(textures)
-          {
-            uint32_t textureCount = textures.GetTextureCount();
-            // EnvMap requires at least 2 texture, diffuse and specular
-            if(textureCount > 2u)
-            {
-              textures.SetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE, currentDiffuseTexture);
-              textures.SetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE, currentSpecularTexture);
-            }
-          }
-        }
-      }
-      renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor);
-    }
+    Control::SetResourceReady(false);
   }
 }
 
-void Model::UpdateImageBasedLightScaleFactor()
+void Model::ResetResourceTasks()
 {
-  if((!mDiffuseTexture || !mSpecularTexture) &&
-     (!mSceneDiffuseTexture || !mSceneSpecularTexture))
+  if(Dali::Adaptor::IsAvailable())
   {
-    return;
-  }
-
-  float currentIBLScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
-  for(auto&& actor : mRenderableActors)
-  {
-    Actor renderableActor = actor.GetHandle();
-    if(renderableActor)
+    if(mModelLoadTask)
     {
-      renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor);
+      Dali::AsyncTaskManager::Get().RemoveTask(mModelLoadTask);
+      mModelLoadTask.Reset();
+    }
+    if(mIblDiffuseLoadTask)
+    {
+      Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
+      mIblDiffuseLoadTask.Reset();
+    }
+    if(mIblSpecularLoadTask)
+    {
+      Dali::AsyncTaskManager::Get().RemoveTask(mIblSpecularLoadTask);
+      mIblSpecularLoadTask.Reset();
     }
-  }
-}
-
-void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
-{
-  if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
-  {
-    mSceneDiffuseTexture  = diffuseTexture;
-    mSceneSpecularTexture = specularTexture;
-    mSceneIblScaleFactor  = scaleFactor;
-    UpdateImageBasedLightTexture();
-  }
-}
-
-void Model::NotifyImageBasedLightScaleFactor(float scaleFactor)
-{
-  mSceneIblScaleFactor = scaleFactor;
-  if(mSceneDiffuseTexture && mSceneSpecularTexture)
-  {
-    UpdateImageBasedLightScaleFactor();
   }
 }
 
index 7bb4889..e4f1f08 100644 (file)
@@ -26,7 +26,9 @@
 #include <dali/public-api/rendering/texture.h>
 
 // INTERNAL INCLUDES
+#include <dali-scene3d/internal/common/environment-map-load-task.h>
 #include <dali-scene3d/internal/common/image-based-light-observer.h>
+#include <dali-scene3d/internal/common/model-load-task.h>
 #include <dali-scene3d/public-api/controls/model/model.h>
 #include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
 
@@ -166,11 +168,6 @@ private:
   bool IsResourceReady() const override;
 
   /**
-   * @brief Loads a model from file
-   */
-  void LoadModel();
-
-  /**
    * @brief Scales the model to fit the control or to return to original size.
    */
   void ScaleModel();
@@ -207,6 +204,32 @@ public: // Overrides ImageBasedLightObserver Methods.
   void NotifyImageBasedLightScaleFactor(float scaleFactor) override;
 
 private:
+  /**
+   * @brief Asynchronously model loading finished.
+   */
+  void OnModelLoadComplete();
+
+  /**
+   * @brief Asynchronously ibl diffusel image loading finished.
+   */
+  void OnIblDiffuseLoadComplete();
+
+  /**
+   * @brief Asynchronously ibl specular image loading finished.
+   */
+  void OnIblSpecularLoadComplete();
+
+  /**
+   * @brief Asynchronously ibl loading finished.
+   */
+  void OnIblLoadComplete();
+
+  /**
+   * @brief Reset Resource loading tasks.
+   */
+  void ResetResourceTasks();
+
+private:
   std::string                    mModelUrl;
   std::string                    mResourceDirectoryUrl;
   Dali::Actor                    mModelRoot;
@@ -214,6 +237,14 @@ private:
   std::vector<WeakHandle<Actor>> mRenderableActors;
   WeakHandle<Scene3D::SceneView> mParentSceneView;
 
+  // Asynchronous loading variable
+  ModelLoadTaskPtr          mModelLoadTask;
+  EnvironmentMapLoadTaskPtr mIblDiffuseLoadTask;
+  EnvironmentMapLoadTaskPtr mIblSpecularLoadTask;
+
+  std::string mDiffuseIblUrl;
+  std::string mSpecularIblUrl;
+
   // TODO: This default texture can be removed after 3D Resource Cache is added.
   Dali::Texture mDefaultSpecularTexture;
   Dali::Texture mDefaultDiffuseTexture;
@@ -228,7 +259,10 @@ private:
   bool          mModelChildrenSensitive;
   bool          mModelChildrenFocusable;
   bool          mModelResourceReady;
-  bool          mIBLResourceReady;
+  bool          mIblDiffuseResourceReady;
+  bool          mIblSpecularResourceReady;
+  bool          mIblDiffuseDirty;
+  bool          mIblSpecularDirty;
 };
 
 } // namespace Internal
index 28d0933..bb44ab7 100644 (file)
@@ -25,7 +25,6 @@
 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
 #include <dali-toolkit/public-api/image-loader/image-url.h>
 #include <dali-toolkit/public-api/image-loader/image.h>
-#include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
 #include <dali/devel-api/actors/camera-actor-devel.h>
 #include <dali/devel-api/adaptor-framework/window-devel.h>
 #include <dali/devel-api/common/stage.h>
@@ -34,6 +33,7 @@
 #include <dali/public-api/math/math-utils.h>
 #include <dali/public-api/object/type-registry-helper.h>
 #include <dali/public-api/object/type-registry.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <string_view>
 
 // INTERNAL INCLUDES
@@ -67,7 +67,7 @@ constexpr uint8_t DEFAULT_FRAME_BUFFER_MULTI_SAMPLING_LEVEL = 4u;
 
 static constexpr std::string_view SKYBOX_INTENSITY_STRING = "uIntensity";
 
-Dali::Actor CreateSkybox(const std::string& skyboxUrl, Scene3D::SceneView::SkyboxType skyboxType)
+Dali::Actor CreateSkybox()
 {
   struct Vertex
   {
@@ -130,30 +130,9 @@ Dali::Actor CreateSkybox(const std::string& skyboxUrl, Scene3D::SceneView::Skybo
   skyboxGeometry.AddVertexBuffer(vertexBuffer);
   skyboxGeometry.SetType(Geometry::TRIANGLES);
 
-  Dali::Texture  skyboxTexture;
-  Dali::Shader   shaderSkybox;
+  Dali::Shader   shaderSkybox = Shader::New(SHADER_SKYBOX_SHADER_VERT.data(), SHADER_SKYBOX_SHADER_FRAG.data());
   Dali::Renderer skyboxRenderer;
-
-  if(skyboxType == Scene3D::SceneView::SkyboxType::CUBEMAP)
-  {
-    skyboxTexture = Dali::Scene3D::Loader::LoadCubeMap(skyboxUrl);
-    shaderSkybox  = Shader::New(SHADER_SKYBOX_SHADER_VERT.data(), SHADER_SKYBOX_SHADER_FRAG.data());
-  }
-  else // Scene3D::SceneView::SkyboxType::EQUIRECTANGULAR
-  {
-    // Load image from file
-    PixelData pixels = Dali::Toolkit::SyncImageLoader::Load(skyboxUrl);
-
-    skyboxTexture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
-    skyboxTexture.Upload(pixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
-    shaderSkybox = Shader::New(SHADER_SKYBOX_SHADER_VERT.data(), SHADER_SKYBOX_EQUIRECTANGULAR_SHADER_FRAG.data());
-  }
-
-  Dali::TextureSet skyboxTextures = TextureSet::New();
-  skyboxTextures.SetTexture(0, skyboxTexture);
-
   skyboxRenderer = Renderer::New(skyboxGeometry, shaderSkybox);
-  skyboxRenderer.SetTextures(skyboxTextures);
   skyboxRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 2.0f);
   // Enables the depth test.
   skyboxRenderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);
@@ -179,7 +158,27 @@ SceneView::SceneView()
 {
 }
 
-SceneView::~SceneView() = default;
+SceneView::~SceneView()
+{
+  if(Dali::Adaptor::IsAvailable())
+  {
+    if(mIblDiffuseLoadTask)
+    {
+      Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
+      mIblDiffuseLoadTask.Reset();
+    }
+    if(mIblSpecularLoadTask)
+    {
+      Dali::AsyncTaskManager::Get().RemoveTask(mIblSpecularLoadTask);
+      mIblSpecularLoadTask.Reset();
+    }
+    if(mSkyboxLoadTask)
+    {
+      Dali::AsyncTaskManager::Get().RemoveTask(mSkyboxLoadTask);
+      mSkyboxLoadTask.Reset();
+    }
+  }
+}
 
 Dali::Scene3D::SceneView SceneView::New()
 {
@@ -303,23 +302,95 @@ void SceneView::UnregisterSceneItem(Scene3D::Internal::ImageBasedLightObserver*
 
 void SceneView::SetImageBasedLightSource(const std::string& diffuseUrl, const std::string& specularUrl, float scaleFactor)
 {
-  mIBLResourceReady = false;
+  bool needIblReset = false;
+  bool isOnScene = Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE);
+  if(mDiffuseIblUrl != diffuseUrl)
+  {
+    mDiffuseIblUrl = diffuseUrl;
+    if(mDiffuseIblUrl.empty())
+    {
+      needIblReset             = true;
+    }
+    else
+    {
+      mIblDiffuseDirty         = true;
+      mIblDiffuseResourceReady = false;
+    }
+  }
 
-  // If url is empty or invalid, reset IBL.
-  mDiffuseTexture  = (!diffuseUrl.empty()) ? Dali::Scene3D::Loader::LoadCubeMap(diffuseUrl) : Texture();
-  mSpecularTexture = (!specularUrl.empty()) ? Dali::Scene3D::Loader::LoadCubeMap(specularUrl) : Texture();
+  if(mSpecularIblUrl != specularUrl)
+  {
+    mSpecularIblUrl = specularUrl;
+    if(mSpecularIblUrl.empty())
+    {
+      needIblReset              = true;
+    }
+    else
+    {
+      mIblSpecularDirty         = true;
+      mIblSpecularResourceReady = false;
+    }
+  }
 
-  mIblScaleFactor = scaleFactor;
+  // If one or both of diffuse url and specular url are empty,
+  // we don't need to request to load texture.
+  if(needIblReset)
+  {
+    if(mIblDiffuseLoadTask)
+    {
+      Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
+      mIblDiffuseLoadTask.Reset();
+    }
 
-  for(auto&& item : mItems)
+    if(mIblSpecularLoadTask)
+    {
+      Dali::AsyncTaskManager::Get().RemoveTask(mIblSpecularLoadTask);
+      mIblSpecularLoadTask.Reset();
+    }
+
+    mIblDiffuseDirty          = false;
+    mIblSpecularDirty         = false;
+    mIblDiffuseResourceReady  = true;
+    mIblSpecularResourceReady = true;
+
+    mDiffuseTexture.Reset();
+    mSpecularTexture.Reset();
+
+    NotifyImageBasedLightTextureChange();
+  }
+  else
   {
-    if(item)
+    if(isOnScene && mIblDiffuseDirty)
     {
-      item->NotifyImageBasedLightTexture(mDiffuseTexture, mSpecularTexture, mIblScaleFactor);
+      if(mIblDiffuseLoadTask)
+      {
+        Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
+        mIblDiffuseLoadTask.Reset();
+      }
+      mIblDiffuseLoadTask = new EnvironmentMapLoadTask(mDiffuseIblUrl, MakeCallback(this, &SceneView::OnIblDiffuseLoadComplete));
+      Dali::AsyncTaskManager::Get().AddTask(mIblDiffuseLoadTask);
+      mIblDiffuseDirty = false;
     }
+
+    if(isOnScene && mIblSpecularDirty)
+    {
+      if(mIblSpecularLoadTask)
+      {
+        Dali::AsyncTaskManager::Get().RemoveTask(mIblSpecularLoadTask);
+        mIblSpecularLoadTask.Reset();
+      }
+      mIblSpecularLoadTask = new EnvironmentMapLoadTask(mSpecularIblUrl, MakeCallback(this, &SceneView::OnIblSpecularLoadComplete));
+      Dali::AsyncTaskManager::Get().AddTask(mIblSpecularLoadTask);
+      mIblSpecularDirty = false;
+    }
+  }
+
+  if(!Dali::Equals(mIblScaleFactor, scaleFactor))
+  {
+    SetImageBasedLightScaleFactor(scaleFactor);
   }
 
-  mIBLResourceReady = true;
+  // If diffuse and specular textures are already loaded, emits resource ready signal here.
   if(IsResourceReady())
   {
     Control::SetResourceReady(false);
@@ -359,21 +430,57 @@ bool SceneView::IsUsingFramebuffer() const
 
 void SceneView::SetSkybox(const std::string& skyboxUrl, Scene3D::SceneView::SkyboxType skyboxType)
 {
-  mSkyboxResourceReady = false;
-  if(mSkybox)
+  mSkyboxEnvironmentMapType = skyboxType;
+  bool isOnScene = Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE);
+  if(mSkyboxUrl != skyboxUrl)
+  {
+    mSkyboxDirty         = true;
+    mSkyboxResourceReady = false;
+    mSkyboxUrl           = skyboxUrl;
+  }
+
+  if(mSkyboxUrl.empty())
   {
-    mSkybox.Unparent();
-    mSkybox.Reset();
+    if(mSkyboxLoadTask)
+    {
+      Dali::AsyncTaskManager::Get().RemoveTask(mSkyboxLoadTask);
+      mSkyboxLoadTask.Reset();
+    }
+    if(mSkyboxImageLoader)
+    {
+      mSkyboxImageLoader.Cancel(mSkyboxImageId);
+    }
+    mSkyboxDirty         = false;
+    mSkyboxResourceReady = true;
   }
-  mSkybox = CreateSkybox(skyboxUrl, skyboxType);
-  SetSkyboxIntensity(mSkyboxIntensity);
-  SetSkyboxOrientation(mSkyboxOrientation);
-  if(mRootLayer)
+  else
   {
-    mRootLayer.Add(mSkybox);
+    if(isOnScene && mSkyboxDirty)
+    {
+      if(mSkyboxLoadTask)
+      {
+        Dali::AsyncTaskManager::Get().RemoveTask(mSkyboxLoadTask);
+        mSkyboxLoadTask.Reset();
+      }
+      if(mSkyboxImageLoader)
+      {
+        mSkyboxImageLoader.Cancel(mSkyboxImageId);
+      }
+      if(mSkyboxEnvironmentMapType == Scene3D::SceneView::SkyboxType::CUBEMAP)
+      {
+        mSkyboxLoadTask = new EnvironmentMapLoadTask(mSkyboxUrl, MakeCallback(this, &SceneView::OnSkyboxLoadComplete));
+        Dali::AsyncTaskManager::Get().AddTask(mSkyboxLoadTask);
+      }
+      else
+      {
+        mSkyboxImageLoader = Dali::Toolkit::AsyncImageLoader::New();
+        mSkyboxImageLoader.ImageLoadedSignal().Connect(this, &SceneView::OnSkyboxEquirectangularLoadComplete);
+        mSkyboxImageId = mSkyboxImageLoader.Load(mSkyboxUrl);
+      }
+      mSkyboxDirty = false;
+    }
   }
 
-  mSkyboxResourceReady = true;
   if(IsResourceReady())
   {
     Control::SetResourceReady(false);
@@ -421,6 +528,17 @@ Quaternion SceneView::GetSkyboxOrientation() const
 
 void SceneView::OnSceneConnection(int depth)
 {
+  // If diffuse and specular url is not valid, IBL does not need to be loaded.
+  if(!mDiffuseIblUrl.empty() && !mSpecularIblUrl.empty())
+  {
+    SetImageBasedLightSource(mDiffuseIblUrl, mSpecularIblUrl, mIblScaleFactor);
+  }
+
+  if(!mSkyboxUrl.empty())
+  {
+    SetSkybox(mSkyboxUrl, mSkyboxEnvironmentMapType);
+  }
+
   Window window = DevelWindow::Get(Self());
   if(window)
   {
@@ -518,7 +636,7 @@ void SceneView::OnRelayout(const Vector2& size, RelayoutContainer& container)
 
 bool SceneView::IsResourceReady() const
 {
-  return mIBLResourceReady & mSkyboxResourceReady;
+  return mIblDiffuseResourceReady && mIblSpecularResourceReady && mSkyboxResourceReady;
 }
 
 void SceneView::UpdateCamera(CameraActor camera)
@@ -630,6 +748,97 @@ void SceneView::RotateCamera()
   }
 }
 
+void SceneView::OnSkyboxEquirectangularLoadComplete(uint32_t loadedTaskId, PixelData pixelData)
+{
+  mSkyboxTexture = Texture::New(TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight());
+  mSkyboxTexture.Upload(pixelData, 0, 0, 0, 0, pixelData.GetWidth(), pixelData.GetHeight());
+  OnSkyboxLoadComplete();
+}
+
+void SceneView::OnSkyboxLoadComplete()
+{
+  if(!mSkybox)
+  {
+    mSkybox = CreateSkybox();
+    SetSkyboxIntensity(mSkyboxIntensity);
+    SetSkyboxOrientation(mSkyboxOrientation);
+    if(mRootLayer)
+    {
+      mRootLayer.Add(mSkybox);
+    }
+  }
+
+  mSkyboxResourceReady = true;
+  if(IsResourceReady())
+  {
+    Control::SetResourceReady(false);
+  }
+
+  Shader skyboxShader;
+  if(mSkyboxEnvironmentMapType == Scene3D::SceneView::SkyboxType::CUBEMAP)
+  {
+    mSkyboxTexture = (mSkyboxLoadTask->HasSucceeded()) ? mSkyboxLoadTask->GetEnvironmentMap().CreateTexture() : Texture();
+    skyboxShader   = Shader::New(SHADER_SKYBOX_SHADER_VERT.data(), SHADER_SKYBOX_SHADER_FRAG.data());
+    Dali::AsyncTaskManager::Get().RemoveTask(mSkyboxLoadTask);
+    mSkyboxLoadTask.Reset();
+  }
+  else
+  {
+    skyboxShader = Shader::New(SHADER_SKYBOX_SHADER_VERT.data(), SHADER_SKYBOX_EQUIRECTANGULAR_SHADER_FRAG.data());
+  }
+
+  Renderer skyboxRenderer = (mSkybox.GetRendererCount() > 0u) ? mSkybox.GetRendererAt(0u) : Renderer();
+  if(skyboxRenderer)
+  {
+    Dali::TextureSet skyboxTextures = TextureSet::New();
+    skyboxTextures.SetTexture(0, mSkyboxTexture);
+    skyboxRenderer.SetTextures(skyboxTextures);
+    skyboxRenderer.SetShader(skyboxShader);
+  }
+}
+
+void SceneView::OnIblDiffuseLoadComplete()
+{
+  mDiffuseTexture          = (mIblDiffuseLoadTask->HasSucceeded()) ? mIblDiffuseLoadTask->GetEnvironmentMap().CreateTexture() : Texture();
+  mIblDiffuseResourceReady = true;
+  if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
+  {
+    OnIblLoadComplete();
+  }
+  mIblDiffuseLoadTask.Reset();
+}
+
+void SceneView::OnIblSpecularLoadComplete()
+{
+  mSpecularTexture          = (mIblSpecularLoadTask->HasSucceeded()) ? mIblSpecularLoadTask->GetEnvironmentMap().CreateTexture() : Texture();
+  mIblSpecularResourceReady = true;
+  if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
+  {
+    OnIblLoadComplete();
+  }
+  mIblSpecularLoadTask.Reset();
+}
+
+void SceneView::OnIblLoadComplete()
+{
+  NotifyImageBasedLightTextureChange();
+  if(IsResourceReady())
+  {
+    Control::SetResourceReady(false);
+  }
+}
+
+void SceneView::NotifyImageBasedLightTextureChange()
+{
+  for(auto&& item : mItems)
+  {
+    if(item)
+    {
+      item->NotifyImageBasedLightTexture(mDiffuseTexture, mSpecularTexture, mIblScaleFactor);
+    }
+  }
+}
+
 } // namespace Internal
 } // namespace Scene3D
 } // namespace Dali
index def2b83..4b026d5 100644 (file)
@@ -21,6 +21,7 @@
 // EXTERNAL INCLUDES
 #include <dali-toolkit/internal/visuals/image/image-visual.h>
 #include <dali-toolkit/public-api/controls/control-impl.h>
+#include <dali-toolkit/public-api/image-loader/async-image-loader.h>
 #include <dali/public-api/actors/camera-actor.h>
 #include <dali/public-api/actors/layer.h>
 #include <dali/public-api/adaptor-framework/window.h>
@@ -31,8 +32,9 @@
 #include <dali/public-api/rendering/texture.h>
 
 // INTERNAL INCLUDES
-#include <dali-scene3d/internal/common/image-based-light-observer.h>
 #include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
+#include <dali-scene3d/internal/common/environment-map-load-task.h>
+#include <dali-scene3d/internal/common/image-based-light-observer.h>
 
 namespace Dali
 {
@@ -241,6 +243,36 @@ private:
    */
   void RotateCamera();
 
+  /**
+   * @brief Asynchronously skybox (Equirectangular) loading finished.
+   */
+  void OnSkyboxEquirectangularLoadComplete(uint32_t loadedTaskId, PixelData pixelData);
+
+  /**
+   * @brief Asynchronously skybox loading finished.
+   */
+  void OnSkyboxLoadComplete();
+
+  /**
+   * @brief Asynchronously ibl diffusel image loading finished.
+   */
+  void OnIblDiffuseLoadComplete();
+
+  /**
+   * @brief Asynchronously ibl specular image loading finished.
+   */
+  void OnIblSpecularLoadComplete();
+
+  /**
+   * @brief Asynchronously ibl loading finished.
+   */
+  void OnIblLoadComplete();
+
+  /**
+   * @brief Notify the changes of Ibl textures to the child items.
+   */
+  void NotifyImageBasedLightTextureChange();
+
 private:
   Toolkit::Visual::Base mVisual;
 
@@ -260,12 +292,28 @@ private:
   Quaternion                                               mSkyboxOrientation;
   float                                                    mSkyboxIntensity{1.0f};
 
-  Dali::Texture mSpecularTexture;
-  Dali::Texture mDiffuseTexture;
-  float         mIblScaleFactor{1.0f};
-  bool          mUseFrameBuffer{false};
-  bool          mIBLResourceReady{true};
-  bool          mSkyboxResourceReady{true};
+  // Asynchronous Loading.
+  EnvironmentMapLoadTaskPtr       mSkyboxLoadTask;
+  EnvironmentMapLoadTaskPtr       mIblDiffuseLoadTask;
+  EnvironmentMapLoadTaskPtr       mIblSpecularLoadTask;
+  std::string                     mSkyboxUrl;
+  std::string                     mDiffuseIblUrl;
+  std::string                     mSpecularIblUrl;
+  Dali::Toolkit::AsyncImageLoader mSkyboxImageLoader;
+  uint32_t                        mSkyboxImageId{0u};
+
+  Scene3D::SceneView::SkyboxType mSkyboxEnvironmentMapType;
+  Dali::Texture                  mSkyboxTexture;
+  Dali::Texture                  mDiffuseTexture;
+  Dali::Texture                  mSpecularTexture;
+  float                          mIblScaleFactor{1.0f};
+  bool                           mUseFrameBuffer{false};
+  bool                           mSkyboxResourceReady{true};
+  bool                           mIblDiffuseResourceReady{true};
+  bool                           mIblSpecularResourceReady{true};
+  bool                           mSkyboxDirty{false};
+  bool                           mIblDiffuseDirty{false};
+  bool                           mIblSpecularDirty{false};
 
   // TODO : Light Source
 };
index c4e735b..d264857 100644 (file)
@@ -1,6 +1,8 @@
 set(scene3d_internal_dir "${scene3d_dir}/internal")
 
 set(scene3d_src_files ${scene3d_src_files}
+       ${scene3d_internal_dir}/common/environment-map-load-task.cpp
+       ${scene3d_internal_dir}/common/model-load-task.cpp
        ${scene3d_internal_dir}/controls/model/model-impl.cpp
        ${scene3d_internal_dir}/controls/scene-view/scene-view-impl.cpp
        ${scene3d_internal_dir}/loader/gltf2-asset.cpp
index 7c4f67b..e538d29 100644 (file)
@@ -331,7 +331,7 @@ public:
    *
    * @SINCE_2_2.0
    * @param[in] skyboxUrl image url for skybox.
-   * @param[in] skyboxType The skybox type (by default it is cubemap).
+   * @param[in] skyboxType The environment type of skymap (by default it is cubemap).
    */
   void SetSkybox(const std::string& skyboxUrl, SkyboxType skyboxType = SkyboxType::CUBEMAP);
 
index 6f24d9e..c201314 100644 (file)
@@ -50,25 +50,25 @@ AnimationDefinition::AnimationDefinition(AnimationDefinition&& other)
 void AnimationDefinition::Animate(Animation& animation, AnimatedProperty::GetActor getActor)
 {
   DALI_ASSERT_ALWAYS(animation);
-  for(auto& ap : mProperties)
+  for(auto& property : mProperties)
   {
-    ap.Animate(animation, getActor);
+    property.Animate(animation, getActor);
   }
 }
 
 Animation AnimationDefinition::ReAnimate(AnimatedProperty::GetActor getActor)
 {
   // Create and configure new animation.
-  Animation a = Animation::New(mDuration);
-  a.SetLoopCount(mLoopCount);
-  a.SetDisconnectAction(mDisconnectAction);
-  a.SetEndAction(mEndAction);
+  Animation animation = Animation::New(mDuration);
+  animation.SetLoopCount(mLoopCount);
+  animation.SetDisconnectAction(mDisconnectAction);
+  animation.SetEndAction(mEndAction);
 
-  a.SetSpeedFactor(mSpeedFactor);
-  a.SetPlayRange(mPlayRange);
+  animation.SetSpeedFactor(mSpeedFactor);
+  animation.SetPlayRange(mPlayRange);
 
-  Animate(a, getActor);
-  return a;
+  Animate(animation, getActor);
+  return animation;
 }
 
 AnimationDefinition& AnimationDefinition::operator=(AnimationDefinition&& other)
index 7573421..96cafcb 100644 (file)
@@ -93,7 +93,6 @@ namespace Loader
 {
 bool LoadCubeData(const std::string& path, CubeData& cubedata)
 {
-  Texture cubeTexture;
   // Diffuse Cube Map
   if(path.empty())
   {
index a8bb7a2..0c76617 100644 (file)
@@ -59,7 +59,7 @@ EnvironmentDefinition::LoadRaw(const std::string& environmentsPath) const
         face.push_back(PixelData::New(new uint8_t[3]{0xff, 0xff, 0xff}, 3, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY));
       }
     }
-    else if(!LoadCubeMapData(environmentsPath + path, cd))
+    else if(!LoadCubeMapData(environmentsPath + path, cd)) // TODO: supporting EQUIRECTANGULAR
     {
       ExceptionFlinger(ASSERT_LOCATION) << "Failed to load cubemap texture from '" << path << "'.";
     }
index d80982a..a6f1946 100644 (file)
@@ -24,6 +24,7 @@
 // EXTERNAL INCLUDES
 #include "dali/public-api/math/quaternion.h"
 #include "dali/public-api/rendering/texture.h"
+#include <memory>
 
 namespace Dali
 {
@@ -87,12 +88,13 @@ struct DALI_SCENE3D_API EnvironmentDefinition
   static float GetDefaultIntensity();
 
 public: // DATA
-  std::string mDiffuseMapPath;
-  std::string mSpecularMapPath;
-  Quaternion  mCubeOrientation = Quaternion::IDENTITY;
-  Vector3     mYDirection      = Vector3::ONE;
-  float       mIblIntensity    = 1.0f;
-  bool        mUseBrdfTexture  = false;
+  std::string              mDiffuseMapPath;
+  std::string              mSpecularMapPath;
+  std::shared_ptr<RawData> mRawData;
+  Quaternion               mCubeOrientation = Quaternion::IDENTITY;
+  Vector3                  mYDirection      = Vector3::ONE;
+  float                    mIblIntensity    = 1.0f;
+  bool                     mUseBrdfTexture  = false;
 };
 
 } // namespace Loader
index 257ba32..6d209d2 100644 (file)
@@ -22,6 +22,7 @@
 #include <dali/integration-api/debug.h>
 #include <dali/public-api/images/image-operations.h>
 #include <dali/public-api/math/quaternion.h>
+#include <dali/devel-api/threading/mutex.h>
 #include <fstream>
 
 // INTERNAL INCLUDES
@@ -43,6 +44,9 @@ namespace Loader
 {
 namespace
 {
+Dali::Mutex gInitializeMutex;
+Dali::Mutex gReadMutex;
+
 const std::string POSITION_PROPERTY("position");
 const std::string ORIENTATION_PROPERTY("orientation");
 const std::string SCALE_PROPERTY("scale");
@@ -76,9 +80,6 @@ struct AttributeMapping
 
 std::vector<gt::Animation> ReadAnimationArray(const json_value_s& j)
 {
-  gt::Animation proxy;
-  SetRefReaderObject(proxy);
-
   auto results = js::Read::Array<gt::Animation, js::ObjectReader<gt::Animation>::Read>(j);
 
   for(auto& animation : results)
@@ -1230,6 +1231,21 @@ void SetDefaultEnvironmentMap(const gt::Document& doc, ConversionContext& contex
 
 } // namespace
 
+void InitializeGltfLoader()
+{
+  // Set ObjectReader only once (for all gltf loading).
+  static bool setObjectReadersRequired = true;
+  {
+    Mutex::ScopedLock lock(gInitializeMutex);
+    if(setObjectReadersRequired)
+    {
+      // NOTE: only referencing own, anonymous namespace, const objects; the pointers will never need to change.
+      SetObjectReaders();
+      setObjectReadersRequired = false;
+    }
+  }
+}
+
 void LoadGltfScene(const std::string& url, ShaderDefinitionFactory& shaderFactory, LoadResult& params)
 {
   bool failed = false;
@@ -1245,14 +1261,6 @@ void LoadGltfScene(const std::string& url, ShaderDefinitionFactory& shaderFactor
     throw std::runtime_error("Failed to parse " + url);
   }
 
-  static bool setObjectReaders = true;
-  if(setObjectReaders)
-  {
-    // NOTE: only referencing own, anonymous namespace, const objects; the pointers will never need to change.
-    SetObjectReaders();
-    setObjectReaders = false;
-  }
-
   gt::Document doc;
 
   auto& rootObj = js::Cast<json_object_s>(*root);
@@ -1272,8 +1280,11 @@ void LoadGltfScene(const std::string& url, ShaderDefinitionFactory& shaderFactor
     isMRendererModel      = (doc.mAsset.mGenerator.find(MRENDERER_MODEL_IDENTIFICATION) != std::string_view::npos);
   }
 
-  gt::SetRefReaderObject(doc);
-  DOCUMENT_READER.Read(rootObj, doc);
+  {
+    Mutex::ScopedLock lock(gReadMutex);
+    gt::SetRefReaderObject(doc);
+    DOCUMENT_READER.Read(rootObj, doc);
+  }
 
   auto              path = url.substr(0, url.rfind('/') + 1);
   ConversionContext context{params, path, INVALID_INDEX};
index 765cd40..dc7becf 100644 (file)
@@ -34,9 +34,16 @@ struct LoadResult;
 class ShaderDefinitionFactory;
 
 /**
+ * @brief Initialize glTF Loader.
+ * @note This method should be called once before LoadGltfScene() is called.
+ */
+DALI_SCENE3D_API void InitializeGltfLoader();
+
+/**
  * @brief Loads the scene from the glTF file located at @a url, storing the results in @a params.
  * @note Will throw std::runtime_error for JSON entities with types mismatching expectations, carrying
  *  invalid values, or I/O errors.
+ * @note InitializeGltfLoader() should be called once before this function is called.
  */
 DALI_SCENE3D_API void LoadGltfScene(const std::string& url, ShaderDefinitionFactory& shaderFactory, LoadResult& params);
 
index b59c6aa..00c2a83 100644 (file)
@@ -225,7 +225,8 @@ struct DALI_SCENE3D_API MaterialDefinition
   }
 
 public: // DATA
-  uint32_t mFlags = 0x0;
+  std::shared_ptr<RawData> mRawData;
+  uint32_t                 mFlags = 0x0;
 
   Index   mEnvironmentIdx      = 0;
   Vector4 mColor               = Color::WHITE;
index 595d06e..1030c7c 100644 (file)
@@ -268,18 +268,19 @@ struct DALI_SCENE3D_API MeshDefinition
   MeshGeometry Load(RawData&& raw) const;
 
 public: // DATA
-  uint32_t       mFlags         = 0x0;
-  Geometry::Type mPrimitiveType = Geometry::TRIANGLES;
-  std::string    mUri;
-  Accessor       mIndices;
-  Accessor       mPositions;
-  Accessor       mNormals; // data can be generated based on positions
-  Accessor       mTexCoords;
-  Accessor       mColors;
-  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;
-  Property::Type mTangentType{Property::VECTOR3};
+  std::shared_ptr<RawData> mRawData;
+  uint32_t                 mFlags         = 0x0;
+  Geometry::Type           mPrimitiveType = Geometry::TRIANGLES;
+  std::string              mUri;
+  Accessor                 mIndices;
+  Accessor                 mPositions;
+  Accessor                 mNormals; // data can be generated based on positions
+  Accessor                 mTexCoords;
+  Accessor                 mColors;
+  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;
+  Property::Type           mTangentType{Property::VECTOR3};
 
   Blob                    mBlendShapeHeader;
   std::vector<BlendShape> mBlendShapes;
index 5df41e2..38f6bc9 100644 (file)
@@ -148,6 +148,119 @@ void ResourceBundle::LoadResources(const ResourceRefCounts& refCounts, PathProvi
   }
 }
 
+void ResourceBundle::LoadRawResources(const ResourceRefCounts& refCounts, PathProvider pathProvider, Options::Type options)
+{
+  const auto kForceLoad  = MaskMatch(options, Options::ForceReload);
+
+  const auto& refCountEnvMaps  = refCounts[ResourceType::Environment];
+  auto        environmentsPath = pathProvider(ResourceType::Environment);
+  for(uint32_t i = 0, iEnd = refCountEnvMaps.Size(); i != iEnd; ++i)
+  {
+    auto  refCount = refCountEnvMaps[i];
+    auto& iEnvMap  = mEnvironmentMaps[i];
+    if(refCount > 0 && (kForceLoad || !iEnvMap.second.IsLoaded()))
+    {
+      iEnvMap.first.mRawData = std::make_shared<EnvironmentDefinition::RawData>(iEnvMap.first.LoadRaw(environmentsPath));
+    }
+  }
+
+  const auto& refCountShaders = refCounts[ResourceType::Shader];
+  auto        shadersPath     = pathProvider(ResourceType::Shader);
+  for(uint32_t i = 0, iEnd = refCountShaders.Size(); i != iEnd; ++i)
+  {
+    auto  refCount = refCountShaders[i];
+    auto& iShader  = mShaders[i];
+    if(refCount > 0 && (kForceLoad || !iShader.second))
+    {
+      iShader.first.mRawData = std::make_shared<ShaderDefinition::RawData>(iShader.first.LoadRaw(shadersPath));
+    }
+  }
+
+  const auto& refCountMeshes = refCounts[ResourceType::Mesh];
+  auto        modelsPath     = pathProvider(ResourceType::Mesh);
+  for(uint32_t i = 0, iEnd = refCountMeshes.Size(); i != iEnd; ++i)
+  {
+    auto  refCount = refCountMeshes[i];
+    auto& iMesh    = mMeshes[i];
+    if(refCount > 0 && (kForceLoad || !iMesh.second.geometry))
+    {
+      iMesh.first.mRawData = std::make_shared<MeshDefinition::RawData>(iMesh.first.LoadRaw(modelsPath));
+    }
+  }
+
+  const auto& refCountMaterials = refCounts[ResourceType::Material];
+  auto        imagesPath        = pathProvider(ResourceType::Material);
+  for(uint32_t i = 0, iEnd = refCountMaterials.Size(); i != iEnd; ++i)
+  {
+    auto  refCount  = refCountMaterials[i];
+    auto& iMaterial = mMaterials[i];
+    if(refCount > 0 && (kForceLoad || !iMaterial.second))
+    {
+      iMaterial.first.mRawData = std::make_shared<MaterialDefinition::RawData>(iMaterial.first.LoadRaw(imagesPath));
+    }
+  }
+}
+
+void ResourceBundle::GenerateResources(const ResourceRefCounts& refCounts, Options::Type options)
+{
+  const auto& refCountEnvMaps = refCounts[ResourceType::Environment];
+  for(uint32_t i = 0, iEnd = refCountEnvMaps.Size(); i != iEnd; ++i)
+  {
+    auto& iEnvMap = mEnvironmentMaps[i];
+    if(iEnvMap.first.mRawData)
+    {
+      iEnvMap.second = iEnvMap.first.Load(std::move(*(iEnvMap.first.mRawData)));
+    }
+    else
+    {
+      iEnvMap.second.mDiffuse  = Texture();
+      iEnvMap.second.mSpecular = Texture();
+    }
+  }
+
+  const auto& refCountShaders = refCounts[ResourceType::Shader];
+  for(uint32_t i = 0, iEnd = refCountShaders.Size(); i != iEnd; ++i)
+  {
+    auto& iShader = mShaders[i];
+    if(iShader.first.mRawData)
+    {
+      iShader.second = iShader.first.Load(std::move(*(iShader.first.mRawData)));
+    }
+    else
+    {
+      iShader.second = Shader();
+    }
+  }
+
+  const auto& refCountMeshes = refCounts[ResourceType::Mesh];
+  for(uint32_t i = 0, iEnd = refCountMeshes.Size(); i != iEnd; ++i)
+  {
+    auto& iMesh = mMeshes[i];
+    if(iMesh.first.mRawData)
+    {
+      iMesh.second = iMesh.first.Load(std::move(*(iMesh.first.mRawData)));
+    }
+    else
+    {
+      iMesh.second.geometry = Geometry();
+    }
+  }
+
+  const auto& refCountMaterials = refCounts[ResourceType::Material];
+  for(uint32_t i = 0, iEnd = refCountMaterials.Size(); i != iEnd; ++i)
+  {
+    auto& iMaterial = mMaterials[i];
+    if(iMaterial.first.mRawData)
+    {
+      iMaterial.second = iMaterial.first.Load(mEnvironmentMaps, std::move(*(iMaterial.first.mRawData)));
+    }
+    else
+    {
+      iMaterial.second = TextureSet();
+    }
+  }
+}
+
 } // namespace Loader
 } // namespace Scene3D
 } // namespace Dali
index 1537f72..cea48c0 100644 (file)
@@ -90,31 +90,64 @@ public:
   ResourceBundle(ResourceBundle&&) = default;
   ResourceBundle& operator=(ResourceBundle&&) = default;
 
-  /*
+  /**
    * @return A ResourceRefCounts object with the correct number of entries for
    *  all resource types (based on the various resource definition vectors),
    *  with all reference counts set to 0.
    */
   ResourceRefCounts CreateRefCounter() const;
 
-  /*
+  /**
    * @brief Based on a ResourceRefCounts, and more specifically the reference
    *  count of materials therein, it will calculate the reference count of
    *  environment maps.
    */
   void CountEnvironmentReferences(ResourceRefCounts& refCounts) const;
 
-  /*
+  /**
    * @brief Performs the loading of all resources based on their respective
-   *  reference count in @a refCounts. Resources that had a non-zero ref count will be
-   *  loaded unless we already have a handle to them (OR the ForceReload option was specified).
-   *  Any handles we have to resources that come in with a zero ref count will be reset,
-   *  UNLESS the KeepUnused option was specified.
+   * reference count in @a refCounts. Resources that had a non-zero ref count will be
+   * loaded unless we already have a handle to them (OR the ForceReload option was specified).
+   * Any handles we have to resources that come in with a zero ref count will be reset,
+   * UNLESS the KeepUnused option was specified.
+   * @param[in] refCounts Reference Count that denote how many the resource is used.
+   * @param[in] pathProvider path provider for resource data.
+   * @param[in] options Option to load resource
+   * @note This method creates DALi objects like Dali::Texture, Dali::Geometry, etc.
    */
   void LoadResources(const ResourceRefCounts& refCounts,
                      PathProvider             pathProvider,
                      Options::Type            options = Options::None);
 
+  /**
+   * @brief Loads of all resources based on their respective
+   * reference count in @a refCounts. Resources that had a non-zero ref count will be
+   * loaded unless we already have a handle to them (OR the ForceReload option was specified).
+   * Any handles we have to resources that come in with a zero ref count will be reset,
+   * UNLESS the KeepUnused option was specified.
+   * @note This method don't create any of DALi objects.
+   * @param[in] refCounts Reference Count that denote how many the resource is used.
+   * @param[in] pathProvider path provider for resource data.
+   * @param[in] options Option to load resource
+   * @note This method only loads raw data from resource file, and
+   * doesn't create any of DALi objects. GenerateResources() method is required to be called
+   * after this method to create DALi objects.
+   */
+  void LoadRawResources(const ResourceRefCounts& refCounts,
+                        PathProvider             pathProvider,
+                        Options::Type            options = Options::None);
+
+  /**
+   * @brief Generates DALi objects from already loaded Raw Resources.
+   * @param[in] refCounts Reference Count that denote how many the resource is used.
+   * @param[in] options Option to load resource
+   * @note This method generates DALi objects from raw data that is already
+   * loaded by LoadRawResources method. Therefore, LoadRawResources should be called first
+   * before this method is called.
+   */
+  void GenerateResources(const ResourceRefCounts& refCounts,
+                         Options::Type            options = Options::None);
+
 public: // DATA
   EnvironmentDefinition::Vector mEnvironmentMaps;
   ShaderDefinition::Vector      mShaders;
index d381c57..3d3236b 100644 (file)
@@ -56,7 +56,7 @@ struct DALI_SCENE3D_API ShaderDefinition
   ShaderDefinition(const ShaderDefinition& other);
   ShaderDefinition& operator=(const ShaderDefinition& other);
 
-  ShaderDefinition(ShaderDefinition&&) = default;
+  ShaderDefinition(ShaderDefinition&&)            = default;
   ShaderDefinition& operator=(ShaderDefinition&&) = default;
 
   /*
@@ -75,7 +75,8 @@ struct DALI_SCENE3D_API ShaderDefinition
   Shader Load(RawData&& raw) const;
 
 public: // DATA
-  RendererState::Type mRendererState = RendererState::NONE;
+  std::shared_ptr<RawData> mRawData;
+  RendererState::Type      mRendererState = RendererState::NONE;
 
   std::string              mVertexShaderPath;
   std::string              mFragmentShaderPath;
@@ -89,4 +90,4 @@ public: // DATA
 } // namespace Scene3D
 } // namespace Dali
 
-#endif //DALI_SCENE3D_LOADER_SHADER_DEFINITION_H
+#endif // DALI_SCENE3D_LOADER_SHADER_DEFINITION_H