[Tizen] Backport 7.5 Scene3D for use ModelNode 01/291501/2
authorhuayong.xu <huayong.xu@samsung.com>
Mon, 20 Mar 2023 11:08:48 +0000 (19:08 +0800)
committerEunki, Hong <eunkiki.hong@samsung.com>
Tue, 18 Apr 2023 00:44:33 +0000 (09:44 +0900)
This is a combination of 10 commits.

Make sure that global variables are initialized lazily in scene3d.

Fix coverity issue (no POSITION attribute + use after move)

Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
Change namings of gltf-util

Signed-off-by: seungho baek <sbsh.baek@samsung.com>
Remove Duplicated code in gltf2-util using template

Signed-off-by: seungho baek <sbsh.baek@samsung.com>
Enable automated tests that load the exercise DLI model

Replace std::string global variables with C-style string.

A std::string global variable would allocate a memory in heap,
then copy the string from .text section to the heap.
This patch may reduce loading time & memory a little.

Use ModelNode / ModelPrimitive / Material instead of Actor / Renderer

 - This patch is based on the previous patch
https://review.tizen.org/gerrit/#/c/platform/core/uifw/dali-toolkit/+/288075/

Signed-off-by: seungho baek <sbsh.baek@samsung.com>
Fix svace/coverity issue (initialize value at Material)

Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
Change public member variable to private

Signed-off-by: seungho baek <sbsh.baek@samsung.com>
Allow to use premultiplied external texture

FrameBuffer result colortexture is premultiplied always.
So make we always use premultiplied texture.

And make a way to user determine native image source is premultiplied or not.

+

Since FBO texture always premultiplied, revert scene view's trick
https://review.tizen.org/gerrit/c/platform/core/uifw/dali-toolkit/+/285486

Change-Id: Idfec7ea5a26dcc827953ce268c32adc8f89247bd
Signed-off-by: Eunki Hong <eunkiki.hong@samsung.com>
108 files changed:
automated-tests/src/dali-scene3d-internal/CMakeLists.txt
automated-tests/src/dali-scene3d-internal/utc-Dali-Gltf2LoaderImpl.cpp
automated-tests/src/dali-scene3d-internal/utc-Dali-MaterialImpl.cpp [new file with mode: 0644]
automated-tests/src/dali-scene3d-internal/utc-Dali-ModelPrimitiveImpl.cpp [new file with mode: 0644]
automated-tests/src/dali-scene3d/CMakeLists.txt
automated-tests/src/dali-scene3d/utc-Dali-AnimationDefinition.cpp
automated-tests/src/dali-scene3d/utc-Dali-BvhLoader.cpp
automated-tests/src/dali-scene3d/utc-Dali-FacialAnimation.cpp
automated-tests/src/dali-scene3d/utc-Dali-Material.cpp [new file with mode: 0644]
automated-tests/src/dali-scene3d/utc-Dali-Model.cpp
automated-tests/src/dali-scene3d/utc-Dali-ModelNode.cpp [new file with mode: 0644]
automated-tests/src/dali-scene3d/utc-Dali-ModelPrimitive.cpp [new file with mode: 0644]
automated-tests/src/dali-scene3d/utc-Dali-NodeDefinition.cpp
automated-tests/src/dali-toolkit/utc-Dali-Image.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageUrl.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextureManager.cpp
dali-scene3d/internal/common/model-cache-manager.h
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/file.list
dali-scene3d/internal/graphics/shaders/default-physically-based-shader.frag
dali-scene3d/internal/graphics/shaders/default-physically-based-shader.vert
dali-scene3d/internal/loader/dli-loader-impl.cpp
dali-scene3d/internal/loader/gltf2-asset.cpp
dali-scene3d/internal/loader/gltf2-util.cpp
dali-scene3d/internal/loader/gltf2-util.h
dali-scene3d/internal/loader/json-util.cpp
dali-scene3d/internal/model-components/material-impl.cpp [new file with mode: 0644]
dali-scene3d/internal/model-components/material-impl.h [new file with mode: 0644]
dali-scene3d/internal/model-components/material-modify-observer.h [new file with mode: 0644]
dali-scene3d/internal/model-components/model-node-data-impl.cpp [new file with mode: 0644]
dali-scene3d/internal/model-components/model-node-data-impl.h [new file with mode: 0644]
dali-scene3d/internal/model-components/model-node-impl.cpp [new file with mode: 0644]
dali-scene3d/internal/model-components/model-node-impl.h [new file with mode: 0644]
dali-scene3d/internal/model-components/model-primitive-impl.cpp [new file with mode: 0644]
dali-scene3d/internal/model-components/model-primitive-impl.h [new file with mode: 0644]
dali-scene3d/internal/model-components/model-primitive-modify-observer.h [new file with mode: 0644]
dali-scene3d/public-api/controls/model/model.cpp
dali-scene3d/public-api/controls/model/model.h
dali-scene3d/public-api/file.list
dali-scene3d/public-api/loader/alpha-function-helper.cpp
dali-scene3d/public-api/loader/animation-definition.cpp
dali-scene3d/public-api/loader/animation-definition.h
dali-scene3d/public-api/loader/blend-shape-details.cpp
dali-scene3d/public-api/loader/blend-shape-details.h
dali-scene3d/public-api/loader/bvh-loader.cpp
dali-scene3d/public-api/loader/environment-definition.cpp
dali-scene3d/public-api/loader/environment-definition.h
dali-scene3d/public-api/loader/facial-animation-loader.cpp
dali-scene3d/public-api/loader/material-definition.cpp
dali-scene3d/public-api/loader/material-definition.h
dali-scene3d/public-api/loader/matrix-stack.h
dali-scene3d/public-api/loader/mesh-definition.cpp
dali-scene3d/public-api/loader/mesh-definition.h
dali-scene3d/public-api/loader/mesh-geometry.h
dali-scene3d/public-api/loader/node-definition.cpp
dali-scene3d/public-api/loader/node-definition.h
dali-scene3d/public-api/loader/parse-renderer-state.cpp
dali-scene3d/public-api/loader/parse-renderer-state.h
dali-scene3d/public-api/loader/resource-bundle.h
dali-scene3d/public-api/loader/scene-definition.cpp
dali-scene3d/public-api/loader/scene-definition.h
dali-scene3d/public-api/loader/shader-definition-factory.cpp
dali-scene3d/public-api/loader/shader-definition.cpp
dali-scene3d/public-api/loader/skeleton-definition.h
dali-scene3d/public-api/loader/skinning-details.cpp
dali-scene3d/public-api/loader/skinning-details.h
dali-scene3d/public-api/model-components/material.cpp [new file with mode: 0644]
dali-scene3d/public-api/model-components/material.h [new file with mode: 0644]
dali-scene3d/public-api/model-components/model-node.cpp [new file with mode: 0644]
dali-scene3d/public-api/model-components/model-node.h [new file with mode: 0644]
dali-scene3d/public-api/model-components/model-primitive.cpp [new file with mode: 0644]
dali-scene3d/public-api/model-components/model-primitive.h [new file with mode: 0644]
dali-toolkit/devel-api/image-loader/texture-manager.cpp
dali-toolkit/devel-api/image-loader/texture-manager.h
dali-toolkit/internal/builder/builder-impl.cpp
dali-toolkit/internal/controls/control/control-data-impl.cpp
dali-toolkit/internal/controls/scene3d-view/scene3d-view-impl.cpp
dali-toolkit/internal/controls/text-controls/text-label-impl.cpp
dali-toolkit/internal/controls/text-controls/text-selection-popup-impl.cpp
dali-toolkit/internal/image-loader/image-url-impl.cpp
dali-toolkit/internal/image-loader/image-url-impl.h
dali-toolkit/internal/text/controller/text-controller-event-handler.cpp
dali-toolkit/internal/text/controller/text-controller-impl.cpp
dali-toolkit/internal/text/controller/text-controller-input-font-handler.cpp
dali-toolkit/internal/text/controller/text-controller-input-font-handler.h
dali-toolkit/internal/text/controller/text-controller-input-properties.cpp
dali-toolkit/internal/text/controller/text-controller-input-properties.h
dali-toolkit/internal/text/controller/text-controller-placeholder-handler.cpp
dali-toolkit/internal/text/controller/text-controller-placeholder-handler.h
dali-toolkit/internal/text/controller/text-controller.cpp
dali-toolkit/internal/text/controller/text-controller.h
dali-toolkit/internal/text/markup-processor/markup-processor-embedded-item.cpp
dali-toolkit/internal/text/markup-processor/markup-processor-font.cpp
dali-toolkit/internal/text/markup-processor/markup-processor-helper-functions.cpp
dali-toolkit/internal/text/text-effects-style.cpp
dali-toolkit/internal/text/text-font-style.cpp
dali-toolkit/internal/texture-manager/texture-cache-manager.cpp
dali-toolkit/internal/texture-manager/texture-cache-manager.h
dali-toolkit/internal/texture-manager/texture-manager-impl.cpp
dali-toolkit/internal/texture-manager/texture-manager-impl.h
dali-toolkit/internal/texture-manager/texture-manager-type.h
dali-toolkit/public-api/image-loader/image-url.cpp
dali-toolkit/public-api/image-loader/image-url.h
dali-toolkit/public-api/image-loader/image.cpp
dali-toolkit/public-api/image-loader/image.h

index 06da0de..561e753 100755 (executable)
@@ -15,7 +15,9 @@ SET(TC_SOURCES
   utc-Dali-Hash.cpp
   utc-Dali-JsonReader.cpp
   utc-Dali-JsonUtil.cpp
+  utc-Dali-MaterialImpl.cpp
   utc-Dali-ModelCacheManager.cpp
+  utc-Dali-ModelPrimitiveImpl.cpp
 )
 
 # List of test harness files (Won't get parsed for test cases)
index 8133fb9..92f66ed 100644 (file)
@@ -202,6 +202,7 @@ int UtcDaliGltfLoaderSuccess1(void)
       1.f,
       1.f,
       Vector3(0.2, 0.1, 0.0),
+      1.0f,
       0.0f,
       0.5f,
       Vector3(0, 0, 1),
@@ -209,6 +210,7 @@ int UtcDaliGltfLoaderSuccess1(void)
       false,
       true,
       false,
+      Scene3D::Material::AlphaModeType::MASK,
       true,
       true,
       {
@@ -267,6 +269,7 @@ int UtcDaliGltfLoaderSuccess1(void)
           },
         },
       },
+      nullptr
     },
     {
       nullptr,
@@ -281,6 +284,7 @@ int UtcDaliGltfLoaderSuccess1(void)
       1.f,
       1.f,
       Vector3(0.2, 0.1, 0.0),
+      -1.0f,
       0.04f,
       1.0f,
       Vector3::ONE,
@@ -288,6 +292,7 @@ int UtcDaliGltfLoaderSuccess1(void)
       true,
       true,
       false,
+      Scene3D::Material::AlphaModeType::OPAQUE,
       true,
       false,
       {
@@ -337,6 +342,7 @@ int UtcDaliGltfLoaderSuccess1(void)
           },
         },
       },
+      nullptr,
     },
   };
 
@@ -355,12 +361,14 @@ int UtcDaliGltfLoaderSuccess1(void)
     DALI_TEST_EQUAL(md.mNormalScale, m.mNormalScale);
     DALI_TEST_EQUAL(md.mOcclusionStrength, m.mOcclusionStrength);
     DALI_TEST_EQUAL(md.mEmissiveFactor, m.mEmissiveFactor);
+    DALI_TEST_EQUAL(md.mIor, m.mIor);
     DALI_TEST_EQUAL(md.mDielectricSpecular, m.mDielectricSpecular);
     DALI_TEST_EQUAL(md.mSpecularFactor, m.mSpecularFactor);
     DALI_TEST_EQUAL(md.mSpecularColorFactor, m.mSpecularColorFactor);
     DALI_TEST_EQUAL(md.mNeedAlbedoTexture, m.mNeedAlbedoTexture);
     DALI_TEST_EQUAL(md.mNeedMetallicRoughnessTexture, m.mNeedMetallicRoughnessTexture);
     DALI_TEST_EQUAL(md.mNeedNormalTexture, m.mNeedNormalTexture);
+    DALI_TEST_EQUAL(md.mAlphaModeType, m.mAlphaModeType);
     DALI_TEST_EQUAL(md.mIsOpaque, m.mIsOpaque);
     DALI_TEST_EQUAL(md.mIsMask, m.mIsMask);
 
@@ -672,9 +680,9 @@ int UtcDaliGltfLoaderAnimationLoadingTest(void)
   }
 
   DALI_TEST_EQUAL(ctx.loadResult.mAnimationDefinitions.size(), 1u);
-  DALI_TEST_EQUAL(ctx.loadResult.mAnimationDefinitions[0].mProperties.size(), 57u);
+  DALI_TEST_EQUAL(ctx.loadResult.mAnimationDefinitions[0].GetPropertyCount(), 57u);
 
-  uint32_t id = ctx.loadResult.mScene.GetNode(ctx.loadResult.mAnimationDefinitions[0].mProperties[0].mNodeIndex)->mNodeId;
+  uint32_t id = ctx.loadResult.mScene.GetNode(ctx.loadResult.mAnimationDefinitions[0].GetPropertyAt(0).mNodeIndex)->mNodeId;
   DALI_TEST_EQUAL(id, root.FindChildByName("Skeleton_torso_joint_1").GetProperty<int32_t>(Dali::Actor::Property::ID));
 
   END_TEST;
diff --git a/automated-tests/src/dali-scene3d-internal/utc-Dali-MaterialImpl.cpp b/automated-tests/src/dali-scene3d-internal/utc-Dali-MaterialImpl.cpp
new file mode 100644 (file)
index 0000000..088bb6f
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2023 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.
+ *
+ */
+
+#include <toolkit-event-thread-callback.h>
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <stdlib.h>
+#include <iostream>
+
+#include <dali-scene3d/internal/model-components/material-impl.h>
+#include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+void model_components_material_impl_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void model_components_material_impl_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+namespace
+{
+const char* TEST_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/gallery-small-1.jpg";
+} // namespace
+
+// Method test
+int UtcDaliMaterialImplSetGetTextureInformation(void)
+{
+  tet_infoline("UtcDaliMaterialImplSetGetTextureInformation.");
+
+  ToolkitTestApplication application;
+
+  Scene3D::Material material = Scene3D::Material::New();
+
+  Scene3D::Internal::Material::TextureInformation baseColor;
+  Dali::Texture                                   baseColorTexture = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 100, 100);
+  Dali::Sampler                                   baseColorSampler = Dali::Sampler::New();
+  baseColor.mTexture                                               = baseColorTexture;
+  baseColor.mSampler                                               = baseColorSampler;
+  baseColor.mUrl                                                   = "baseColor";
+  GetImplementation(material).SetTextureInformation(Scene3D::Material::TextureType::BASE_COLOR, std::move(baseColor));
+  DALI_TEST_EQUALS(baseColorTexture, material.GetTexture(Scene3D::Material::TextureType::BASE_COLOR), TEST_LOCATION);
+  DALI_TEST_EQUALS(baseColorSampler, material.GetSampler(Scene3D::Material::TextureType::BASE_COLOR), TEST_LOCATION);
+  DALI_TEST_EQUALS("baseColor", material.GetProperty<std::string>(Scene3D::Material::Property::BASE_COLOR_URL), TEST_LOCATION);
+
+  Scene3D::Internal::Material::TextureInformation metallicRoughness;
+  Dali::Texture                                   metallicRoughnessTexture = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 100, 100);
+  Dali::Sampler                                   metallicRoughnessSampler = Dali::Sampler::New();
+  metallicRoughness.mTexture                                               = metallicRoughnessTexture;
+  metallicRoughness.mSampler                                               = metallicRoughnessSampler;
+  metallicRoughness.mUrl                                                   = "metallicRoughness";
+  GetImplementation(material).SetTextureInformation(Scene3D::Material::TextureType::METALLIC_ROUGHNESS, std::move(metallicRoughness));
+  DALI_TEST_EQUALS(metallicRoughnessTexture, material.GetTexture(Scene3D::Material::TextureType::METALLIC_ROUGHNESS), TEST_LOCATION);
+  DALI_TEST_EQUALS(metallicRoughnessSampler, material.GetSampler(Scene3D::Material::TextureType::METALLIC_ROUGHNESS), TEST_LOCATION);
+  DALI_TEST_EQUALS("metallicRoughness", material.GetProperty<std::string>(Scene3D::Material::Property::METALLIC_ROUGHNESS_URL), TEST_LOCATION);
+
+  Scene3D::Internal::Material::TextureInformation normal;
+  Dali::Texture                                   normalTexture = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 100, 100);
+  Dali::Sampler                                   normalSampler = Dali::Sampler::New();
+  normal.mTexture                                               = normalTexture;
+  normal.mSampler                                               = normalSampler;
+  normal.mUrl                                                   = "normal";
+  GetImplementation(material).SetTextureInformation(Scene3D::Material::TextureType::NORMAL, std::move(normal));
+  DALI_TEST_EQUALS(normalTexture, material.GetTexture(Scene3D::Material::TextureType::NORMAL), TEST_LOCATION);
+  DALI_TEST_EQUALS(normalSampler, material.GetSampler(Scene3D::Material::TextureType::NORMAL), TEST_LOCATION);
+  DALI_TEST_EQUALS("normal", material.GetProperty<std::string>(Scene3D::Material::Property::NORMAL_URL), TEST_LOCATION);
+
+  Dali::TextureSet textureSet = GetImplementation(material).GetTextureSet();
+  DALI_TEST_EQUALS(3, textureSet.GetTextureCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(baseColorTexture, textureSet.GetTexture(0u), TEST_LOCATION);
+  DALI_TEST_EQUALS(metallicRoughnessTexture, textureSet.GetTexture(1u), TEST_LOCATION);
+  DALI_TEST_EQUALS(normalTexture, textureSet.GetTexture(2u), TEST_LOCATION);
+  DALI_TEST_EQUALS(baseColorSampler, textureSet.GetSampler(0u), TEST_LOCATION);
+  DALI_TEST_EQUALS(metallicRoughnessSampler, textureSet.GetSampler(1u), TEST_LOCATION);
+
+  DALI_TEST_EQUALS(true, GetImplementation(material).IsResourceReady(), TEST_LOCATION);
+  GetImplementation(material).UpdateMaterialData();
+
+  std::vector<std::string> defines;
+  defines.push_back("THREE_TEX");
+  defines.push_back("GLTF_CHANNELS");
+  defines.push_back("BASECOLOR_TEX");
+  defines.push_back("METALLIC_ROUGHNESS_TEX");
+  defines.push_back("NORMAL_TEX");
+
+  std::string fragmentShader = SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_FRAG.data();
+  for(const auto& define : defines)
+  {
+    Scene3D::Loader::ShaderDefinition::ApplyDefine(fragmentShader, define);
+  }
+
+  DALI_TEST_EQUALS(fragmentShader, GetImplementation(material).GetFragmentShader(), TEST_LOCATION);
+
+  Scene3D ::Internal ::Material ::TextureInformation occlusion;
+  Dali ::Texture                                     occlusiontexture = Dali ::Texture ::New(TextureType ::TEXTURE_2D, Pixel ::RGBA8888, 100, 100);
+  Dali ::Sampler                                     occlusionSampler = Dali ::Sampler ::New();
+  occlusion.mTexture                                                  = occlusiontexture;
+  occlusion.mSampler                                                  = occlusionSampler;
+  occlusion.mUrl                                                      = "occlusion";
+  GetImplementation(material).SetTextureInformation(Scene3D ::Material ::TextureType ::OCCLUSION, std ::move(occlusion));
+  DALI_TEST_EQUALS(occlusiontexture, material.GetTexture(Scene3D ::Material ::TextureType ::OCCLUSION), TEST_LOCATION);
+  DALI_TEST_EQUALS(occlusionSampler, material.GetSampler(Scene3D ::Material ::TextureType ::OCCLUSION), TEST_LOCATION);
+  DALI_TEST_EQUALS("occlusion", material.GetProperty<std ::string>(Scene3D ::Material ::Property ::OCCLUSION_URL), TEST_LOCATION);
+
+  Scene3D::Internal::Material::TextureInformation emissive;
+  Dali::Texture                                   emissiveTexture = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 100, 100);
+  Dali::Sampler                                   emissiveSampler = Dali::Sampler::New();
+  emissive.mTexture                                               = emissiveTexture;
+  emissive.mSampler                                               = emissiveSampler;
+  emissive.mUrl                                                   = "emissive";
+  GetImplementation(material).SetTextureInformation(Scene3D::Material::TextureType::EMISSIVE, std::move(emissive));
+  DALI_TEST_EQUALS(emissiveTexture, material.GetTexture(Scene3D::Material::TextureType::EMISSIVE), TEST_LOCATION);
+  DALI_TEST_EQUALS(emissiveSampler, material.GetSampler(Scene3D::Material::TextureType::EMISSIVE), TEST_LOCATION);
+  DALI_TEST_EQUALS("emissive", material.GetProperty<std::string>(Scene3D::Material::Property::EMISSIVE_URL), TEST_LOCATION);
+
+  Scene3D ::Internal ::Material ::TextureInformation specular;
+  Dali ::Texture                                     specularTexture = Dali ::Texture ::New(TextureType ::TEXTURE_2D, Pixel ::RGBA8888, 100, 100);
+  Dali ::Sampler                                     specularSampler = Dali ::Sampler ::New();
+  specular.mTexture                                                  = specularTexture;
+  specular.mSampler                                                  = specularSampler;
+  specular.mUrl                                                      = "specular";
+  GetImplementation(material).SetTextureInformation(Scene3D ::Material ::TextureType ::SPECULAR, std ::move(specular));
+  DALI_TEST_EQUALS(specularTexture, material.GetTexture(Scene3D ::Material ::TextureType ::SPECULAR), TEST_LOCATION);
+  DALI_TEST_EQUALS(specularSampler, material.GetSampler(Scene3D ::Material ::TextureType ::SPECULAR), TEST_LOCATION);
+  DALI_TEST_EQUALS("specular", material.GetProperty<std ::string>(Scene3D ::Material ::Property ::SPECULAR_URL), TEST_LOCATION);
+
+  Scene3D::Internal::Material::TextureInformation specularColor;
+  Dali::Texture                                   specularColorTexture = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 100, 100);
+  Dali::Sampler                                   specularColorSampler = Dali::Sampler::New();
+  specularColor.mTexture                                               = specularColorTexture;
+  specularColor.mSampler                                               = specularColorSampler;
+  specularColor.mUrl                                                   = "specularColor";
+  GetImplementation(material).SetTextureInformation(Scene3D::Material::TextureType::SPECULAR_COLOR, std::move(specularColor));
+  DALI_TEST_EQUALS(specularColorTexture, material.GetTexture(Scene3D::Material::TextureType::SPECULAR_COLOR), TEST_LOCATION);
+  DALI_TEST_EQUALS(specularColorSampler, material.GetSampler(Scene3D::Material::TextureType::SPECULAR_COLOR), TEST_LOCATION);
+  DALI_TEST_EQUALS("specularColor", material.GetProperty<std::string>(Scene3D::Material::Property::SPECULAR_COLOR_URL), TEST_LOCATION);
+
+  textureSet = GetImplementation(material).GetTextureSet();
+  DALI_TEST_EQUALS(7, textureSet.GetTextureCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(baseColorTexture, textureSet.GetTexture(0u), TEST_LOCATION);
+  DALI_TEST_EQUALS(metallicRoughnessTexture, textureSet.GetTexture(1u), TEST_LOCATION);
+  DALI_TEST_EQUALS(normalTexture, textureSet.GetTexture(2u), TEST_LOCATION);
+  DALI_TEST_EQUALS(occlusiontexture, textureSet.GetTexture(3u), TEST_LOCATION);
+  DALI_TEST_EQUALS(emissiveTexture, textureSet.GetTexture(4u), TEST_LOCATION);
+  DALI_TEST_EQUALS(specularTexture, textureSet.GetTexture(5u), TEST_LOCATION);
+  DALI_TEST_EQUALS(specularColorTexture, textureSet.GetTexture(6u), TEST_LOCATION);
+
+  DALI_TEST_EQUALS(baseColorSampler, textureSet.GetSampler(0u), TEST_LOCATION);
+  DALI_TEST_EQUALS(metallicRoughnessSampler, textureSet.GetSampler(1u), TEST_LOCATION);
+  DALI_TEST_EQUALS(normalSampler, textureSet.GetSampler(2u), TEST_LOCATION);
+  DALI_TEST_EQUALS(occlusionSampler, textureSet.GetSampler(3u), TEST_LOCATION);
+  DALI_TEST_EQUALS(emissiveSampler, textureSet.GetSampler(4u), TEST_LOCATION);
+  DALI_TEST_EQUALS(specularSampler, textureSet.GetSampler(5u), TEST_LOCATION);
+  DALI_TEST_EQUALS(specularColorSampler, textureSet.GetSampler(6u), TEST_LOCATION);
+
+  DALI_TEST_EQUALS(true, GetImplementation(material).IsResourceReady(), TEST_LOCATION);
+  GetImplementation(material).UpdateMaterialData();
+
+  defines.push_back("OCCLUSION");
+  defines.push_back("EMISSIVE");
+  defines.push_back("MATERIAL_SPECULAR_TEXTURE");
+  defines.push_back("MATERIAL_SPECULAR_COLOR_TEXTURE");
+
+  fragmentShader = SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_FRAG.data();
+  for(const auto& define : defines)
+  {
+    Scene3D::Loader::ShaderDefinition::ApplyDefine(fragmentShader, define);
+  }
+
+  DALI_TEST_EQUALS(fragmentShader, GetImplementation(material).GetFragmentShader(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Method test
+int UtcDaliMaterialCheckUniform(void)
+{
+  tet_infoline("UtcDaliMaterialCheckUniform.");
+
+  ToolkitTestApplication application;
+
+  Scene3D::Material material = Scene3D::Material::New();
+
+  Vector4 baseColorFactor(0.1f, 0.2f, 0.3f, 0.4f);
+  material.SetProperty(Scene3D::Material::Property::BASE_COLOR_FACTOR, baseColorFactor);
+  DALI_TEST_EQUALS(baseColorFactor, material.GetProperty<Vector4>(Scene3D::Material::Property::BASE_COLOR_FACTOR), TEST_LOCATION);
+
+  float metallicFactor = 0.5f;
+  material.SetProperty(Scene3D::Material::Property::METALLIC_FACTOR, metallicFactor);
+  DALI_TEST_EQUALS(metallicFactor, material.GetProperty<float>(Scene3D::Material::Property::METALLIC_FACTOR), TEST_LOCATION);
+
+  float roughnessFactor = 0.6f;
+  material.SetProperty(Scene3D::Material::Property::ROUGHNESS_FACTOR, roughnessFactor);
+  DALI_TEST_EQUALS(roughnessFactor, material.GetProperty<float>(Scene3D::Material::Property::ROUGHNESS_FACTOR), TEST_LOCATION);
+
+  float normalScale = 0.7f;
+  material.SetProperty(Scene3D::Material::Property::NORMAL_SCALE, normalScale);
+  DALI_TEST_EQUALS(normalScale, material.GetProperty<float>(Scene3D::Material::Property::NORMAL_SCALE), TEST_LOCATION);
+
+  float occlusionStrength = 0.8f;
+  material.SetProperty(Scene3D::Material::Property::OCCLUSION_STRENGTH, occlusionStrength);
+  DALI_TEST_EQUALS(occlusionStrength, material.GetProperty<float>(Scene3D::Material::Property::OCCLUSION_STRENGTH), TEST_LOCATION);
+
+  Vector3 emissiveFactor(0.9f, 0.1f, 0.2f);
+  material.SetProperty(Scene3D::Material::Property::EMISSIVE_FACTOR, emissiveFactor);
+  DALI_TEST_EQUALS(emissiveFactor, material.GetProperty<Vector3>(Scene3D::Material::Property::EMISSIVE_FACTOR), TEST_LOCATION);
+
+  Dali::Scene3D::Material::AlphaModeType alphaMode = Dali::Scene3D::Material::AlphaModeType::BLEND;
+  material.SetProperty(Scene3D::Material::Property::ALPHA_MODE, alphaMode);
+  DALI_TEST_EQUALS(alphaMode, material.GetProperty<Dali::Scene3D::Material::AlphaModeType>(Scene3D::Material ::Property ::ALPHA_MODE), TEST_LOCATION);
+
+  float alphaCutoff = 0.9f;
+  material.SetProperty(Scene3D ::Material ::Property ::ALPHA_CUTOFF, alphaCutoff);
+  DALI_TEST_EQUALS(alphaCutoff, material.GetProperty<float>(Scene3D ::Material ::Property ::ALPHA_CUTOFF), TEST_LOCATION);
+
+  bool doubleSided = false;
+  material.SetProperty(Scene3D ::Material ::Property ::DOUBLE_SIDED, doubleSided);
+  DALI_TEST_EQUALS(doubleSided, material.GetProperty<bool>(Scene3D ::Material ::Property ::DOUBLE_SIDED), TEST_LOCATION);
+
+  float ior = 1.0f;
+  material.SetProperty(Scene3D::Material::Property::IOR, ior);
+  DALI_TEST_EQUALS(ior, material.GetProperty<float>(Scene3D::Material::Property::IOR), TEST_LOCATION);
+
+  float specularFactor = 1.0f;
+  material.SetProperty(Scene3D::Material::Property::SPECULAR_FACTOR, specularFactor);
+  DALI_TEST_EQUALS(specularFactor, material.GetProperty<float>(Scene3D::Material::Property::SPECULAR_FACTOR), TEST_LOCATION);
+
+  Vector3 specularColorFactor(1.0f, 1.0f, 1.0f);
+  material.SetProperty(Scene3D::Material::Property::SPECULAR_COLOR_FACTOR, specularColorFactor);
+  DALI_TEST_EQUALS(specularColorFactor, material.GetProperty<Vector3>(Scene3D::Material::Property::SPECULAR_COLOR_FACTOR), TEST_LOCATION);
+
+  DALI_TEST_EQUALS(true, GetImplementation(material).IsResourceReady(), TEST_LOCATION);
+  GetImplementation(material).UpdateMaterialData();
+
+  Dali::Shader   shader   = Dali::Shader::New(SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_VERT.data(), SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_FRAG.data());
+  Dali::Geometry geometry = Dali::Geometry::New();
+  Dali::Renderer renderer = Dali::Renderer::New(geometry, shader);
+
+  GetImplementation(material).SetRendererUniform(renderer);
+
+  DALI_TEST_EQUALS(baseColorFactor, renderer.GetProperty<Vector4>(renderer.GetPropertyIndex("uColorFactor")), TEST_LOCATION);
+  DALI_TEST_EQUALS(metallicFactor, renderer.GetProperty<float>(renderer.GetPropertyIndex("uMetallicFactor")), TEST_LOCATION);
+  DALI_TEST_EQUALS(roughnessFactor, renderer.GetProperty<float>(renderer.GetPropertyIndex("uRoughnessFactor")), TEST_LOCATION);
+  DALI_TEST_EQUALS(normalScale, renderer.GetProperty<float>(renderer.GetPropertyIndex("uNormalScale")), TEST_LOCATION);
+  DALI_TEST_EQUALS(emissiveFactor, renderer.GetProperty<Vector3>(renderer.GetPropertyIndex("uEmissiveFactor")), TEST_LOCATION);
+  float dielectricSpecular = (Dali::Equals(ior, -1.0)) ? 0.04f : powf((ior - 1.0f) / (ior + 1.0f), 2.0f);
+  DALI_TEST_EQUALS(dielectricSpecular, renderer.GetProperty<float>(renderer.GetPropertyIndex("uDielectricSpecular")), TEST_LOCATION);
+  DALI_TEST_EQUALS(specularFactor, renderer.GetProperty<float>(renderer.GetPropertyIndex("uSpecularFactor")), TEST_LOCATION);
+  DALI_TEST_EQUALS(specularColorFactor, renderer.GetProperty<Vector3>(renderer.GetPropertyIndex("uSpecularColorFactor")), TEST_LOCATION);
+  float opaque = alphaMode == Dali::Scene3D::Material::AlphaModeType::BLEND ? 0.0f : 1.0f;
+  float mask   = alphaMode == Dali::Scene3D::Material::AlphaModeType::MASK ? 1.0f : 0.0f;
+  DALI_TEST_EQUALS(opaque, renderer.GetProperty<float>(renderer.GetPropertyIndex("uOpaque")), TEST_LOCATION);
+  DALI_TEST_EQUALS(mask, renderer.GetProperty<float>(renderer.GetPropertyIndex("uMask")), TEST_LOCATION);
+  DALI_TEST_EQUALS(alphaCutoff, renderer.GetProperty<float>(renderer.GetPropertyIndex("uAlphaThreshold")), TEST_LOCATION);
+
+  DALI_TEST_EQUALS(DepthTestMode::ON, renderer.GetProperty<DepthTestMode::Type>(Dali::Renderer::Property::DEPTH_TEST_MODE), TEST_LOCATION);
+  DALI_TEST_EQUALS(FaceCullingMode::BACK, renderer.GetProperty<FaceCullingMode::Type>(Dali::Renderer::Property::FACE_CULLING_MODE), TEST_LOCATION);
+  DALI_TEST_EQUALS(DepthTestMode::ON, renderer.GetProperty<DepthTestMode::Type>(Dali::Renderer::Property::DEPTH_TEST_MODE), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliMaterialImplLoadTexture_1(void)
+{
+  tet_infoline("UtcDaliMaterialLoadTexture.");
+
+  ToolkitTestApplication application;
+
+  Scene3D::Material material = Scene3D::Material::New();
+
+  DALI_TEST_EQUALS(true, GetImplementation(material).IsResourceReady(), TEST_LOCATION);
+  DALI_TEST_CHECK(!material.GetTexture(Scene3D::Material::TextureType::BASE_COLOR));
+  material.SetProperty(Scene3D::Material::Property::BASE_COLOR_URL, TEST_IMAGE_FILE_NAME);
+  DALI_TEST_EQUALS(false, GetImplementation(material).IsResourceReady(), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(16);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(16);
+
+  DALI_TEST_EQUALS(true, GetImplementation(material).IsResourceReady(), TEST_LOCATION);
+  DALI_TEST_CHECK(material.GetTexture(Scene3D::Material::TextureType::BASE_COLOR));
+  Dali::Texture baseColorTexture = material.GetTexture(Scene3D::Material::TextureType::BASE_COLOR);
+
+  Dali::TextureSet textureSet = GetImplementation(material).GetTextureSet();
+  DALI_TEST_EQUALS(1u, textureSet.GetTextureCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(baseColorTexture, textureSet.GetTexture(0u), TEST_LOCATION);
+  DALI_TEST_CHECK(textureSet.GetSampler(0u));
+
+  material.SetProperty(Scene3D::Material::Property::BASE_COLOR_URL, TEST_IMAGE_FILE_NAME);
+  DALI_TEST_EQUALS(true, GetImplementation(material).IsResourceReady(), TEST_LOCATION);
+  DALI_TEST_EQUALS(baseColorTexture, material.GetTexture(Scene3D::Material::TextureType::BASE_COLOR), TEST_LOCATION);
+
+  material.SetProperty(Scene3D::Material::Property::BASE_COLOR_URL, "");
+  DALI_TEST_CHECK(!material.GetTexture(Scene3D::Material::TextureType::BASE_COLOR));
+
+  END_TEST;
+}
+
+int UtcDaliMaterialImplLoadTexture_2(void)
+{
+  tet_infoline("UtcDaliMaterialImplLoadTexture_2.");
+
+  ToolkitTestApplication application;
+
+  Scene3D::Material material = Scene3D::Material::New();
+
+  DALI_TEST_EQUALS(true, GetImplementation(material).IsResourceReady(), TEST_LOCATION);
+  DALI_TEST_CHECK(!material.GetTexture(Scene3D::Material::TextureType::BASE_COLOR));
+  material.SetProperty(Scene3D::Material::Property::BASE_COLOR_URL, TEST_IMAGE_FILE_NAME);
+  DALI_TEST_EQUALS(false, GetImplementation(material).IsResourceReady(), TEST_LOCATION);
+  material.SetProperty(Scene3D::Material::Property::BASE_COLOR_URL, "");
+  DALI_TEST_EQUALS(true, GetImplementation(material).IsResourceReady(), TEST_LOCATION);
+  DALI_TEST_CHECK(!material.GetTexture(Scene3D::Material::TextureType::BASE_COLOR));
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene3d-internal/utc-Dali-ModelPrimitiveImpl.cpp b/automated-tests/src/dali-scene3d-internal/utc-Dali-ModelPrimitiveImpl.cpp
new file mode 100644 (file)
index 0000000..00dba92
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2023 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.
+ *
+ */
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <stdlib.h>
+#include <iostream>
+
+#include <dali-scene3d/internal/model-components/model-primitive-impl.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+void model_components_model_primitive_impl_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void model_components_model_primitive_impl_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+namespace
+{
+} // namespace
+
+// Method test
+int UtcDaliModelPrimitiveImplSetData(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::ModelPrimitive modelPrimitive = Scene3D::ModelPrimitive::New();
+
+  tet_printf("Check primitive don't have material initial time\n");
+
+  DALI_TEST_CHECK(!GetImplementation(modelPrimitive).GetRenderer());
+  DALI_TEST_CHECK(!modelPrimitive.GetGeometry());
+  DALI_TEST_CHECK(!modelPrimitive.GetMaterial());
+
+  Dali::Geometry geometry = Dali::Geometry::New();
+  Dali::Scene3D::Material material = Dali::Scene3D::Material::New();
+
+  modelPrimitive.SetGeometry(geometry);
+  modelPrimitive.SetMaterial(material);
+  DALI_TEST_CHECK(geometry == modelPrimitive.GetGeometry());
+  DALI_TEST_CHECK(material == modelPrimitive.GetMaterial());
+  DALI_TEST_CHECK(GetImplementation(modelPrimitive).GetRenderer());
+
+  END_TEST;
+}
\ No newline at end of file
index 799dd6d..41a55c0 100755 (executable)
@@ -16,7 +16,10 @@ SET(TC_SOURCES
   utc-Dali-EnvironmentDefinition.cpp
   utc-Dali-FacialAnimation.cpp
   utc-Dali-KtxLoader.cpp
+  utc-Dali-Material.cpp
   utc-Dali-Model.cpp
+  utc-Dali-ModelNode.cpp
+  utc-Dali-ModelPrimitive.cpp
   utc-Dali-SceneView.cpp
   utc-Dali-MatrixStack.cpp
   utc-Dali-MeshDefinition.cpp
index 99b0aaf..a2e052c 100644 (file)
@@ -48,15 +48,16 @@ int UtcDaliAnimationDefinitionReAnimate(void)
     return actor.FindChildByName(property.mNodeName);
   };
 
+  uint32_t animatedPropertyIndex = 0;
   for (bool b: { false, true })
   {
     AnimationDefinition animDef;
-    animDef.mName = "WalkRight";
-    animDef.mDuration = 10.f;
-    animDef.mLoopCount = 2;
-    animDef.mEndAction = Animation::BAKE_FINAL;
-    animDef.mSpeedFactor = .7f;
-    animDef.mProperties.push_back(AnimatedProperty{
+    animDef.SetName("WalkRight");
+    animDef.SetDuration(10.f);
+    animDef.SetLoopCount(2);
+    animDef.SetEndAction(Animation::BAKE_FINAL);
+    animDef.SetSpeedFactor(.7f);
+    animDef.SetProperty(animatedPropertyIndex++,std::move(AnimatedProperty{
      INVALID_INDEX,
      "ChristopherPlummer",
      "position",
@@ -66,14 +67,14 @@ int UtcDaliAnimationDefinitionReAnimate(void)
        b
      } },
      AlphaFunction::EASE_OUT,
-     TimePeriod(animDef.mDuration)
-    });
+     TimePeriod(animDef.GetDuration())
+    }));
 
     auto anim = animDef.ReAnimate(getActor);
-    DALI_TEST_EQUAL(anim.GetDuration(), animDef.mDuration);
-    DALI_TEST_EQUAL(anim.GetEndAction(), animDef.mEndAction);
-    DALI_TEST_EQUAL(anim.GetSpeedFactor(), animDef.mSpeedFactor);
-    DALI_TEST_EQUAL(anim.GetLoopCount(), animDef.mLoopCount);
+    DALI_TEST_EQUAL(anim.GetDuration(), animDef.GetDuration());
+    DALI_TEST_EQUAL(anim.GetEndAction(), animDef.GetEndAction());
+    DALI_TEST_EQUAL(anim.GetSpeedFactor(), animDef.GetSpeedFactor());
+    DALI_TEST_EQUAL(anim.GetLoopCount(), animDef.GetLoopCount());
   }
 
   END_TEST;
@@ -93,27 +94,28 @@ int UtcDaliAnimationDefinitionReAnimateKeyFrames(void)
   kf.Add(0.f, Vector3::ZERO);
   kf.Add(1.f, Vector3::XAXIS * 100.f);
 
+  uint32_t animatedPropertyIndex = 0;
   AnimationDefinition animDef;
-  animDef.mName = "WalkRight";
-  animDef.mDuration = 10.f;
-  animDef.mLoopCount = 2;
-  animDef.mEndAction = Animation::BAKE_FINAL;
-  animDef.mSpeedFactor = .7f;
-  animDef.mProperties.push_back(AnimatedProperty{
+  animDef.SetName("WalkRight");
+  animDef.SetDuration(10.f);
+  animDef.SetLoopCount(2);
+  animDef.SetEndAction(Animation::BAKE_FINAL);
+  animDef.SetSpeedFactor(.7f);
+  animDef.SetProperty(animatedPropertyIndex++, std::move(AnimatedProperty{
    INVALID_INDEX,
    "ChristopherPlummer",
    "position",
    kf,
    nullptr,
    AlphaFunction::EASE_OUT,
-   TimePeriod(animDef.mDuration)
-  });
+   TimePeriod(animDef.GetDuration())
+  }));
 
   auto anim = animDef.ReAnimate(getActor);
-  DALI_TEST_EQUAL(anim.GetDuration(), animDef.mDuration);
-  DALI_TEST_EQUAL(anim.GetEndAction(), animDef.mEndAction);
-  DALI_TEST_EQUAL(anim.GetSpeedFactor(), animDef.mSpeedFactor);
-  DALI_TEST_EQUAL(anim.GetLoopCount(), animDef.mLoopCount);
+  DALI_TEST_EQUAL(anim.GetDuration(), animDef.GetDuration());
+  DALI_TEST_EQUAL(anim.GetEndAction(), animDef.GetEndAction());
+  DALI_TEST_EQUAL(anim.GetSpeedFactor(), animDef.GetSpeedFactor());
+  DALI_TEST_EQUAL(anim.GetLoopCount(), animDef.GetLoopCount());
 
   END_TEST;
 }
index db5f525..2315bfd 100644 (file)
@@ -27,28 +27,28 @@ int UtcDaliLoadBvh(void)
 
   AnimationDefinition animDef = LoadBvh(TEST_RESOURCE_DIR "/test.bvh", "testBvh");
 
-  DALI_TEST_EQUAL(animDef.mName, "testBvh");
-  DALI_TEST_EQUAL(animDef.mDuration, 0.3f);
+  DALI_TEST_EQUAL(animDef.GetName(), "testBvh");
+  DALI_TEST_EQUAL(animDef.GetDuration(), 0.3f);
 
-  DALI_TEST_EQUAL(animDef.mProperties[0].mNodeName, "root");
-  DALI_TEST_EQUAL(animDef.mProperties[0].mPropertyName, "position");
-  DALI_TEST_EQUAL(animDef.mProperties[0].mKeyFrames.GetType(), Property::Type::VECTOR3);
-  DALI_TEST_EQUAL(animDef.mProperties[0].mTimePeriod.durationSeconds, 0.3f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mNodeName, "root");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mPropertyName, "position");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mKeyFrames.GetType(), Property::Type::VECTOR3);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mTimePeriod.durationSeconds, 0.3f);
 
-  DALI_TEST_EQUAL(animDef.mProperties[1].mNodeName, "root");
-  DALI_TEST_EQUAL(animDef.mProperties[1].mPropertyName, "orientation");
-  DALI_TEST_EQUAL(animDef.mProperties[1].mKeyFrames.GetType(), Property::Type::ROTATION);
-  DALI_TEST_EQUAL(animDef.mProperties[1].mTimePeriod.durationSeconds, 0.3f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(1).mNodeName, "root");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(1).mPropertyName, "orientation");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(1).mKeyFrames.GetType(), Property::Type::ROTATION);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(1).mTimePeriod.durationSeconds, 0.3f);
 
-  DALI_TEST_EQUAL(animDef.mProperties[2].mNodeName, "first");
-  DALI_TEST_EQUAL(animDef.mProperties[2].mPropertyName, "position");
-  DALI_TEST_EQUAL(animDef.mProperties[2].mKeyFrames.GetType(), Property::Type::VECTOR3);
-  DALI_TEST_EQUAL(animDef.mProperties[2].mTimePeriod.durationSeconds, 0.3f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(2).mNodeName, "first");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(2).mPropertyName, "position");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(2).mKeyFrames.GetType(), Property::Type::VECTOR3);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(2).mTimePeriod.durationSeconds, 0.3f);
 
-  DALI_TEST_EQUAL(animDef.mProperties[3].mNodeName, "first");
-  DALI_TEST_EQUAL(animDef.mProperties[3].mPropertyName, "orientation");
-  DALI_TEST_EQUAL(animDef.mProperties[3].mKeyFrames.GetType(), Property::Type::ROTATION);
-  DALI_TEST_EQUAL(animDef.mProperties[3].mTimePeriod.durationSeconds, 0.3f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(3).mNodeName, "first");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(3).mPropertyName, "orientation");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(3).mKeyFrames.GetType(), Property::Type::ROTATION);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(3).mTimePeriod.durationSeconds, 0.3f);
 
   Actor root = Actor::New();
   root.SetProperty(Actor::Property::NAME, "root");
@@ -62,7 +62,7 @@ int UtcDaliLoadBvh(void)
   };
 
   Animation animation = animDef.ReAnimate(getActor);
-  DALI_TEST_EQUAL(animation.GetDuration(), animDef.mDuration);
+  DALI_TEST_EQUAL(animation.GetDuration(), animDef.GetDuration());
 
   application.GetScene().Add(root);
 
@@ -98,6 +98,6 @@ int UtcDaliLoadBvhFailed(void)
   TestApplication application;
 
   AnimationDefinition animDef = LoadBvh("/nothing.bvh", "testBvh");
-  DALI_TEST_EQUALS(0u, animDef.mProperties.size(), TEST_LOCATION);
+  DALI_TEST_EQUALS(0u, animDef.GetPropertyCount(), TEST_LOCATION);
   END_TEST;
 }
index 2e97dc9..100cd72 100644 (file)
@@ -28,42 +28,43 @@ int UtcDaliLoadFacialAnimation(void)
 
   AnimationDefinition animDef = LoadFacialAnimation(TEST_RESOURCE_DIR "/facial-blendshape-animation.json");
 
-  DALI_TEST_EQUAL(animDef.mName, "Facial_Blendshape_Animation");
-  DALI_TEST_EQUAL(animDef.mDuration, 14.966001f);
-  DALI_TEST_EQUAL(animDef.mEndAction, Animation::BAKE);
-  DALI_TEST_EQUAL(animDef.mSpeedFactor, 1.0f);
-  DALI_TEST_EQUAL(animDef.mLoopCount, 1);
-  DALI_TEST_EQUAL(animDef.mProperties.size(), 122);
+  std::string name = animDef.GetName();
+  DALI_TEST_EQUAL(name, "Facial_Blendshape_Animation");
+  DALI_TEST_EQUAL(animDef.GetDuration(), 14.966001f);
+  DALI_TEST_EQUAL(animDef.GetEndAction(), Animation::BAKE);
+  DALI_TEST_EQUAL(animDef.GetSpeedFactor(), 1.0f);
+  DALI_TEST_EQUAL(animDef.GetLoopCount(), 1);
+  DALI_TEST_EQUAL(animDef.GetPropertyCount(), 122);
 
-  DALI_TEST_EQUAL(animDef.mProperties[0].mNodeName, "GEO_1");
-  DALI_TEST_EQUAL(animDef.mProperties[0].mPropertyName, "uBlendShapeWeight[0]");
-  DALI_TEST_EQUAL(animDef.mProperties[0].mKeyFrames.GetType(), Property::Type::FLOAT);
-  DALI_TEST_EQUAL(animDef.mProperties[0].mTimePeriod.delaySeconds, 0.0f);
-  DALI_TEST_EQUAL(animDef.mProperties[0].mTimePeriod.durationSeconds, 14.966001f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mNodeName, "GEO_1");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mPropertyName, "uBlendShapeWeight[0]");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mKeyFrames.GetType(), Property::Type::FLOAT);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mTimePeriod.delaySeconds, 0.0f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(0).mTimePeriod.durationSeconds, 14.966001f);
 
-  DALI_TEST_EQUAL(animDef.mProperties[69].mNodeName, "GEO_2");
-  DALI_TEST_EQUAL(animDef.mProperties[69].mPropertyName, "uBlendShapeWeight[1]");
-  DALI_TEST_EQUAL(animDef.mProperties[69].mKeyFrames.GetType(), Property::Type::FLOAT);
-  DALI_TEST_EQUAL(animDef.mProperties[69].mTimePeriod.delaySeconds, 0.0f);
-  DALI_TEST_EQUAL(animDef.mProperties[69].mTimePeriod.durationSeconds, 14.966001f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mNodeName, "GEO_2");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mPropertyName, "uBlendShapeWeight[1]");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mKeyFrames.GetType(), Property::Type::FLOAT);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mTimePeriod.delaySeconds, 0.0f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(69).mTimePeriod.durationSeconds, 14.966001f);
 
-  DALI_TEST_EQUAL(animDef.mProperties[86].mNodeName, "GEO_3");
-  DALI_TEST_EQUAL(animDef.mProperties[86].mPropertyName, "uBlendShapeWeight[2]");
-  DALI_TEST_EQUAL(animDef.mProperties[86].mKeyFrames.GetType(), Property::Type::FLOAT);
-  DALI_TEST_EQUAL(animDef.mProperties[86].mTimePeriod.delaySeconds, 0.0f);
-  DALI_TEST_EQUAL(animDef.mProperties[86].mTimePeriod.durationSeconds, 14.966001f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mNodeName, "GEO_3");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mPropertyName, "uBlendShapeWeight[2]");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mKeyFrames.GetType(), Property::Type::FLOAT);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mTimePeriod.delaySeconds, 0.0f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(86).mTimePeriod.durationSeconds, 14.966001f);
 
-  DALI_TEST_EQUAL(animDef.mProperties[100].mNodeName, "GEO_4");
-  DALI_TEST_EQUAL(animDef.mProperties[100].mPropertyName, "uBlendShapeWeight[7]");
-  DALI_TEST_EQUAL(animDef.mProperties[100].mKeyFrames.GetType(), Property::Type::FLOAT);
-  DALI_TEST_EQUAL(animDef.mProperties[100].mTimePeriod.delaySeconds, 0.0f);
-  DALI_TEST_EQUAL(animDef.mProperties[100].mTimePeriod.durationSeconds, 14.966001f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mNodeName, "GEO_4");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mPropertyName, "uBlendShapeWeight[7]");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mKeyFrames.GetType(), Property::Type::FLOAT);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mTimePeriod.delaySeconds, 0.0f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(100).mTimePeriod.durationSeconds, 14.966001f);
 
-  DALI_TEST_EQUAL(animDef.mProperties[121].mNodeName, "GEO_5");
-  DALI_TEST_EQUAL(animDef.mProperties[121].mPropertyName, "uBlendShapeWeight[19]");
-  DALI_TEST_EQUAL(animDef.mProperties[121].mKeyFrames.GetType(), Property::Type::FLOAT);
-  DALI_TEST_EQUAL(animDef.mProperties[121].mTimePeriod.delaySeconds, 0.0f);
-  DALI_TEST_EQUAL(animDef.mProperties[121].mTimePeriod.durationSeconds, 14.966001f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mNodeName, "GEO_5");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mPropertyName, "uBlendShapeWeight[19]");
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mKeyFrames.GetType(), Property::Type::FLOAT);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mTimePeriod.delaySeconds, 0.0f);
+  DALI_TEST_EQUAL(animDef.GetPropertyAt(121).mTimePeriod.durationSeconds, 14.966001f);
 
   auto actor = Actor::New();
   actor.SetProperty(Actor::Property::NAME, "GEO_1");
@@ -82,10 +83,10 @@ int UtcDaliLoadFacialAnimation(void)
   };
 
   auto anim = animDef.ReAnimate(getActor);
-  DALI_TEST_EQUAL(anim.GetDuration(), animDef.mDuration);
-  DALI_TEST_EQUAL(anim.GetEndAction(), animDef.mEndAction);
-  DALI_TEST_EQUAL(anim.GetSpeedFactor(), animDef.mSpeedFactor);
-  DALI_TEST_EQUAL(anim.GetLoopCount(), animDef.mLoopCount);
+  DALI_TEST_EQUAL(anim.GetDuration(), animDef.GetDuration());
+  DALI_TEST_EQUAL(anim.GetEndAction(), animDef.GetEndAction());
+  DALI_TEST_EQUAL(anim.GetSpeedFactor(), animDef.GetSpeedFactor());
+  DALI_TEST_EQUAL(anim.GetLoopCount(), animDef.GetLoopCount());
 
   END_TEST;
 }
diff --git a/automated-tests/src/dali-scene3d/utc-Dali-Material.cpp b/automated-tests/src/dali-scene3d/utc-Dali-Material.cpp
new file mode 100644 (file)
index 0000000..c3fcdd0
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2023 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.
+ *
+ */
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <stdlib.h>
+#include <iostream>
+
+#include <dali-scene3d/public-api/model-components/material.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+void model_components_material_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void model_components_material_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+namespace
+{
+} // namespace
+
+// Positive test case for a method
+int UtcDaliMaterialNew(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliMaterialNew");
+
+  Scene3D::Material material = Scene3D::Material::New();
+  DALI_TEST_CHECK(material);
+  END_TEST;
+}
+
+// Positive test case for a method
+int UtcDaliMaterialDownCast(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliMaterialDownCast");
+
+  Scene3D::Material material = Scene3D::Material::New();
+  BaseHandle        handle(material);
+
+  Scene3D::Material material2 = Scene3D::Material::DownCast(handle);
+  DALI_TEST_CHECK(material);
+  DALI_TEST_CHECK(material2);
+  DALI_TEST_CHECK(material2 == material);
+  END_TEST;
+}
+
+int UtcDaliMaterialTypeRegistry(void)
+{
+  ToolkitTestApplication application;
+
+  TypeRegistry typeRegistry = TypeRegistry::Get();
+  DALI_TEST_CHECK(typeRegistry);
+
+  TypeInfo typeInfo = typeRegistry.GetTypeInfo("Material");
+  DALI_TEST_CHECK(typeInfo);
+
+  BaseHandle handle = typeInfo.CreateInstance();
+  DALI_TEST_CHECK(handle);
+
+  Scene3D::Material mateiral = Scene3D::Material::DownCast(handle);
+  DALI_TEST_CHECK(mateiral);
+
+  END_TEST;
+}
+
+int UtcDaliMaterialCopyAndAssignment(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::Material mateiral = Scene3D::Material::New();
+  DALI_TEST_CHECK(mateiral);
+
+  Scene3D::Material copy(mateiral);
+  DALI_TEST_CHECK(mateiral == copy);
+
+  Scene3D::Material assign;
+  DALI_TEST_CHECK(!assign);
+
+  assign = copy;
+  DALI_TEST_CHECK(assign == mateiral);
+
+  END_TEST;
+}
+
+int UtcDaliMaterialMoveConstructor(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::Material mateiral = Scene3D::Material::New();
+  DALI_TEST_EQUALS(1, mateiral.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+
+  Scene3D::Material moved = std::move(mateiral);
+  DALI_TEST_CHECK(moved);
+  DALI_TEST_EQUALS(1, moved.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+  DALI_TEST_CHECK(!mateiral);
+
+  END_TEST;
+}
+
+int UtcDaliMaterialMoveAssignment(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::Material mateiral = Scene3D::Material::New();
+  DALI_TEST_EQUALS(1, mateiral.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+
+  Scene3D::Material moved;
+  moved = std::move(mateiral);
+  DALI_TEST_CHECK(moved);
+  DALI_TEST_EQUALS(1, moved.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+  DALI_TEST_CHECK(!mateiral);
+
+  END_TEST;
+}
+
+// Method test
+int UtcDaliMaterialSetGetProperty(void)
+{
+  tet_infoline("UtcDaliMaterialSetGetProperty.");
+
+  ToolkitTestApplication application;
+
+  Scene3D::Material material = Scene3D::Material::New();
+
+  std::string name = "name";
+  material.SetProperty(Scene3D::Material::Property::NAME, name);
+  DALI_TEST_EQUALS(name, material.GetProperty<std::string>(Scene3D::Material::Property::NAME), TEST_LOCATION);
+
+  std::string baseColorUrl = "baseColorUrl";
+  material.SetProperty(Scene3D::Material::Property::BASE_COLOR_URL, baseColorUrl);
+  DALI_TEST_EQUALS(baseColorUrl, material.GetProperty<std::string>(Scene3D::Material::Property::BASE_COLOR_URL), TEST_LOCATION);
+
+  Vector4 baseColorFactor(0.1f, 0.2f, 0.3f, 0.4f);
+  material.SetProperty(Scene3D::Material::Property::BASE_COLOR_FACTOR, baseColorFactor);
+  DALI_TEST_EQUALS(baseColorFactor, material.GetProperty<Vector4>(Scene3D::Material::Property::BASE_COLOR_FACTOR), TEST_LOCATION);
+
+  std::string metallicRoughnessUrl = "metallicRoughnessUrl";
+  material.SetProperty(Scene3D::Material::Property::METALLIC_ROUGHNESS_URL, metallicRoughnessUrl);
+  DALI_TEST_EQUALS(metallicRoughnessUrl, material.GetProperty<std::string>(Scene3D::Material::Property::METALLIC_ROUGHNESS_URL), TEST_LOCATION);
+
+  float metallicFactor = 0.5f;
+  material.SetProperty(Scene3D::Material::Property::METALLIC_FACTOR, metallicFactor);
+  DALI_TEST_EQUALS(metallicFactor, material.GetProperty<float>(Scene3D::Material::Property::METALLIC_FACTOR), TEST_LOCATION);
+
+  float roughnessFactor = 0.6f;
+  material.SetProperty(Scene3D::Material::Property::ROUGHNESS_FACTOR, roughnessFactor);
+  DALI_TEST_EQUALS(roughnessFactor, material.GetProperty<float>(Scene3D::Material::Property::ROUGHNESS_FACTOR), TEST_LOCATION);
+
+  std::string normalUrl = "normalUrl";
+  material.SetProperty(Scene3D::Material::Property::NORMAL_URL, normalUrl);
+  DALI_TEST_EQUALS(normalUrl, material.GetProperty<std::string>(Scene3D::Material::Property::NORMAL_URL), TEST_LOCATION);
+
+  float normalScale = 0.7f;
+  material.SetProperty(Scene3D::Material::Property::NORMAL_SCALE, normalScale);
+  DALI_TEST_EQUALS(normalScale, material.GetProperty<float>(Scene3D::Material::Property::NORMAL_SCALE), TEST_LOCATION);
+
+  std::string occlusionUrl = "occlusionUrl";
+  material.SetProperty(Scene3D::Material::Property::OCCLUSION_URL, occlusionUrl);
+  DALI_TEST_EQUALS(occlusionUrl, material.GetProperty<std::string>(Scene3D::Material::Property::OCCLUSION_URL), TEST_LOCATION);
+
+  float occlusionStrength = 0.8f;
+  material.SetProperty(Scene3D::Material::Property::OCCLUSION_STRENGTH, occlusionStrength);
+  DALI_TEST_EQUALS(occlusionStrength, material.GetProperty<float>(Scene3D::Material::Property::OCCLUSION_STRENGTH), TEST_LOCATION);
+
+  std::string emissiveUrl = "emissiveUrl";
+  material.SetProperty(Scene3D::Material::Property::EMISSIVE_URL, emissiveUrl);
+  DALI_TEST_EQUALS(emissiveUrl, material.GetProperty<std::string>(Scene3D::Material::Property::EMISSIVE_URL), TEST_LOCATION);
+
+  Vector3 emissiveFactor(0.9f, 0.1f, 0.2f);
+  material.SetProperty(Scene3D::Material::Property::EMISSIVE_FACTOR, emissiveFactor);
+  DALI_TEST_EQUALS(emissiveFactor, material.GetProperty<Vector3>(Scene3D::Material::Property::EMISSIVE_FACTOR), TEST_LOCATION);
+
+  int alphaMode = 1;
+  material.SetProperty(Scene3D::Material::Property::ALPHA_MODE, alphaMode);
+  DALI_TEST_EQUALS(alphaMode, material.GetProperty<int>(Scene3D::Material ::Property ::ALPHA_MODE), TEST_LOCATION);
+
+  float alphaCutoff = 0.9f;
+  material.SetProperty(Scene3D ::Material ::Property ::ALPHA_CUTOFF, alphaCutoff);
+  DALI_TEST_EQUALS(alphaCutoff, material.GetProperty<float>(Scene3D ::Material ::Property ::ALPHA_CUTOFF), TEST_LOCATION);
+
+  bool doubleSided = true;
+  material.SetProperty(Scene3D ::Material ::Property ::DOUBLE_SIDED, doubleSided);
+  DALI_TEST_EQUALS(doubleSided, material.GetProperty<bool>(Scene3D ::Material ::Property ::DOUBLE_SIDED), TEST_LOCATION);
+
+  float ior = 1.0f;
+  material.SetProperty(Scene3D::Material::Property::IOR, ior);
+  DALI_TEST_EQUALS(ior, material.GetProperty<float>(Scene3D::Material::Property::IOR), TEST_LOCATION);
+
+  std::string specularUrl = "specularUrl";
+  material.SetProperty(Scene3D::Material::Property::SPECULAR_URL, specularUrl);
+  DALI_TEST_EQUALS(specularUrl, material.GetProperty<std::string>(Scene3D::Material::Property::SPECULAR_URL), TEST_LOCATION);
+
+  float specularFactor = 1.0f;
+  material.SetProperty(Scene3D::Material::Property::SPECULAR_FACTOR, specularFactor);
+  DALI_TEST_EQUALS(specularFactor, material.GetProperty<float>(Scene3D::Material::Property::SPECULAR_FACTOR), TEST_LOCATION);
+
+  std::string specularColorUrl = "specularColorUrl";
+  material.SetProperty(Scene3D::Material::Property::SPECULAR_COLOR_URL, specularColorUrl);
+  DALI_TEST_EQUALS(specularColorUrl, material.GetProperty<std::string>(Scene3D::Material::Property::SPECULAR_COLOR_URL), TEST_LOCATION);
+
+  Vector3 specularColorFactor(1.0f, 1.0f, 1.0f);
+  material.SetProperty(Scene3D::Material::Property::SPECULAR_COLOR_FACTOR, specularColorFactor);
+  DALI_TEST_EQUALS(specularColorFactor, material.GetProperty<Vector3>(Scene3D::Material::Property::SPECULAR_COLOR_FACTOR), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliMaterialSetGetTexture(void)
+{
+  tet_infoline("UtcDaliMaterialSetGetTexture.");
+
+  ToolkitTestApplication application;
+
+  Scene3D::Material material = Scene3D::Material::New();
+
+  Dali::Texture baseColor = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 100, 100);
+  material.SetTexture(Scene3D::Material::TextureType::BASE_COLOR, baseColor);
+  DALI_TEST_EQUALS(baseColor, material.GetTexture(Scene3D::Material::TextureType::BASE_COLOR), TEST_LOCATION);
+
+  Dali::Texture metallicRoughness = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 100, 100);
+  material.SetTexture(Scene3D::Material::TextureType::METALLIC_ROUGHNESS, metallicRoughness);
+  DALI_TEST_EQUALS(metallicRoughness, material.GetTexture(Scene3D::Material::TextureType::METALLIC_ROUGHNESS), TEST_LOCATION);
+
+  Dali::Texture normal = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 100, 100);
+  material.SetTexture(Scene3D::Material::TextureType::NORMAL, normal);
+  DALI_TEST_EQUALS(normal, material.GetTexture(Scene3D::Material::TextureType::NORMAL), TEST_LOCATION);
+
+  Dali::Texture occlusion = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 100, 100);
+  material.SetTexture(Scene3D::Material::TextureType::OCCLUSION, occlusion);
+  DALI_TEST_EQUALS(occlusion, material.GetTexture(Scene3D::Material::TextureType::OCCLUSION), TEST_LOCATION);
+
+  Dali::Texture emissive = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 100, 100);
+  material.SetTexture(Scene3D::Material::TextureType::EMISSIVE, emissive);
+  DALI_TEST_EQUALS(emissive, material.GetTexture(Scene3D::Material::TextureType::EMISSIVE), TEST_LOCATION);
+
+  Dali::Texture specular = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 100, 100);
+  material.SetTexture(Scene3D::Material::TextureType::SPECULAR, specular);
+  DALI_TEST_EQUALS(specular, material.GetTexture(Scene3D::Material::TextureType::SPECULAR), TEST_LOCATION);
+
+  Dali::Texture specularColor = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 100, 100);
+  material.SetTexture(Scene3D::Material::TextureType::SPECULAR_COLOR, specularColor);
+  DALI_TEST_EQUALS(specularColor, material.GetTexture(Scene3D::Material::TextureType::SPECULAR_COLOR), TEST_LOCATION);
+
+  DALI_TEST_CHECK(!material.GetTexture((Scene3D::Material::TextureType)100));
+
+  END_TEST;
+}
+
+int UtcDaliMaterialSetGetSampler(void)
+{
+  tet_infoline("UtcDaliMaterialSetGetSampler.");
+
+  ToolkitTestApplication application;
+
+  Scene3D::Material material = Scene3D::Material::New();
+
+  Dali::Sampler baseColor = Dali::Sampler::New();
+  material.SetSampler(Scene3D::Material::TextureType::BASE_COLOR, baseColor);
+  DALI_TEST_EQUALS(baseColor, material.GetSampler(Scene3D::Material::TextureType::BASE_COLOR), TEST_LOCATION);
+
+  Dali::Sampler metallicRoughness = Dali::Sampler::New();
+  material.SetSampler(Scene3D::Material::TextureType::METALLIC_ROUGHNESS, metallicRoughness);
+  DALI_TEST_EQUALS(metallicRoughness, material.GetSampler(Scene3D::Material::TextureType::METALLIC_ROUGHNESS), TEST_LOCATION);
+
+  Dali::Sampler normal = Dali::Sampler::New();
+  material.SetSampler(Scene3D::Material::TextureType::NORMAL, normal);
+  DALI_TEST_EQUALS(normal, material.GetSampler(Scene3D::Material::TextureType::NORMAL), TEST_LOCATION);
+
+  Dali::Sampler occlusion = Dali::Sampler::New();
+  material.SetSampler(Scene3D::Material::TextureType::OCCLUSION, occlusion);
+  DALI_TEST_EQUALS(occlusion, material.GetSampler(Scene3D::Material::TextureType::OCCLUSION), TEST_LOCATION);
+
+  Dali::Sampler emissive = Dali::Sampler::New();
+  material.SetSampler(Scene3D::Material::TextureType::EMISSIVE, emissive);
+  DALI_TEST_EQUALS(emissive, material.GetSampler(Scene3D::Material::TextureType::EMISSIVE), TEST_LOCATION);
+
+  Dali::Sampler specular = Dali::Sampler::New();
+  material.SetSampler(Scene3D::Material::TextureType::SPECULAR, specular);
+  DALI_TEST_EQUALS(specular, material.GetSampler(Scene3D::Material::TextureType::SPECULAR), TEST_LOCATION);
+
+  Dali::Sampler specularColor = Dali::Sampler::New();
+  material.SetSampler(Scene3D::Material::TextureType::SPECULAR_COLOR, specularColor);
+  DALI_TEST_EQUALS(specularColor, material.GetSampler(Scene3D::Material::TextureType::SPECULAR_COLOR), TEST_LOCATION);
+
+  DALI_TEST_CHECK(!material.GetSampler((Scene3D::Material::TextureType)100));
+
+  END_TEST;
+}
\ No newline at end of file
index 7840c74..e931b3b 100644 (file)
@@ -25,6 +25,7 @@
 #include <toolkit-event-thread-callback.h>
 
 #include <dali-scene3d/public-api/controls/model/model.h>
+#include <dali-scene3d/public-api/model-components/model-node.h>
 
 #include <dali/devel-api/actors/camera-actor-devel.h>
 
@@ -54,12 +55,8 @@ const char* TEST_GLTF_FILE_NAME                    = TEST_RESOURCE_DIR "/Animate
 const char* TEST_GLTF_ANIMATION_TEST_FILE_NAME     = TEST_RESOURCE_DIR "/animationTest.gltf";
 const char* TEST_GLTF_MULTIPLE_PRIMITIVE_FILE_NAME = TEST_RESOURCE_DIR "/simpleMultiplePrimitiveTest.gltf";
 const char* TEST_DLI_FILE_NAME                     = TEST_RESOURCE_DIR "/arc.dli";
-// @TODO: The test cases for loading the DLI model below is temporarily disabled.
-// Need to fix how resources are loaded when a model contains multiple scenes and
-// each scene has its own root node.
-#ifdef MULTIPLE_SCENES_MODEL_SUPPORT
-const char* TEST_DLI_EXERCISE_FILE_NAME = TEST_RESOURCE_DIR "/exercise.dli";
-#endif
+const char* TEST_DLI_EXERCISE_FILE_NAME            = TEST_RESOURCE_DIR "/exercise.dli";
+
 /**
  * For the diffuse and specular cube map texture.
  * These textures are based off version of Wave engine sample
@@ -152,6 +149,26 @@ int UtcDaliModelNew(void)
 }
 
 // Positive test case for a method
+int UtcDaliModelNewP2(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliModelNew without url");
+
+  Scene3D::Model model = Scene3D::Model::New();
+  DALI_TEST_CHECK(model);
+
+  application.GetScene().Add(model);
+
+  DALI_TEST_CHECK(model.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE));
+
+  application.GetScene().Remove(model);
+
+  DALI_TEST_CHECK(!model.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE));
+
+  END_TEST;
+}
+
+// Positive test case for a method
 int UtcDaliModelDownCast(void)
 {
   ToolkitTestApplication application;
@@ -315,8 +332,8 @@ int UtcDaliModelOnScene02(void)
   uint32_t modelCount = model.GetModelRoot().GetChildCount();
   DALI_TEST_EQUALS(1, modelCount, TEST_LOCATION);
 
-  Actor   rootActor = model.GetModelRoot();
-  Vector3 rootSize  = rootActor.GetProperty<Vector3>(Dali::Actor::Property::SIZE);
+  Scene3D::ModelNode rootNode = model.GetModelRoot();
+  Vector3            rootSize = rootNode.GetProperty<Vector3>(Dali::Actor::Property::SIZE);
   DALI_TEST_EQUALS(Vector3(2, 2, 1), rootSize, TEST_LOCATION);
 
   END_TEST;
@@ -367,8 +384,8 @@ int UtcDaliModelGetNaturalSize(void)
   naturalSize = model.GetNaturalSize();
   DALI_TEST_EQUALS(Vector3(2, 2, 2), naturalSize, TEST_LOCATION);
 
-  Actor root = model.GetModelRoot();
-  DALI_TEST_CHECK(root);
+  Scene3D::ModelNode rootNode = model.GetModelRoot();
+  DALI_TEST_CHECK(rootNode);
 
   END_TEST;
 }
@@ -1011,7 +1028,6 @@ int UtcDaliModelAnimation02(void)
 
 int UtcDaliModelAnimation03(void)
 {
-#ifdef MULTIPLE_SCENES_MODEL_SUPPORT
   ToolkitTestApplication application;
 
   Scene3D::Model model = Scene3D::Model::New(TEST_DLI_EXERCISE_FILE_NAME);
@@ -1040,16 +1056,12 @@ int UtcDaliModelAnimation03(void)
   Animation animationByName = model.GetAnimation("idleClip");
   DALI_TEST_CHECK(animationByName);
   DALI_TEST_EQUALS(animationByIndex, animationByName, TEST_LOCATION);
-#else
-  tet_result(TET_PASS);
-#endif
 
   END_TEST;
 }
 
 int UtcDaliModelCameraGenerate01(void)
 {
-#ifdef MULTIPLE_SCENES_MODEL_SUPPORT
   ToolkitTestApplication application;
 
   Scene3D::Model model = Scene3D::Model::New(TEST_DLI_EXERCISE_FILE_NAME);
@@ -1077,9 +1089,6 @@ int UtcDaliModelCameraGenerate01(void)
 
   generatedCamera = model.GenerateCamera(1u); // Fail to generate camera
   DALI_TEST_CHECK(!generatedCamera);
-#else
-  tet_result(TET_PASS);
-#endif
 
   END_TEST;
 }
@@ -1126,7 +1135,8 @@ int UtcDaliModelCameraGenerate02(void)
   CameraActor appliedCamera;
   DALI_TEST_EQUALS(model.ApplyCamera(0u, appliedCamera), false, TEST_LOCATION); // Cannot apply into empty camera.
 
-  auto CompareCameraProperties = [](CameraActor lhs, CameraActor rhs, const char* location) {
+  auto CompareCameraProperties = [](CameraActor lhs, CameraActor rhs, const char* location)
+  {
     DALI_TEST_EQUALS(lhs.GetProperty<int>(Dali::CameraActor::Property::PROJECTION_MODE), rhs.GetProperty<int>(Dali::CameraActor::Property::PROJECTION_MODE), TEST_LOCATION);
     DALI_TEST_EQUALS(lhs.GetProperty<float>(Dali::CameraActor::Property::NEAR_PLANE_DISTANCE), rhs.GetProperty<float>(Dali::CameraActor::Property::NEAR_PLANE_DISTANCE), TEST_LOCATION);
 
@@ -1134,7 +1144,7 @@ int UtcDaliModelCameraGenerate02(void)
     {
       DALI_TEST_EQUALS(lhs.GetProperty<float>(Dali::CameraActor::Property::FIELD_OF_VIEW), rhs.GetProperty<float>(Dali::CameraActor::Property::FIELD_OF_VIEW), TEST_LOCATION);
       // TODO : Open this test when infinity far projection implement.
-      //DALI_TEST_EQUALS(lhs.GetProperty<float>(Dali::CameraActor::Property::FAR_PLANE_DISTANCE), rhs.GetProperty<float>(Dali::CameraActor::Property::FAR_PLANE_DISTANCE), TEST_LOCATION);
+      // DALI_TEST_EQUALS(lhs.GetProperty<float>(Dali::CameraActor::Property::FAR_PLANE_DISTANCE), rhs.GetProperty<float>(Dali::CameraActor::Property::FAR_PLANE_DISTANCE), TEST_LOCATION);
     }
     else
     {
@@ -1339,7 +1349,7 @@ int UtcDaliModelResourceCacheCheck(void)
   DALI_TEST_EQUALS(textureSet2.GetTextureCount(), 9u, TEST_LOCATION);
   DALI_TEST_EQUALS(textureSet3.GetTextureCount(), 9u, TEST_LOCATION);
 
-  for (uint32_t i = 0; i < 7u; i++)
+  for(uint32_t i = 0; i < 7u; i++)
   {
     DALI_TEST_EQUALS(textureSet2.GetTexture(i), textureSet3.GetTexture(i), TEST_LOCATION);
   }
@@ -1350,3 +1360,68 @@ int UtcDaliModelResourceCacheCheck(void)
   END_TEST;
 }
 
+int UtcDaliModelAddRemoveModelNode(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::Model model = Scene3D::Model::New();
+  model.SetProperty(Dali::Actor::Property::SIZE, Vector2(50, 50));
+
+  Scene3D::ModelNode node1 = Scene3D::ModelNode::New();
+  Scene3D::ModelNode node2 = Scene3D::ModelNode::New();
+  Scene3D::ModelNode node3 = Scene3D::ModelNode::New();
+  Scene3D::ModelNode node4 = Scene3D::ModelNode::New();
+
+  model.AddModelNode(node1);
+  model.AddModelNode(node2);
+  model.AddModelNode(node3);
+  model.RemoveModelNode(node1); // Remove node before scene on
+
+  application.GetScene().Add(model);
+
+  Dali::Scene3D::ModelNode root = model.GetModelRoot();
+  DALI_TEST_CHECK(root);
+  DALI_TEST_EQUALS(2, root.GetChildCount(), TEST_LOCATION);
+
+  model.RemoveModelNode(node2); // Remove node after scene on
+
+  DALI_TEST_EQUALS(1, root.GetChildCount(), TEST_LOCATION);
+
+  model.AddModelNode(node4); // Add during scene on
+
+  DALI_TEST_EQUALS(2, root.GetChildCount(), TEST_LOCATION);
+
+  application.GetScene().Remove(model);
+
+  model.RemoveModelNode(node3); // Remove node after scene off
+
+  END_TEST;
+}
+
+int UtcDaliModelFindChildModelNodeByName(void)
+{
+  tet_infoline(" UtcDaliModelNodeFindChildModelNodeByName.");
+
+  ToolkitTestApplication application;
+
+  Scene3D::Model model = Scene3D::Model::New();
+  application.GetScene().Add(model);
+
+  Scene3D::ModelNode modelNode1 = Scene3D::ModelNode::New();
+  Scene3D::ModelNode modelNode2 = Scene3D::ModelNode::New();
+
+  modelNode1.SetProperty(Dali::Actor::Property::NAME, "modelNode1");
+  modelNode2.SetProperty(Dali::Actor::Property::NAME, "modelNode2");
+  model.AddModelNode(modelNode1);
+  model.AddModelNode(modelNode2);
+
+  Scene3D::ModelNode child1 = model.FindChildModelNodeByName("modelNode1");
+  DALI_TEST_CHECK(child1);
+  DALI_TEST_EQUALS(child1, modelNode1, TEST_LOCATION);
+
+  Scene3D::ModelNode child2 = model.FindChildModelNodeByName("modelNode2");
+  DALI_TEST_CHECK(child2);
+  DALI_TEST_EQUALS(child2, modelNode2, TEST_LOCATION);
+
+  END_TEST;
+}
\ No newline at end of file
diff --git a/automated-tests/src/dali-scene3d/utc-Dali-ModelNode.cpp b/automated-tests/src/dali-scene3d/utc-Dali-ModelNode.cpp
new file mode 100644 (file)
index 0000000..e124bc6
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2023 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.
+ *
+ */
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <stdlib.h>
+#include <iostream>
+
+#include <dali-scene3d/public-api/model-components/model-node.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+void model_components_model_node_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void model_components_model_node_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+namespace
+{
+} // namespace
+
+// Negative test case for a method
+int UtcDaliModelNodeUninitialized(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliModelNodeUninitialized");
+
+  Scene3D::ModelNode modelNode;
+
+  try
+  {
+    // New() must be called to create a ModelNode or it wont be valid.
+    Actor a = Actor::New();
+    modelNode.Add(a);
+    DALI_TEST_CHECK(false);
+  }
+  catch(Dali::DaliException& e)
+  {
+    // Tests that a negative test of an assertion succeeds
+    DALI_TEST_PRINT_ASSERT(e);
+    DALI_TEST_CHECK(!modelNode);
+  }
+  END_TEST;
+}
+
+// Positive test case for a method
+int UtcDaliModelNodeNew(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliModelNodeNew");
+
+  Scene3D::ModelNode modelNode = Scene3D::ModelNode::New();
+  DALI_TEST_CHECK(modelNode);
+  END_TEST;
+}
+
+// Positive test case for a method
+int UtcDaliModelNodeDownCast(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliModelNodeDownCast");
+
+  Scene3D::ModelNode modelNode = Scene3D::ModelNode::New();
+  BaseHandle         handle(modelNode);
+
+  Scene3D::ModelNode modelNode2 = Scene3D::ModelNode::DownCast(handle);
+  DALI_TEST_CHECK(modelNode);
+  DALI_TEST_CHECK(modelNode2);
+  DALI_TEST_CHECK(modelNode2 == modelNode);
+  END_TEST;
+}
+
+int UtcDaliModelNodeTypeRegistry(void)
+{
+  ToolkitTestApplication application;
+
+  TypeRegistry typeRegistry = TypeRegistry::Get();
+  DALI_TEST_CHECK(typeRegistry);
+
+  TypeInfo typeInfo = typeRegistry.GetTypeInfo("ModelNode");
+  DALI_TEST_CHECK(typeInfo);
+
+  BaseHandle handle = typeInfo.CreateInstance();
+  DALI_TEST_CHECK(handle);
+
+  Scene3D::ModelNode modelNode = Scene3D::ModelNode::DownCast(handle);
+  DALI_TEST_CHECK(modelNode);
+
+  END_TEST;
+}
+
+// Positive test case for a method
+int UtcDaliModelNodeAddRemove(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliModelAddRemove");
+
+  Scene3D::ModelNode modelNode = Scene3D::ModelNode::New();
+  DALI_TEST_CHECK(modelNode);
+
+  Actor actor = Actor::New();
+  DALI_TEST_CHECK(!actor.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE));
+
+  modelNode.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  modelNode.SetProperty(Actor::Property::SIZE, application.GetScene().GetSize());
+  modelNode.Add(actor);
+  application.GetScene().Add(modelNode);
+
+  DALI_TEST_CHECK(actor.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE));
+
+  modelNode.Remove(actor);
+
+  DALI_TEST_CHECK(!actor.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE));
+  END_TEST;
+}
+
+int UtcDaliModelNodeCopyAndAssignment(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::ModelNode modelNode = Scene3D::ModelNode::New();
+  DALI_TEST_CHECK(modelNode);
+
+  Scene3D::ModelNode copy(modelNode);
+  DALI_TEST_CHECK(modelNode == copy);
+
+  Scene3D::ModelNode assign;
+  DALI_TEST_CHECK(!assign);
+
+  assign = copy;
+  DALI_TEST_CHECK(assign == modelNode);
+
+  END_TEST;
+}
+
+int UtcDaliModelNodeMoveConstructor(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::ModelNode modelNode = Scene3D::ModelNode::New();
+  DALI_TEST_EQUALS(1, modelNode.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+  modelNode.SetProperty(Actor::Property::SENSITIVE, false);
+  DALI_TEST_CHECK(false == modelNode.GetProperty<bool>(Actor::Property::SENSITIVE));
+
+  Scene3D::ModelNode moved = std::move(modelNode);
+  DALI_TEST_CHECK(moved);
+  DALI_TEST_EQUALS(1, moved.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+  DALI_TEST_CHECK(false == moved.GetProperty<bool>(Actor::Property::SENSITIVE));
+  DALI_TEST_CHECK(!modelNode);
+
+  END_TEST;
+}
+
+int UtcDaliModelNodeMoveAssignment(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::ModelNode modelNode = Scene3D::ModelNode::New();
+  DALI_TEST_EQUALS(1, modelNode.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+  modelNode.SetProperty(Actor::Property::SENSITIVE, false);
+  DALI_TEST_CHECK(false == modelNode.GetProperty<bool>(Actor::Property::SENSITIVE));
+
+  Scene3D::ModelNode moved;
+  moved = std::move(modelNode);
+  DALI_TEST_CHECK(moved);
+  DALI_TEST_EQUALS(1, moved.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+  DALI_TEST_CHECK(false == moved.GetProperty<bool>(Actor::Property::SENSITIVE));
+  DALI_TEST_CHECK(!modelNode);
+
+  END_TEST;
+}
+
+int UtcDaliModelNodeOnSizeSet(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::ModelNode modelNode = Scene3D::ModelNode::New();
+
+  application.GetScene().Add(modelNode);
+
+  application.SendNotification();
+  application.Render();
+
+  Vector2 size(200.0f, 300.0f);
+  modelNode.SetProperty(Actor::Property::SIZE, size);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(modelNode.GetCurrentProperty<Vector2>(Actor::Property::SIZE), size, TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Method test
+
+int UtcDaliModelNodeAddRemovePrimitive(void)
+{
+  tet_infoline(" UtcDaliModelNodeAddPrimitive.");
+
+  ToolkitTestApplication application;
+
+  Scene3D::ModelNode modelNode = Scene3D::ModelNode::New();
+
+  uint32_t expect = 0u;
+
+  tet_printf("Test empty primitive case\n");
+
+  DALI_TEST_EQUALS(expect, modelNode.GetModelPrimitiveCount(), TEST_LOCATION);
+  DALI_TEST_CHECK(!modelNode.GetModelPrimitive(0u));
+  DALI_TEST_CHECK(!modelNode.GetModelPrimitive(1u));
+
+  Scene3D::ModelPrimitive primitive1 = Scene3D::ModelPrimitive::New();
+  Scene3D::ModelPrimitive primitive2 = Scene3D::ModelPrimitive::New();
+
+  Scene3D::Material material = Scene3D::Material::New();
+
+  primitive1.SetMaterial(material);
+  primitive2.SetMaterial(material);
+
+  tet_printf("Test primitive appended during off scene\n");
+
+  modelNode.AddModelPrimitive(primitive1);
+  ++expect;
+  DALI_TEST_EQUALS(expect, modelNode.GetModelPrimitiveCount(), TEST_LOCATION);
+  DALI_TEST_CHECK(primitive1 == modelNode.GetModelPrimitive(0u));
+  DALI_TEST_CHECK(!modelNode.GetModelPrimitive(1u));
+
+  tet_printf("Test primitive appended during on scene\n");
+
+  application.GetScene().Add(modelNode);
+
+  modelNode.AddModelPrimitive(primitive2);
+  ++expect;
+  DALI_TEST_EQUALS(expect, modelNode.GetModelPrimitiveCount(), TEST_LOCATION);
+  DALI_TEST_CHECK(primitive1 == modelNode.GetModelPrimitive(0u));
+  DALI_TEST_CHECK(primitive2 == modelNode.GetModelPrimitive(1u));
+
+  modelNode.RemoveModelPrimitive(primitive1);
+  DALI_TEST_CHECK(primitive2 == modelNode.GetModelPrimitive(0u));
+  DALI_TEST_CHECK(!modelNode.GetModelPrimitive(1u));
+
+  modelNode.RemoveModelPrimitive(0u);
+  DALI_TEST_CHECK(!modelNode.GetModelPrimitive(0u));
+  DALI_TEST_CHECK(!modelNode.GetModelPrimitive(1u));
+
+  modelNode.Unparent();
+
+  END_TEST;
+}
+
+int UtcDaliModelNodeFindChildModelNodeByName(void)
+{
+  tet_infoline(" UtcDaliModelNodeFindChildModelNodeByName.");
+
+  ToolkitTestApplication application;
+
+  Scene3D::ModelNode modelNode = Scene3D::ModelNode::New();
+  Scene3D::ModelNode modelNode1 = Scene3D::ModelNode::New();
+  Scene3D::ModelNode modelNode2 = Scene3D::ModelNode::New();
+
+  modelNode1.SetProperty(Dali::Actor::Property::NAME, "modelNode1");
+  modelNode2.SetProperty(Dali::Actor::Property::NAME, "modelNode2");
+  modelNode.Add(modelNode1);
+  modelNode.Add(modelNode2);
+
+  Scene3D::ModelNode child1 = modelNode.FindChildModelNodeByName("modelNode1");
+  DALI_TEST_CHECK(child1);
+  DALI_TEST_EQUALS(child1, modelNode1, TEST_LOCATION);
+
+  Scene3D::ModelNode child2 = modelNode.FindChildModelNodeByName("modelNode2");
+  DALI_TEST_CHECK(child2);
+  DALI_TEST_EQUALS(child2, modelNode2, TEST_LOCATION);
+
+  END_TEST;
+}
\ No newline at end of file
diff --git a/automated-tests/src/dali-scene3d/utc-Dali-ModelPrimitive.cpp b/automated-tests/src/dali-scene3d/utc-Dali-ModelPrimitive.cpp
new file mode 100644 (file)
index 0000000..3719861
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2023 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.
+ *
+ */
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <stdlib.h>
+#include <iostream>
+
+#include <dali-scene3d/public-api/model-components/model-primitive.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+void model_components_model_primitive_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void model_components_model_primitive_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+namespace
+{
+} // namespace
+
+// Positive test case for a method
+int UtcDaliModelPrimitiveNew(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliModelPrimitiveNew");
+
+  Scene3D::ModelPrimitive modelPrimitive = Scene3D::ModelPrimitive::New();
+  DALI_TEST_CHECK(modelPrimitive);
+  END_TEST;
+}
+
+// Positive test case for a method
+int UtcDaliModelPrimitiveDownCast(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliModelPrimitiveDownCast");
+
+  Scene3D::ModelPrimitive modelPrimitive = Scene3D::ModelPrimitive::New();
+  BaseHandle              handle(modelPrimitive);
+
+  Scene3D::ModelPrimitive modelPrimitive2 = Scene3D::ModelPrimitive::DownCast(handle);
+  DALI_TEST_CHECK(modelPrimitive);
+  DALI_TEST_CHECK(modelPrimitive2);
+  DALI_TEST_CHECK(modelPrimitive2 == modelPrimitive);
+  END_TEST;
+}
+
+int UtcDaliModelPrimitiveTypeRegistry(void)
+{
+  ToolkitTestApplication application;
+
+  TypeRegistry typeRegistry = TypeRegistry::Get();
+  DALI_TEST_CHECK(typeRegistry);
+
+  TypeInfo typeInfo = typeRegistry.GetTypeInfo("ModelPrimitive");
+  DALI_TEST_CHECK(typeInfo);
+
+  BaseHandle handle = typeInfo.CreateInstance();
+  DALI_TEST_CHECK(handle);
+
+  Scene3D::ModelPrimitive modelPrimitive = Scene3D::ModelPrimitive::DownCast(handle);
+  DALI_TEST_CHECK(modelPrimitive);
+
+  END_TEST;
+}
+
+int UtcDaliModelPrimitiveCopyAndAssignment(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::ModelPrimitive modelPrimitive = Scene3D::ModelPrimitive::New();
+  DALI_TEST_CHECK(modelPrimitive);
+
+  Scene3D::ModelPrimitive copy(modelPrimitive);
+  DALI_TEST_CHECK(modelPrimitive == copy);
+
+  Scene3D::ModelPrimitive assign;
+  DALI_TEST_CHECK(!assign);
+
+  assign = copy;
+  DALI_TEST_CHECK(assign == modelPrimitive);
+
+  END_TEST;
+}
+
+int UtcDaliModelPrimitiveMoveConstructor(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::ModelPrimitive modelPrimitive = Scene3D::ModelPrimitive::New();
+  DALI_TEST_EQUALS(1, modelPrimitive.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+
+  Scene3D::ModelPrimitive moved = std::move(modelPrimitive);
+  DALI_TEST_CHECK(moved);
+  DALI_TEST_EQUALS(1, moved.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+  DALI_TEST_CHECK(!modelPrimitive);
+
+  END_TEST;
+}
+
+int UtcDaliModelPrimitiveMoveAssignment(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::ModelPrimitive modelPrimitive = Scene3D::ModelPrimitive::New();
+  DALI_TEST_EQUALS(1, modelPrimitive.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+
+  Scene3D::ModelPrimitive moved;
+  moved = std::move(modelPrimitive);
+  DALI_TEST_CHECK(moved);
+  DALI_TEST_EQUALS(1, moved.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Method test
+int UtcDaliModelPrimitiveSetGetGeometry(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::ModelPrimitive modelPrimitive = Scene3D::ModelPrimitive::New();
+
+  tet_printf("Check primitive don't have renderer initial time\n");
+
+  DALI_TEST_CHECK(!modelPrimitive.GetGeometry());
+
+  Dali::Geometry geometry = Dali::Geometry::New();
+
+  modelPrimitive.SetGeometry(geometry);
+  DALI_TEST_CHECK(geometry == modelPrimitive.GetGeometry());
+
+  tet_printf("Replace as new Renderer\n");
+
+  Dali::Geometry geometry2 = Dali::Geometry::New();
+
+  modelPrimitive.SetGeometry(geometry2);
+  DALI_TEST_CHECK(geometry != modelPrimitive.GetGeometry());
+  DALI_TEST_CHECK(geometry2 == modelPrimitive.GetGeometry());
+  END_TEST;
+}
+
+int UtcDaliModelPrimitiveSetGetMaterial(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::ModelPrimitive modelPrimitive = Scene3D::ModelPrimitive::New();
+
+  tet_printf("Check primitive don't have material initial time\n");
+
+  DALI_TEST_CHECK(!modelPrimitive.GetMaterial());
+
+  Dali::Scene3D::Material material = Dali::Scene3D::Material::New();
+
+  modelPrimitive.SetMaterial(material);
+  DALI_TEST_CHECK(material == modelPrimitive.GetMaterial());
+
+  tet_printf("Replace as new Material\n");
+
+  Dali::Scene3D::Material material2 = Dali::Scene3D::Material::New();
+
+  modelPrimitive.SetMaterial(material2);
+  DALI_TEST_CHECK(material != modelPrimitive.GetMaterial());
+  DALI_TEST_CHECK(material2 == modelPrimitive.GetMaterial());
+
+  END_TEST;
+}
\ No newline at end of file
index 2b7c319..1de81f7 100644 (file)
@@ -114,7 +114,7 @@ int UtcDaliNodeDefinitionProperties(void)
   nodeDef.mExtras.push_back(NodeDefinition::Extra{"frobnicateFactor", frobnicateFactor});
 
   Context ctx;
-  auto    actor = nodeDef.CreateActor(ctx.createParams);
+  auto    actor = nodeDef.CreateModelNode(ctx.createParams);
   DALI_TEST_EQUAL(nodeDef.mName, actor.GetProperty(Actor::Property::NAME).Get<std::string>());
   DALI_TEST_EQUAL(nodeDef.mPosition, actor.GetProperty(Actor::Property::POSITION).Get<Vector3>());
   DALI_TEST_EQUAL(nodeDef.mOrientation, actor.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>());
@@ -220,7 +220,7 @@ int UtcDaliNodeDefinitionRenderable(void)
   auto       shader = Shader::New(VSH, FSH);
   ctx.resources.mShaders.push_back({ShaderDefinition{}, shader});
 
-  auto actor = nodeDef.CreateActor(ctx.createParams);
+  auto actor = nodeDef.CreateModelNode(ctx.createParams);
   DALI_TEST_EQUAL(1, actor.GetRendererCount());
 
   auto renderer = actor.GetRendererAt(0);
index 6dff87b..4767977 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -77,10 +77,10 @@ int UtcDaliImageConvertFrameBufferToUrl2(void)
   END_TEST;
 }
 
-int UtcDaliImageConvertPixelDataToUrl(void)
+int UtcDaliImageConvertPixelDataToUrl01(void)
 {
   ToolkitTestApplication application;
-  tet_infoline("UtcDaliImageConvertPixelDataToUrl");
+  tet_infoline("UtcDaliImageConvertPixelDataToUrl01");
 
   unsigned int width(64);
   unsigned int height(64);
@@ -94,10 +94,27 @@ int UtcDaliImageConvertPixelDataToUrl(void)
   END_TEST;
 }
 
-int UtcDaliImageConvertNativeImageSourceToUrl(void)
+int UtcDaliImageConvertPixelDataToUrl02(void)
 {
   ToolkitTestApplication application;
-  tet_infoline("UtcDaliImageConvertNativeImageSourceToUrl");
+  tet_infoline("UtcDaliImageConvertPixelDataToUrl02");
+
+  unsigned int width(64);
+  unsigned int height(64);
+  unsigned int bufferSize = width * height * Pixel::GetBytesPerPixel(Pixel::RGBA8888);
+
+  unsigned char* buffer    = reinterpret_cast<unsigned char*>(malloc(bufferSize));
+  PixelData      pixelData = PixelData::New(buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::FREE);
+
+  DALI_TEST_CHECK(Dali::Toolkit::Image::GenerateUrl(pixelData, true).GetUrl().size() > 0u);
+
+  END_TEST;
+}
+
+int UtcDaliImageConvertNativeImageSourceToUrl01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliImageConvertNativeImageSourceToUrl01");
 
   unsigned int width(64);
   unsigned int height(64);
@@ -121,6 +138,33 @@ int UtcDaliImageConvertNativeImageSourceToUrl(void)
   END_TEST;
 }
 
+int UtcDaliImageConvertNativeImageSourceToUrl02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliImageConvertNativeImageSourceToUrl02");
+
+  unsigned int width(64);
+  unsigned int height(64);
+  try
+  {
+    NativeImageSourcePtr nativeImageSource = NativeImageSource::New(width, height, NativeImageSource::COLOR_DEPTH_DEFAULT);
+
+    DALI_TEST_CHECK(Dali::Toolkit::Image::GenerateUrl(nativeImageSource, true).GetUrl().size() > 0u);
+  }
+  catch(Dali::DaliException& e)
+  {
+    DALI_TEST_PRINT_ASSERT(e);
+    DALI_TEST_ASSERT(e, "Adaptor::IsAvailable()", TEST_LOCATION);
+  }
+  catch(...)
+  {
+    tet_printf("Assertion test failed - wrong Exception\n");
+    tet_result(TET_FAIL);
+  }
+
+  END_TEST;
+}
+
 int UtcDaliImageConvertEncodedImageBufferToUrl(void)
 {
   ToolkitTestApplication application;
index 658d585..d137c5d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -24,7 +24,7 @@
 using namespace Dali;
 using namespace Dali::Toolkit;
 
-int UtcImageUrlConstructor(void)
+int UtcImageUrlConstructor01(void)
 {
   ToolkitTestApplication application;
 
@@ -50,3 +50,30 @@ int UtcImageUrlConstructor(void)
   DALI_TEST_CHECK(downcastUrl);
   END_TEST;
 }
+
+int UtcImageUrlConstructor02(void)
+{
+  ToolkitTestApplication application;
+
+  tet_infoline(" UtcImageUrlValid ");
+
+  // Test default constructor.
+  ImageUrl imageUrl;
+  DALI_TEST_CHECK(!imageUrl);
+
+  // Test object creation
+  Texture image = Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 4u, 4u); // test texture
+  imageUrl      = ImageUrl::New(image, true);
+  DALI_TEST_CHECK(imageUrl);
+
+  // Test copy constructor
+  ImageUrl ImageUrlCopy(imageUrl);
+  DALI_TEST_CHECK(ImageUrlCopy);
+
+  // Test down cast
+  BaseHandle baseUrl;
+  baseUrl              = imageUrl;
+  ImageUrl downcastUrl = ImageUrl::DownCast(baseUrl);
+  DALI_TEST_CHECK(downcastUrl);
+  END_TEST;
+}
index 1fcd10d..43c7530 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -424,6 +424,198 @@ int UtcDaliImageVisualRemoteImageLoad(void)
   END_TEST;
 }
 
+int UtcDaliImageVisualWithFrameBufferPreMultipliedAlpha01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Use FrameBuffer as url");
+
+  uint32_t width(64);
+  uint32_t height(64);
+  FrameBuffer frameBuffer = Dali::FrameBuffer::New(width, height, FrameBuffer::Attachment::NONE);
+
+  DALI_TEST_CHECK(frameBuffer);
+  ImageUrl    imageUrl = Dali::Toolkit::Image::GenerateUrl(frameBuffer, Pixel::Format::RGBA8888, width, height);
+  std::string url      = imageUrl.GetUrl();
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  Property::Map propertyMap;
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, url);
+
+  Visual::Base visual = factory.CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  DummyControl      actor     = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+
+  application.GetScene().Add(actor);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(16);
+
+  Renderer renderer = actor.GetRendererAt(0);
+
+  // Check whether preMultipliedAlpha is true.
+  auto preMultipliedAlpha = renderer.GetProperty<bool>(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA);
+  DALI_TEST_EQUALS(preMultipliedAlpha, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualWithFrameBufferPreMultipliedAlpha02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Use FrameBuffer as url");
+
+  uint32_t width(64);
+  uint32_t height(64);
+  FrameBuffer frameBuffer = Dali::FrameBuffer::New(width, height, FrameBuffer::Attachment::NONE);
+
+  DALI_TEST_CHECK(frameBuffer);
+
+  Texture texture = Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, width, height);
+  frameBuffer.AttachColorTexture(texture);
+
+  ImageUrl    imageUrl = Dali::Toolkit::Image::GenerateUrl(frameBuffer, 0u);
+  std::string url      = imageUrl.GetUrl();
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  Property::Map propertyMap;
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, url);
+
+  Visual::Base visual = factory.CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  DummyControl      actor     = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+
+  application.GetScene().Add(actor);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(16);
+
+  Renderer renderer = actor.GetRendererAt(0);
+
+  // Check whether preMultipliedAlpha is true.
+  auto preMultipliedAlpha = renderer.GetProperty<bool>(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA);
+  DALI_TEST_EQUALS(preMultipliedAlpha, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualWithPixelData(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Use PixelData as url");
+
+  uint32_t width(64);
+  uint32_t height(64);
+  uint32_t bufferSize = width * height * Pixel::GetBytesPerPixel(Pixel::RGBA8888);
+
+  uint8_t*  buffer    = reinterpret_cast<uint8_t*>(malloc(bufferSize));
+  PixelData pixelData = PixelData::New(buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::FREE);
+
+  DALI_TEST_CHECK(pixelData);
+
+  ImageUrl    imageUrl = Dali::Toolkit::Image::GenerateUrl(pixelData);
+  std::string url      = imageUrl.GetUrl();
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  Property::Map propertyMap;
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, url);
+
+  Visual::Base visual = factory.CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  DummyControl      actor     = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+
+  application.GetScene().Add(actor);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(16);
+
+  Renderer renderer = actor.GetRendererAt(0);
+
+  // Check whether preMultipliedAlpha is false.
+  auto preMultipliedAlpha = renderer.GetProperty<bool>(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA);
+  DALI_TEST_EQUALS(preMultipliedAlpha, false, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualWithPixelDataPreMultipliedAlpha(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Use PixelData as url");
+
+  uint32_t width(64);
+  uint32_t height(64);
+  uint32_t bufferSize = width * height * Pixel::GetBytesPerPixel(Pixel::RGBA8888);
+
+  uint8_t*  buffer    = reinterpret_cast<uint8_t*>(malloc(bufferSize));
+  PixelData pixelData = PixelData::New(buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::FREE);
+
+  DALI_TEST_CHECK(pixelData);
+
+  ImageUrl    imageUrl = Dali::Toolkit::Image::GenerateUrl(pixelData, true);
+  std::string url      = imageUrl.GetUrl();
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  Property::Map propertyMap;
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, url);
+
+  Visual::Base visual = factory.CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  DummyControl      actor     = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+
+  application.GetScene().Add(actor);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(16);
+
+  Renderer renderer = actor.GetRendererAt(0);
+
+  // Check whether preMultipliedAlpha is true.
+  auto preMultipliedAlpha = renderer.GetProperty<bool>(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA);
+  DALI_TEST_EQUALS(preMultipliedAlpha, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcDaliImageVisualWithNativeImage(void)
 {
   ToolkitTestApplication application;
@@ -453,6 +645,9 @@ int UtcDaliImageVisualWithNativeImage(void)
 
   DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
 
+  application.SendNotification();
+  application.Render(16);
+
   Renderer renderer = actor.GetRendererAt(0);
   Shader   shader   = renderer.GetShader();
 
@@ -466,6 +661,62 @@ int UtcDaliImageVisualWithNativeImage(void)
 
   DALI_TEST_EQUALS(pos != std::string::npos, true, TEST_LOCATION);
 
+  // Check whether preMultipliedAlpha is false.
+  auto preMultipliedAlpha = renderer.GetProperty<bool>(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA);
+  DALI_TEST_EQUALS(preMultipliedAlpha, false, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualWithNativeImagePreMultipliedAlpha(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Use Native Image as url");
+
+  NativeImageSourcePtr nativeImageSource = NativeImageSource::New(500, 500, NativeImageSource::COLOR_DEPTH_DEFAULT);
+  ImageUrl             imageUrl          = Dali::Toolkit::Image::GenerateUrl(nativeImageSource, true);
+  std::string          url               = imageUrl.GetUrl();
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  Property::Map propertyMap;
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, url);
+
+  Visual::Base visual = factory.CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  DummyControl      actor     = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+
+  application.GetScene().Add(actor);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(16);
+
+  Renderer renderer = actor.GetRendererAt(0);
+  Shader   shader   = renderer.GetShader();
+
+  Property::Value value = shader.GetProperty(Shader::Property::PROGRAM);
+  DALI_TEST_CHECK(value.GetType() == Property::MAP);
+  const Property::Map* outMap         = value.GetMap();
+  std::string          fragmentShader = (*outMap)["fragment"].Get<std::string>();
+
+  const char* fragmentPrefix = Dali::NativeImageSourceTest::GetCustomFragmentPrefix();
+  size_t      pos            = fragmentShader.find(fragmentPrefix);
+
+  DALI_TEST_EQUALS(pos != std::string::npos, true, TEST_LOCATION);
+
+  // Check whether preMultipliedAlpha is true.
+  auto preMultipliedAlpha = renderer.GetProperty<bool>(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA);
+  DALI_TEST_EQUALS(preMultipliedAlpha, true, TEST_LOCATION);
+
   END_TEST;
 }
 
@@ -475,7 +726,7 @@ int UtcDaliImageVisualWithNativeImageCustomShader(void)
   tet_infoline("Use Native Image as url and Use custom shader");
 
   NativeImageSourcePtr nativeImageSource = NativeImageSource::New(500, 500, NativeImageSource::COLOR_DEPTH_DEFAULT);
-  ImageUrl             imageUrl          = Dali::Toolkit::Image::GenerateUrl(nativeImageSource);
+  ImageUrl             imageUrl          = Dali::Toolkit::Image::GenerateUrl(nativeImageSource, true);
   std::string          url               = imageUrl.GetUrl();
 
   VisualFactory factory = VisualFactory::Get();
@@ -531,6 +782,11 @@ int UtcDaliImageVisualWithNativeImageCustomShader(void)
 
   DALI_TEST_EQUALS(std::string(fragmentPrefix) + customFragmentShaderSource, fragmentShaderSource, TEST_LOCATION);
 
+  // Check whether preMultipliedAlpha is false.
+  // Note : We dont use preMultiplied alpha when app developer using custom shader.
+  auto preMultipliedAlpha = renderer.GetProperty<bool>(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA);
+  DALI_TEST_EQUALS(preMultipliedAlpha, false, TEST_LOCATION);
+
   END_TEST;
 }
 
index 116b489..beba867 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -47,6 +47,8 @@ int UtcDaliTextureManagerAddRemoveP(void)
   std::string url2;
   std::string url3;
   std::string url4;
+  std::string url5;
+  std::string url6;
   // scope to ensure texturesets are kept alive by texture manager
   {
     auto texture = Texture::New(Dali::TextureType::TEXTURE_2D, Pixel::RGBA8888, 88, 99);
@@ -71,6 +73,23 @@ int UtcDaliTextureManagerAddRemoveP(void)
     DALI_TEST_CHECK(url4 != url);
     DALI_TEST_CHECK(url4 != url2);
     DALI_TEST_CHECK(url4 != url3);
+
+    // add same texture again with preMultiplied, should give new Url
+    url5 = TextureManager::AddTexture(texture, true);
+    DALI_TEST_CHECK(url5.size() > 0u);
+    DALI_TEST_CHECK(url5 != url);
+    DALI_TEST_CHECK(url5 != url2);
+    DALI_TEST_CHECK(url5 != url3);
+    DALI_TEST_CHECK(url5 != url4);
+
+    textureSet = TextureSet::New();
+    url6       = TextureManager::AddTexture(textureSet, true);
+    DALI_TEST_CHECK(url6.size() > 0u);
+    DALI_TEST_CHECK(url6 != url);
+    DALI_TEST_CHECK(url6 != url2);
+    DALI_TEST_CHECK(url6 != url3);
+    DALI_TEST_CHECK(url6 != url4);
+    DALI_TEST_CHECK(url6 != url5);
   }
 
   auto textureSet = TextureManager::RemoveTexture(url);
@@ -102,6 +121,16 @@ int UtcDaliTextureManagerAddRemoveP(void)
   textureSet = TextureManager::RemoveTexture(url4);
   DALI_TEST_CHECK(!textureSet && "Texture needs to be removed from texture manager");
 
+  textureSet = TextureManager::RemoveTexture(url5);
+  DALI_TEST_CHECK(textureSet && "Texture needs to be non empty handle");
+  textureSet = TextureManager::RemoveTexture(url5);
+  DALI_TEST_CHECK(!textureSet && "Texture needs to be removed from texture manager");
+
+  textureSet = TextureManager::RemoveTexture(url6);
+  DALI_TEST_CHECK(textureSet && "Texture needs to be non empty handle");
+  textureSet = TextureManager::RemoveTexture(url6);
+  DALI_TEST_CHECK(!textureSet && "Texture needs to be removed from texture manager");
+
   END_TEST;
 }
 
index 0d30b26..e0d38b4 100644 (file)
@@ -135,7 +135,7 @@ public:
   ModelCacheManager(const ModelCacheManager& rhs) = default;
   ModelCacheManager(ModelCacheManager&& rhs)      = default;
   ModelCacheManager& operator=(const ModelCacheManager& rhs) = default;
-  ModelCacheManager& operator=(ModelCacheManager&& rhs) = default;
+  ModelCacheManager& operator=(ModelCacheManager&& rhs) noexcept = default;
 
 private:
   class Impl;
index 27e8bc8..2f29b25 100644 (file)
@@ -33,6 +33,7 @@
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/common/model-cache-manager.h>
 #include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
+#include <dali-scene3d/internal/model-components/model-node-impl.h>
 #include <dali-scene3d/public-api/controls/model/model.h>
 #include <dali-scene3d/public-api/loader/animation-definition.h>
 #include <dali-scene3d/public-api/loader/camera-parameters.h>
@@ -52,6 +53,9 @@ namespace Internal
 {
 namespace
 {
+/**
+ * Creates control through type registry
+ */
 BaseHandle Create()
 {
   return Scene3D::Model::New(std::string());
@@ -61,9 +65,6 @@ BaseHandle Create()
 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::Model, Toolkit::Control, Create);
 DALI_TYPE_REGISTRATION_END()
 
-static constexpr uint32_t OFFSET_FOR_DIFFUSE_CUBE_TEXTURE  = 2u;
-static constexpr uint32_t OFFSET_FOR_SPECULAR_CUBE_TEXTURE = 1u;
-
 static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
 
 static constexpr bool DEFAULT_MODEL_CHILDREN_SENSITIVE = false;
@@ -194,7 +195,7 @@ Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUr
 
 Model::~Model()
 {
-  if(ModelCacheManager::Get())
+  if(ModelCacheManager::Get() && !mModelUrl.empty())
   {
     ModelCacheManager::Get().UnreferenceModelCache(mModelUrl);
   }
@@ -215,11 +216,44 @@ Dali::Scene3D::Model Model::New(const std::string& modelUrl, const std::string&
   return handle;
 }
 
-const Actor Model::GetModelRoot() const
+const Scene3D::ModelNode Model::GetModelRoot() const
 {
   return mModelRoot;
 }
 
+void Model::AddModelNode(Scene3D::ModelNode modelNode)
+{
+  if(!mModelRoot)
+  {
+    CreateModelRoot();
+  }
+
+  mModelRoot.Add(modelNode);
+  if(mModelUrl.empty())
+  {
+    mModelResourceReady = true;
+  }
+
+  if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
+  {
+    UpdateImageBasedLightTexture();
+    UpdateImageBasedLightScaleFactor();
+  }
+
+  if(Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
+  {
+    NotifyResourceReady();
+  }
+}
+
+void Model::RemoveModelNode(Scene3D::ModelNode modelNode)
+{
+  if(mModelRoot)
+  {
+    mModelRoot.Remove(modelNode);
+  }
+}
+
 void Model::SetChildrenSensitive(bool enable)
 {
   if(mModelChildrenSensitive != enable)
@@ -417,6 +451,12 @@ bool Model::ApplyCamera(uint32_t index, Dali::CameraActor camera) const
   return false;
 }
 
+Scene3D::ModelNode Model::FindChildModelNodeByName(std::string_view nodeName)
+{
+  Actor childActor = Self().FindChildByName(nodeName);
+  return Scene3D::ModelNode::DownCast(childActor);
+}
+
 ///////////////////////////////////////////////////////////
 //
 // Private methods
@@ -430,8 +470,9 @@ void Model::OnInitialize()
 
 void Model::OnSceneConnection(int depth)
 {
-  if(!mModelLoadTask && !mModelRoot)
+  if(!mModelLoadTask && !mModelResourceReady && !mModelUrl.empty())
   {
+    // Request model load only if we setup url.
     if(ModelCacheManager::Get())
     {
       ModelCacheManager::Get().ReferenceModelCache(mModelUrl);
@@ -439,6 +480,7 @@ void Model::OnSceneConnection(int depth)
     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())
   {
@@ -458,6 +500,7 @@ void Model::OnSceneConnection(int depth)
     parent = parent.GetParent();
   }
 
+  NotifyResourceReady();
   Control::OnSceneConnection(depth);
 }
 
@@ -508,6 +551,17 @@ bool Model::IsResourceReady() const
   return mModelResourceReady && mIblDiffuseResourceReady && mIblSpecularResourceReady;
 }
 
+void Model::CreateModelRoot()
+{
+  mModelRoot = Scene3D::ModelNode::New();
+  mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
+  mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION);
+  mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
+  mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
+  mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
+  Self().Add(mModelRoot);
+}
+
 void Model::ScaleModel()
 {
   if(!mModelRoot)
@@ -539,18 +593,45 @@ void Model::FitModelPosition()
   mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
 }
 
-void Model::CollectRenderableActor(Actor actor)
+void Model::UpdateImageBasedLightTextureRecursively(Scene3D::ModelNode node, Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
+{
+  if(!node)
+  {
+    return;
+  }
+
+  GetImplementation(node).SetImageBasedLightTexture(diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
+  uint32_t childrenCount = node.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
+  {
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(!childNode)
+    {
+      continue;
+    }
+    UpdateImageBasedLightTextureRecursively(childNode, diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
+  }
+}
+
+void Model::UpdateImageBasedLightScaleFactorRecursively(Scene3D::ModelNode node, float iblScaleFactor)
 {
-  uint32_t rendererCount = actor.GetRendererCount();
-  if(rendererCount)
+  if(!node)
   {
-    mRenderableActors.push_back(actor);
+    return;
   }
 
-  uint32_t childrenCount = actor.GetChildCount();
+  node.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), iblScaleFactor);
+  GetImplementation(node).SetImageBasedLightScaleFactor(iblScaleFactor);
+
+  uint32_t childrenCount = node.GetChildCount();
   for(uint32_t i = 0; i < childrenCount; ++i)
   {
-    CollectRenderableActor(actor.GetChildAt(i));
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(!childNode)
+    {
+      continue;
+    }
+    UpdateImageBasedLightScaleFactorRecursively(childNode, iblScaleFactor);
   }
 }
 
@@ -569,57 +650,7 @@ void Model::UpdateImageBasedLightTexture()
     currentIblSpecularMipmapLevels = 1u;
   }
 
-  for(auto&& actor : mRenderableActors)
-  {
-    Actor renderableActor = actor.GetHandle();
-    if(!renderableActor)
-    {
-      continue;
-    }
-
-    uint32_t rendererCount = renderableActor.GetRendererCount();
-    for(uint32_t i = 0; i < rendererCount; ++i)
-    {
-      Dali::Renderer renderer = renderableActor.GetRendererAt(i);
-      if(!renderer)
-      {
-        continue;
-      }
-      Dali::TextureSet textures = renderer.GetTextures();
-      if(!textures)
-      {
-        continue;
-      }
-      uint32_t textureCount = textures.GetTextureCount();
-      // EnvMap requires at least 2 texture, diffuse and specular
-      if(textureCount > 2u &&
-         (textures.GetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE) != currentDiffuseTexture ||
-          textures.GetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE) != currentSpecularTexture))
-      {
-        Dali::TextureSet newTextures = Dali::TextureSet::New();
-
-        for(uint32_t index = 0u; index < textureCount; ++index)
-        {
-          Dali::Texture texture = textures.GetTexture(index);
-          if(index == textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE)
-          {
-            texture = currentDiffuseTexture;
-          }
-          else if(index == textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE)
-          {
-            texture = currentSpecularTexture;
-          }
-
-          newTextures.SetTexture(index, texture);
-          newTextures.SetSampler(index, textures.GetSampler(index));
-        }
-
-        renderer.SetTextures(newTextures);
-      }
-    }
-    renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIblScaleFactor);
-    renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblMaxLodUniformName().data(), static_cast<float>(currentIblSpecularMipmapLevels));
-  }
+  UpdateImageBasedLightTextureRecursively(mModelRoot, currentDiffuseTexture, currentSpecularTexture, currentIblScaleFactor, currentIblSpecularMipmapLevels);
 }
 
 void Model::UpdateImageBasedLightScaleFactor()
@@ -631,14 +662,7 @@ void Model::UpdateImageBasedLightScaleFactor()
   }
 
   float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
-  for(auto&& actor : mRenderableActors)
-  {
-    Actor renderableActor = actor.GetHandle();
-    if(renderableActor)
-    {
-      renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIblScaleFactor);
-    }
-  }
+  UpdateImageBasedLightScaleFactorRecursively(mModelRoot, currentIblScaleFactor);
 }
 
 void Model::ApplyCameraTransform(Dali::CameraActor camera) const
@@ -711,7 +735,7 @@ void Model::OnModelLoadComplete()
   {
     ResetResourceTasks();
 
-    if(ModelCacheManager::Get())
+    if(ModelCacheManager::Get() && !mModelUrl.empty())
     {
       ModelCacheManager::Get().UnreferenceModelCache(mModelUrl);
     }
@@ -719,9 +743,11 @@ void Model::OnModelLoadComplete()
     return;
   }
 
+  if(!mModelRoot)
+  {
+    CreateModelRoot();
+  }
   CreateModel();
-  mRenderableActors.clear();
-  CollectRenderableActor(mModelRoot);
 
   auto& resources = mModelLoadTask->GetResources();
   auto& scene     = mModelLoadTask->GetScene();
@@ -735,12 +761,6 @@ void Model::OnModelLoadComplete()
 
   UpdateImageBasedLightTexture();
   UpdateImageBasedLightScaleFactor();
-
-  mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
-  mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
-  mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
-
-  Self().Add(mModelRoot);
   Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
 
   mModelResourceReady = true;
@@ -809,13 +829,10 @@ void Model::NotifyResourceReady()
 
 void Model::CreateModel()
 {
-  mModelRoot = Actor::New();
-  mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
-
   BoundingVolume                                      AABB;
-  auto&                                               resources        = mModelLoadTask->GetResources();
-  auto&                                               scene            = mModelLoadTask->GetScene();
-  auto&                                               resourceChoices  = mModelLoadTask->GetResourceChoices();
+  auto&                                               resources       = mModelLoadTask->GetResources();
+  auto&                                               scene           = mModelLoadTask->GetScene();
+  auto&                                               resourceChoices = mModelLoadTask->GetResourceChoices();
   Dali::Scene3D::Loader::Transforms                   xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
   Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, {}, {}, {}};
 
@@ -871,7 +888,7 @@ void Model::CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene)
     for(auto&& animation : mModelLoadTask->GetAnimations())
     {
       Dali::Animation anim = animation.ReAnimate(getActor);
-      mAnimations.push_back({animation.mName, anim});
+      mAnimations.push_back({animation.GetName(), anim});
     }
   }
 }
index e5e36d9..23aaf61 100644 (file)
@@ -33,6 +33,7 @@
 #include <dali-scene3d/public-api/controls/model/model.h>
 #include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
 #include <dali-scene3d/public-api/loader/load-result.h>
+#include <dali-scene3d/public-api/model-components/model-node.h>
 
 namespace Dali
 {
@@ -59,7 +60,17 @@ public:
   /**
    * @copydoc Model::GetModelRoot()
    */
-  const Actor GetModelRoot() const;
+  const Scene3D::ModelNode GetModelRoot() const;
+
+  /**
+   * @copydoc Model::AddModelNode()
+   */
+  void AddModelNode(Scene3D::ModelNode modelNode);
+
+  /**
+   * @copydoc Model::RemoveModelNode()
+   */
+  void RemoveModelNode(Scene3D::ModelNode modelNode);
 
   /**
    * @copydoc Model::SetChildrenSensitive()
@@ -126,6 +137,11 @@ public:
    */
   bool ApplyCamera(uint32_t index, Dali::CameraActor camera) const;
 
+  /**
+   * @copydoc Model::FindChildModelNodeByName()
+   */
+  Scene3D::ModelNode FindChildModelNodeByName(std::string_view nodeName);
+
 protected:
   /**
    * @brief Constructs a new Model.
@@ -182,6 +198,11 @@ private:
 
 private:
   /**
+   * @brief Create Model Root of this Model class.
+   */
+  void CreateModelRoot();
+
+  /**
    * @brief Scales the model to fit the control or to return to original size.
    */
   void ScaleModel();
@@ -194,7 +215,12 @@ private:
   /**
    * @brief Changes IBL information of the input node.
    */
-  void CollectRenderableActor(Actor actor);
+  void UpdateImageBasedLightTextureRecursively(Scene3D::ModelNode node, Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels);
+
+  /**
+   * @brief Changes IBL factor of the input node.
+   */
+  void UpdateImageBasedLightScaleFactorRecursively(Scene3D::ModelNode node, float iblScaleFactor);
 
   /**
    * @brief Changes IBL textures of the input node.
@@ -278,13 +304,12 @@ private:
   void ResetCameraParameters();
 
 private:
-  std::string                    mModelUrl;
-  std::string                    mResourceDirectoryUrl;
-  Dali::Actor                    mModelRoot;
-  std::vector<AnimationData>     mAnimations;
-  std::vector<CameraData>        mCameraParameters;
-  std::vector<WeakHandle<Actor>> mRenderableActors;
-  WeakHandle<Scene3D::SceneView> mParentSceneView;
+  std::string                                 mModelUrl;
+  std::string                                 mResourceDirectoryUrl;
+  Scene3D::ModelNode                          mModelRoot;
+  std::vector<AnimationData>                  mAnimations;
+  std::vector<CameraData>                     mCameraParameters;
+  WeakHandle<Scene3D::SceneView>              mParentSceneView;
 
   // Asynchronous loading variable
   ModelLoadTaskPtr          mModelLoadTask;
@@ -294,7 +319,6 @@ private:
   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;
   Dali::Texture mSceneSpecularTexture;
index e975368..c606f8f 100644 (file)
@@ -668,19 +668,10 @@ void SceneView::UpdateRenderTask()
         Property::Map imagePropertyMap;
         imagePropertyMap.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
         imagePropertyMap.Insert(Toolkit::ImageVisual::Property::URL, imageUrl.GetUrl());
-        // To make sure this visual call LoadTexture API immediate.
-        imagePropertyMap.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, Toolkit::ImageVisual::LoadPolicy::IMMEDIATE);
-        imagePropertyMap.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, Toolkit::ImageVisual::ReleasePolicy::DESTROYED);
         // To flip rendered scene without CameraActor::SetInvertYAxis() to avoid backface culling.
         imagePropertyMap.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, Vector4(0.0f, 1.0f, 1.0f, -1.0f));
         mVisual = Toolkit::VisualFactory::Get().CreateVisual(imagePropertyMap);
 
-        // Use premultiplied alpha when we use FBO
-        if(mVisual)
-        {
-          Toolkit::GetImplementation(mVisual).EnablePreMultipliedAlpha(true);
-        }
-
         Toolkit::DevelControl::RegisterVisual(*this, RENDERING_BUFFER, mVisual);
 
         mRenderTask.SetFrameBuffer(mFrameBuffer);
index cff1bca..063c0d1 100644 (file)
@@ -14,4 +14,8 @@ set(scene3d_src_files ${scene3d_src_files}
        ${scene3d_internal_dir}/loader/hash.cpp
        ${scene3d_internal_dir}/loader/json-reader.cpp
        ${scene3d_internal_dir}/loader/json-util.cpp
+       ${scene3d_internal_dir}/model-components/material-impl.cpp
+       ${scene3d_internal_dir}/model-components/model-node-data-impl.cpp
+       ${scene3d_internal_dir}/model-components/model-node-impl.cpp
+       ${scene3d_internal_dir}/model-components/model-primitive-impl.cpp
 )
index 022e21b..1508bf1 100644 (file)
@@ -88,9 +88,7 @@ uniform lowp float uAlphaThreshold;
 // TODO: Multiple texture coordinate will be supported.
 in mediump vec2 vUV;
 in lowp mat3 vTBN;
-#ifdef COLOR_ATTRIBUTE
 in lowp vec4 vColor;
-#endif
 in highp vec3 vPositionToCamera;
 
 out vec4 FragColor;
@@ -120,11 +118,7 @@ void main()
   lowp vec4 baseColor = texture(sAlbedoAlpha, vUV);
   baseColor = vec4(linear(baseColor.rgb), baseColor.w) * uColorFactor;
 #else // BASECOLOR_TEX
-#ifdef COLOR_ATTRIBUTE
   lowp vec4 baseColor = vColor * uColorFactor;
-#else // COLOR_ATTRIBUTE
-  lowp vec4 baseColor = uColorFactor;
-#endif // COLOR_ATTRIBUTE
 #endif // BASECOLOR_TEX
 
 #ifdef METALLIC_ROUGHNESS_TEX
@@ -139,11 +133,7 @@ void main()
 #endif // NORMAL_TEX
 #else // THREE_TEX
   vec4 albedoMetal = texture(sAlbedoMetal, vUV);
-#ifdef COLOR_ATTRIBUTE
   lowp vec4 baseColor = vec4(linear(albedoMetal.rgb), 1.0) * vColor * uColorFactor;
-#else // COLOR_ATTRIBUTE
-  lowp vec4 baseColor = vec4(linear(albedoMetal.rgb), 1.0) * uColorFactor;
-#endif // COLOR_ATTRIBUTE
 
   metallic = albedoMetal.METALLIC * metallic;
 
index 238ad20..4161b3a 100644 (file)
@@ -20,10 +20,7 @@ in vec4 aTangent;
 in vec3 aTangent;
 #endif
 
-#ifdef COLOR_ATTRIBUTE
 in vec4 aVertexColor;
-uniform lowp float uHasVertexColor;
-#endif
 
 #ifdef MORPH
   uniform highp sampler2D sBlendShapeGeometry;
@@ -31,9 +28,7 @@ uniform lowp float uHasVertexColor;
 
 out mediump vec2 vUV;
 out lowp mat3 vTBN;
-#ifdef COLOR_ATTRIBUTE
 out lowp vec4 vColor;
-#endif
 out highp vec3 vPositionToCamera;
 
 uniform highp mat4 uViewMatrix;
@@ -164,9 +159,7 @@ void main()
   vUV = aTexCoord;
 #endif
 
-#ifdef COLOR_ATTRIBUTE
-  vColor = mix(vec4(1.0f), aVertexColor, uHasVertexColor);
-#endif
+  vColor = aVertexColor;
 
   gl_Position = uProjection * positionV;
 }
index 26525e3..d684548 100644 (file)
@@ -61,19 +61,18 @@ namespace rs = RendererState;
 
 namespace
 {
-const std::string NODES         = "nodes";
-const std::string SCENES        = "scenes";
-const std::string NODE          = "node";
-const std::string URI           = "uri";
-const std::string URL           = "url";
-const std::string CUSTOMIZATION = "customization";
-const std::string HINTS         = "hints";
-const std::string NAME("name");
-const std::string BLEND_SHAPE_HEADER("blendShapeHeader");
-const std::string BLEND_SHAPES("blendShapes");
-const std::string BLEND_SHAPE_VERSION_1_0("1.0");
-const std::string BLEND_SHAPE_VERSION_2_0("2.0");
-const std::string VERSION("version");
+const char* NODES         = "nodes";
+const char* SCENES        = "scenes";
+const char* NODE          = "node";
+const char* URI           = "uri";
+const char* URL           = "url";
+const char* HINTS         = "hints";
+const char* NAME("name");
+const char* BLEND_SHAPE_HEADER("blendShapeHeader");
+const char* BLEND_SHAPES("blendShapes");
+const char* BLEND_SHAPE_VERSION_1_0("1.0");
+const char* BLEND_SHAPE_VERSION_2_0("2.0");
+const char* VERSION("version");
 
 const char* const SHADOW_MAP_SIZE   = "shadowMapSize";
 const char* const ORTHOGRAPHIC_SIZE = "orthographicSize";
@@ -1440,51 +1439,56 @@ void DliLoaderImpl::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadPara
       ++iAnim)
   {
     const TreeNode&     tnAnim = (*iAnim).second;
+    uint32_t animationPropertyIndex = 0;
     AnimationDefinition animDef;
-    ReadString(tnAnim.GetChild(NAME), animDef.mName);
+    std::string animationName;
+    ReadString(tnAnim.GetChild(NAME), animationName);
+    animDef.SetName(animationName);
 
-    auto       iFind     = std::lower_bound(definitions.begin(), definitions.end(), animDef, [](const AnimationDefinition& ad0, const AnimationDefinition& ad1) { return ad0.mName < ad1.mName; });
-    const bool overwrite = iFind != definitions.end() && iFind->mName == animDef.mName;
+    auto       iFind     = std::lower_bound(definitions.begin(), definitions.end(), animDef, [](const AnimationDefinition& ad0, const AnimationDefinition& ad1) { return ad0.GetName() < ad1.GetName(); });
+    const bool overwrite = iFind != definitions.end() && iFind->GetName() == animDef.GetName();
     if(overwrite)
     {
-      mOnError(FormatString("Pre-existing animation with name '%s' is being overwritten.", animDef.mName.c_str()));
+      mOnError(FormatString("Pre-existing animation with name '%s' is being overwritten.", animDef.GetName().c_str()));
     }
 
     // Duration -- We need something that animated properties' delay / duration can
     // be expressed as a multiple of; 0 won't work. This is small enough (i.e. shorter
     // than our frame delay) to not be restrictive WRT replaying. If anything needs
     // to occur more frequently, then Animations are likely not your solution anyway.
-    animDef.mDuration = AnimationDefinition::MIN_DURATION_SECONDS;
-    if(!ReadFloat(tnAnim.GetChild("duration"), animDef.mDuration))
+    animDef.SetDuration(AnimationDefinition::MIN_DURATION_SECONDS);
+    float animationDuration;
+    if(!ReadFloat(tnAnim.GetChild("duration"), animationDuration))
     {
+      animDef.SetDuration(animationDuration);
       mOnError(FormatString("Animation '%s' fails to define '%s', defaulting to %f.",
-                            animDef.mName.c_str(),
+                            animDef.GetName().c_str(),
                             "duration",
-                            animDef.mDuration));
+                            animDef.GetDuration()));
     }
 
     // Get loop count - # of playbacks. Default is once. 0 means repeat indefinitely.
-    animDef.mLoopCount = 1;
-    if(ReadInt(tnAnim.GetChild("loopCount"), animDef.mLoopCount) &&
-       animDef.mLoopCount < 0)
+    int32_t animationLoopCount = 1;
+    if(ReadInt(tnAnim.GetChild("loopCount"), animationLoopCount) && animationLoopCount < 0)
     {
-      animDef.mLoopCount = 0;
+      animationLoopCount = 0;
     }
+    animDef.SetLoopCount(animationLoopCount);
 
     std::string endAction;
     if(ReadString(tnAnim.GetChild("endAction"), endAction))
     {
       if("BAKE" == endAction)
       {
-        animDef.mEndAction = Animation::BAKE;
+        animDef.SetEndAction(Animation::BAKE);
       }
       else if("DISCARD" == endAction)
       {
-        animDef.mEndAction = Animation::DISCARD;
+        animDef.SetEndAction(Animation::DISCARD);
       }
       else if("BAKE_FINAL" == endAction)
       {
-        animDef.mEndAction = Animation::BAKE_FINAL;
+        animDef.SetEndAction(Animation::BAKE_FINAL);
       }
     }
 
@@ -1492,21 +1496,21 @@ void DliLoaderImpl::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadPara
     {
       if("BAKE" == endAction)
       {
-        animDef.mDisconnectAction = Animation::BAKE;
+        animDef.SetDisconnectAction(Animation::BAKE);
       }
       else if("DISCARD" == endAction)
       {
-        animDef.mDisconnectAction = Animation::DISCARD;
+        animDef.SetDisconnectAction(Animation::DISCARD);
       }
       else if("BAKE_FINAL" == endAction)
       {
-        animDef.mDisconnectAction = Animation::BAKE_FINAL;
+        animDef.SetDisconnectAction(Animation::BAKE_FINAL);
       }
     }
 
     if(const TreeNode* tnProperties = tnAnim.GetChild("properties"))
     {
-      animDef.mProperties.reserve(tnProperties->Size());
+      animDef.ReserveSize(tnProperties->Size());
       for(TreeNode::ConstIterator iProperty = tnProperties->CBegin(), iPropertyEnd = tnProperties->CEnd();
           iProperty != iPropertyEnd;
           ++iProperty)
@@ -1516,24 +1520,24 @@ void DliLoaderImpl::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadPara
         AnimatedProperty animProp;
         if(!ReadString(tnProperty.GetChild("node"), animProp.mNodeName))
         {
-          mOnError(FormatString("Animation '%s': Failed to read the 'node' tag.", animDef.mName.c_str()));
+          mOnError(FormatString("Animation '%s': Failed to read the 'node' tag.", animDef.GetName().c_str()));
           continue;
         }
 
         if(!ReadString(tnProperty.GetChild("property"), animProp.mPropertyName))
         {
-          mOnError(FormatString("Animation '%s': Failed to read the 'property' tag", animDef.mName.c_str()));
+          mOnError(FormatString("Animation '%s': Failed to read the 'property' tag", animDef.GetName().c_str()));
           continue;
         }
 
         // these are the defaults
         animProp.mTimePeriod.delaySeconds    = 0.f;
-        animProp.mTimePeriod.durationSeconds = animDef.mDuration;
+        animProp.mTimePeriod.durationSeconds = animDef.GetDuration();
         if(!ReadTimePeriod(tnProperty.GetChild("timePeriod"), animProp.mTimePeriod))
         {
           mOnError(FormatString("Animation '%s': timePeriod missing in Property #%d: defaulting to %f.",
-                                animDef.mName.c_str(),
-                                animDef.mProperties.size(),
+                                animDef.GetName().c_str(),
+                                animDef.GetPropertyCount(),
                                 animProp.mTimePeriod.durationSeconds));
         }
 
@@ -1669,10 +1673,17 @@ void DliLoaderImpl::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadPara
           }
         }
 
-        animDef.mProperties.push_back(std::move(animProp));
+        animDef.SetProperty(animationPropertyIndex++, std::move(animProp));
       }
     }
 
+    if(auto proc = params.input->mAnimationPropertyProcessor) // optional processing
+    {
+      Property::Map map;
+      ParseProperties(tnAnim, map);
+      proc(animDef, std::move(map), mOnError);
+    }
+
     if(overwrite)
     {
       *iFind = std::move(animDef);
@@ -1681,13 +1692,6 @@ void DliLoaderImpl::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadPara
     {
       iFind = definitions.insert(iFind, std::move(animDef));
     }
-
-    if(auto proc = params.input->mAnimationPropertyProcessor) // optional processing
-    {
-      Property::Map map;
-      ParseProperties(tnAnim, map);
-      proc(animDef, std::move(map), mOnError);
-    }
   }
 }
 
index 1012292..c2a932f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2022 Samsung Electronics Co., Ltd.
+* Copyright (c) 2023 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.
@@ -40,53 +40,73 @@ constexpr uint32_t ACCESSOR_TYPE_ELEMENT_COUNT[]{
   static_cast<uint32_t>(-1),
 };
 
-const std::map<std::string_view, AccessorType::Type> ACCESSOR_TYPES{
-  ENUM_STRING_MAPPING(AccessorType, SCALAR),
-  ENUM_STRING_MAPPING(AccessorType, VEC2),
-  ENUM_STRING_MAPPING(AccessorType, VEC3),
-  ENUM_STRING_MAPPING(AccessorType, VEC4),
-  ENUM_STRING_MAPPING(AccessorType, MAT2),
-  ENUM_STRING_MAPPING(AccessorType, MAT3),
-  ENUM_STRING_MAPPING(AccessorType, MAT4),
-};
+const std::map<std::string_view, AccessorType::Type>& GetAccessorTypes()
+{
+  static const std::map<std::string_view, AccessorType::Type> ACCESSOR_TYPES{
+    ENUM_STRING_MAPPING(AccessorType, SCALAR),
+    ENUM_STRING_MAPPING(AccessorType, VEC2),
+    ENUM_STRING_MAPPING(AccessorType, VEC3),
+    ENUM_STRING_MAPPING(AccessorType, VEC4),
+    ENUM_STRING_MAPPING(AccessorType, MAT2),
+    ENUM_STRING_MAPPING(AccessorType, MAT3),
+    ENUM_STRING_MAPPING(AccessorType, MAT4),
+  };
+  return ACCESSOR_TYPES;
+}
 
-const std::map<std::string_view, AlphaMode::Type> ALPHA_MODE_TYPES{
-  ENUM_STRING_MAPPING(AlphaMode::Type, OPAQUE),
-  ENUM_STRING_MAPPING(AlphaMode::Type, MASK),
-  ENUM_STRING_MAPPING(AlphaMode::Type, BLEND),
-};
+const std::map<std::string_view, AlphaMode::Type>& GetAlphaModeTypes()
+{
+  static const std::map<std::string_view, AlphaMode::Type> ALPHA_MODE_TYPES{
+    ENUM_STRING_MAPPING(AlphaMode::Type, OPAQUE),
+    ENUM_STRING_MAPPING(AlphaMode::Type, MASK),
+    ENUM_STRING_MAPPING(AlphaMode::Type, BLEND),
+  };
+  return ALPHA_MODE_TYPES;
+}
 
-const std::map<std::string_view, Attribute::Type> ATTRIBUTE_TYPES{
-  ENUM_STRING_MAPPING(Attribute::Type, POSITION),
-  ENUM_STRING_MAPPING(Attribute::Type, NORMAL),
-  ENUM_STRING_MAPPING(Attribute::Type, TANGENT),
-  ENUM_STRING_MAPPING(Attribute::Type, TEXCOORD_0),
-  ENUM_STRING_MAPPING(Attribute::Type, TEXCOORD_1),
-  ENUM_STRING_MAPPING(Attribute::Type, COLOR_0),
-  ENUM_STRING_MAPPING(Attribute::Type, JOINTS_0),
-  ENUM_STRING_MAPPING(Attribute::Type, WEIGHTS_0),
-};
+const std::map<std::string_view, Attribute::Type>& GetAttributeTypes()
+{
+  static const std::map<std::string_view, Attribute::Type> ATTRIBUTE_TYPES{
+    ENUM_STRING_MAPPING(Attribute::Type, POSITION),
+    ENUM_STRING_MAPPING(Attribute::Type, NORMAL),
+    ENUM_STRING_MAPPING(Attribute::Type, TANGENT),
+    ENUM_STRING_MAPPING(Attribute::Type, TEXCOORD_0),
+    ENUM_STRING_MAPPING(Attribute::Type, TEXCOORD_1),
+    ENUM_STRING_MAPPING(Attribute::Type, COLOR_0),
+    ENUM_STRING_MAPPING(Attribute::Type, JOINTS_0),
+    ENUM_STRING_MAPPING(Attribute::Type, WEIGHTS_0),
+  };
+  return ATTRIBUTE_TYPES;
+}
 
-const std::map<std::string_view, Animation::Sampler::Interpolation::Type> ANIMATION_SAMPLER_INTERPOLATION{
-  ENUM_STRING_MAPPING(Animation::Sampler::Interpolation::Type, STEP),
-  ENUM_STRING_MAPPING(Animation::Sampler::Interpolation::Type, LINEAR),
-  ENUM_STRING_MAPPING(Animation::Sampler::Interpolation::Type, CUBICSPLINE),
-};
+const std::map<std::string_view, Animation::Sampler::Interpolation::Type>& GetAnimationSamplerInterpolation()
+{
+  static const std::map<std::string_view, Animation::Sampler::Interpolation::Type> ANIMATION_SAMPLER_INTERPOLATION{
+    ENUM_STRING_MAPPING(Animation::Sampler::Interpolation::Type, STEP),
+    ENUM_STRING_MAPPING(Animation::Sampler::Interpolation::Type, LINEAR),
+    ENUM_STRING_MAPPING(Animation::Sampler::Interpolation::Type, CUBICSPLINE),
+  };
+  return ANIMATION_SAMPLER_INTERPOLATION;
+}
 
-const std::map<std::string_view, Animation::Channel::Target::Type> ANIMATION_CHANNEL_TARGET_PATH_TYPES{
-  ENUM_STRING_MAPPING(Animation::Channel::Target::Type, TRANSLATION),
-  ENUM_STRING_MAPPING(Animation::Channel::Target::Type, ROTATION),
-  ENUM_STRING_MAPPING(Animation::Channel::Target::Type, SCALE),
-  ENUM_STRING_MAPPING(Animation::Channel::Target::Type, WEIGHTS),
-};
+const std::map<std::string_view, Animation::Channel::Target::Type>& GetAnimationChannelTargetPathTypes()
+{
+  static const std::map<std::string_view, Animation::Channel::Target::Type> ANIMATION_CHANNEL_TARGET_PATH_TYPES{
+    ENUM_STRING_MAPPING(Animation::Channel::Target::Type, TRANSLATION),
+    ENUM_STRING_MAPPING(Animation::Channel::Target::Type, ROTATION),
+    ENUM_STRING_MAPPING(Animation::Channel::Target::Type, SCALE),
+    ENUM_STRING_MAPPING(Animation::Channel::Target::Type, WEIGHTS),
+  };
+  return ANIMATION_CHANNEL_TARGET_PATH_TYPES;
+}
 
 } // namespace
 
-ENUM_TYPE_FROM_STRING(AccessorType, ACCESSOR_TYPES)
-ENUM_TYPE_FROM_STRING(AlphaMode, ALPHA_MODE_TYPES)
-ENUM_TYPE_FROM_STRING(Attribute, ATTRIBUTE_TYPES)
-ENUM_TYPE_FROM_STRING(Animation::Sampler::Interpolation, ANIMATION_SAMPLER_INTERPOLATION)
-ENUM_TYPE_FROM_STRING(Animation::Channel::Target, ANIMATION_CHANNEL_TARGET_PATH_TYPES)
+ENUM_TYPE_FROM_STRING(AccessorType, GetAccessorTypes())
+ENUM_TYPE_FROM_STRING(AlphaMode, GetAlphaModeTypes())
+ENUM_TYPE_FROM_STRING(Attribute, GetAttributeTypes())
+ENUM_TYPE_FROM_STRING(Animation::Sampler::Interpolation, GetAnimationSamplerInterpolation())
+ENUM_TYPE_FROM_STRING(Animation::Channel::Target, GetAnimationChannelTargetPathTypes())
 
 bool Component::IsUnsigned(Type t)
 {
index 68dd8bb..38ddfcf 100644 (file)
@@ -19,6 +19,7 @@
 #include <dali-scene3d/internal/loader/gltf2-util.h>
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/threading/mutex.h>
 #include <dali/integration-api/debug.h>
 
 using namespace Dali::Scene3D::Loader;
@@ -80,102 +81,162 @@ void ApplyAccessorMinMax(const gltf2::Accessor& accessor, float* values)
   MeshDefinition::Blob::ApplyMinMax(accessor.mMin, accessor.mMax, accessor.mCount, values);
 }
 
-const auto BUFFER_READER = std::move(json::Reader<gltf2::Buffer>()
-                                       .Register(*json::MakeProperty("byteLength", json::Read::Number<uint32_t>, &gltf2::Buffer::mByteLength))
-                                       .Register(*json::MakeProperty("uri", json::Read::StringView, &gltf2::Buffer::mUri)));
-
-const auto BUFFER_VIEW_READER = std::move(json::Reader<gltf2::BufferView>()
-                                            .Register(*json::MakeProperty("buffer", gltf2::RefReader<gltf2::Document>::Read<gltf2::Buffer, &gltf2::Document::mBuffers>, &gltf2::BufferView::mBuffer))
-                                            .Register(*json::MakeProperty("byteOffset", json::Read::Number<uint32_t>, &gltf2::BufferView::mByteOffset))
-                                            .Register(*json::MakeProperty("byteLength", json::Read::Number<uint32_t>, &gltf2::BufferView::mByteLength))
-                                            .Register(*json::MakeProperty("byteStride", json::Read::Number<uint32_t>, &gltf2::BufferView::mByteStride))
-                                            .Register(*json::MakeProperty("target", json::Read::Number<uint32_t>, &gltf2::BufferView::mTarget)));
-
-const auto BUFFER_VIEW_CLIENT_READER = std::move(json::Reader<gltf2::BufferViewClient>()
-                                                   .Register(*json::MakeProperty("bufferView", gltf2::RefReader<gltf2::Document>::Read<gltf2::BufferView, &gltf2::Document::mBufferViews>, &gltf2::BufferViewClient::mBufferView))
-                                                   .Register(*json::MakeProperty("byteOffset", json::Read::Number<uint32_t>, &gltf2::BufferViewClient::mByteOffset)));
-
-const auto COMPONENT_TYPED_BUFFER_VIEW_CLIENT_READER = std::move(json::Reader<gltf2::ComponentTypedBufferViewClient>()
-                                                                   .Register(*new json::Property<gltf2::ComponentTypedBufferViewClient, gltf2::Ref<gltf2::BufferView>>("bufferView", gltf2::RefReader<gltf2::Document>::Read<gltf2::BufferView, &gltf2::Document::mBufferViews>, &gltf2::ComponentTypedBufferViewClient::mBufferView))
-                                                                   .Register(*new json::Property<gltf2::ComponentTypedBufferViewClient, uint32_t>("byteOffset", json::Read::Number<uint32_t>, &gltf2::ComponentTypedBufferViewClient::mByteOffset))
-                                                                   .Register(*json::MakeProperty("componentType", json::Read::Enum<gltf2::Component::Type>, &gltf2::ComponentTypedBufferViewClient::mComponentType)));
-
-const auto ACCESSOR_SPARSE_READER = std::move(json::Reader<gltf2::Accessor::Sparse>()
-                                                .Register(*json::MakeProperty("count", json::Read::Number<uint32_t>, &gltf2::Accessor::Sparse::mCount))
-                                                .Register(*json::MakeProperty("indices", json::ObjectReader<gltf2::ComponentTypedBufferViewClient>::Read, &gltf2::Accessor::Sparse::mIndices))
-                                                .Register(*json::MakeProperty("values", json::ObjectReader<gltf2::BufferViewClient>::Read, &gltf2::Accessor::Sparse::mValues)));
-
-const auto ACCESSOR_READER = std::move(json::Reader<gltf2::Accessor>()
-                                         .Register(*new json::Property<gltf2::Accessor, gltf2::Ref<gltf2::BufferView>>("bufferView",
-                                                                                                                       gltf2::RefReader<gltf2::Document>::Read<gltf2::BufferView, &gltf2::Document::mBufferViews>,
-                                                                                                                       &gltf2::Accessor::mBufferView))
-                                         .Register(*new json::Property<gltf2::Accessor, uint32_t>("byteOffset",
-                                                                                                  json::Read::Number<uint32_t>,
-                                                                                                  &gltf2::Accessor::mByteOffset))
-                                         .Register(*new json::Property<gltf2::Accessor, gltf2::Component::Type>("componentType",
-                                                                                                                json::Read::Enum<gltf2::Component::Type>,
-                                                                                                                &gltf2::Accessor::mComponentType))
-                                         .Register(*new json::Property<gltf2::Accessor, std::string_view>("name", json::Read::StringView, &gltf2::Accessor::mName))
-                                         .Register(*json::MakeProperty("count", json::Read::Number<uint32_t>, &gltf2::Accessor::mCount))
-                                         .Register(*json::MakeProperty("normalized", json::Read::Boolean, &gltf2::Accessor::mNormalized))
-                                         .Register(*json::MakeProperty("type", gltf2::ReadStringEnum<gltf2::AccessorType>, &gltf2::Accessor::mType))
-                                         .Register(*json::MakeProperty("min", json::Read::Array<float, json::Read::Number>, &gltf2::Accessor::mMin))
-                                         .Register(*json::MakeProperty("max", json::Read::Array<float, json::Read::Number>, &gltf2::Accessor::mMax))
-                                         .Register(*new json::Property<gltf2::Accessor, gltf2::Accessor::Sparse>("sparse", json::ObjectReader<gltf2::Accessor::Sparse>::Read, &gltf2::Accessor::SetSparse)));
-
-const auto IMAGE_READER = std::move(json::Reader<gltf2::Image>()
-                                      .Register(*new json::Property<gltf2::Image, std::string_view>("name", json::Read::StringView, &gltf2::Material::mName))
-                                      .Register(*json::MakeProperty("uri", json::Read::StringView, &gltf2::Image::mUri))
-                                      .Register(*json::MakeProperty("mimeType", json::Read::StringView, &gltf2::Image::mMimeType))
-                                      .Register(*json::MakeProperty("bufferView", gltf2::RefReader<gltf2::Document>::Read<gltf2::BufferView, &gltf2::Document::mBufferViews>, &gltf2::Image::mBufferView)));
-
-const auto SAMPLER_READER = std::move(json::Reader<gltf2::Sampler>()
-                                        .Register(*json::MakeProperty("minFilter", json::Read::Enum<gltf2::Filter::Type>, &gltf2::Sampler::mMinFilter))
-                                        .Register(*json::MakeProperty("magFilter", json::Read::Enum<gltf2::Filter::Type>, &gltf2::Sampler::mMagFilter))
-                                        .Register(*json::MakeProperty("wrapS", json::Read::Enum<gltf2::Wrap::Type>, &gltf2::Sampler::mWrapS))
-                                        .Register(*json::MakeProperty("wrapT", json::Read::Enum<gltf2::Wrap::Type>, &gltf2::Sampler::mWrapT)));
-
-const auto TEXURE_READER = std::move(json::Reader<gltf2::Texture>()
-                                       .Register(*json::MakeProperty("source", gltf2::RefReader<gltf2::Document>::Read<gltf2::Image, &gltf2::Document::mImages>, &gltf2::Texture::mSource))
-                                       .Register(*json::MakeProperty("sampler", gltf2::RefReader<gltf2::Document>::Read<gltf2::Sampler, &gltf2::Document::mSamplers>, &gltf2::Texture::mSampler)));
-
-const auto TEXURE_INFO_READER = std::move(json::Reader<gltf2::TextureInfo>()
-                                            .Register(*json::MakeProperty("index", gltf2::RefReader<gltf2::Document>::Read<gltf2::Texture, &gltf2::Document::mTextures>, &gltf2::TextureInfo::mTexture))
-                                            .Register(*json::MakeProperty("texCoord", json::Read::Number<uint32_t>, &gltf2::TextureInfo::mTexCoord))
-                                            .Register(*json::MakeProperty("scale", json::Read::Number<float>, &gltf2::TextureInfo::mScale))
-                                            .Register(*json::MakeProperty("strength", json::Read::Number<float>, &gltf2::TextureInfo::mStrength)));
-
-const auto MATERIAL_PBR_READER = std::move(json::Reader<gltf2::Material::Pbr>()
-                                             .Register(*json::MakeProperty("baseColorFactor", gltf2::ReadDaliVector<Vector4>, &gltf2::Material::Pbr::mBaseColorFactor))
-                                             .Register(*json::MakeProperty("baseColorTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::Material::Pbr::mBaseColorTexture))
-                                             .Register(*json::MakeProperty("metallicFactor", json::Read::Number<float>, &gltf2::Material::Pbr::mMetallicFactor))
-                                             .Register(*json::MakeProperty("roughnessFactor", json::Read::Number<float>, &gltf2::Material::Pbr::mRoughnessFactor))
-                                             .Register(*json::MakeProperty("metallicRoughnessTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::Material::Pbr::mMetallicRoughnessTexture)));
-
-const auto MATERIAL_SPECULAR_READER = std::move(json::Reader<gltf2::MaterialSpecular>()
-                                                  .Register(*json::MakeProperty("specularFactor", json::Read::Number<float>, &gltf2::MaterialSpecular::mSpecularFactor))
-                                                  .Register(*json::MakeProperty("specularTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::MaterialSpecular::mSpecularTexture))
-                                                  .Register(*json::MakeProperty("specularColorFactor", gltf2::ReadDaliVector<Vector3>, &gltf2::MaterialSpecular::mSpecularColorFactor))
-                                                  .Register(*json::MakeProperty("specularColorTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::MaterialSpecular::mSpecularColorTexture)));
-
-const auto MATERIAL_IOR_READER = std::move(json::Reader<gltf2::MaterialIor>()
-                                             .Register(*json::MakeProperty("ior", json::Read::Number<float>, &gltf2::MaterialIor::mIor)));
-
-const auto MATERIAL_EXTENSION_READER = std::move(json::Reader<gltf2::MaterialExtensions>()
-                                                   .Register(*json::MakeProperty("KHR_materials_ior", json::ObjectReader<gltf2::MaterialIor>::Read, &gltf2::MaterialExtensions::mMaterialIor))
-                                                   .Register(*json::MakeProperty("KHR_materials_specular", json::ObjectReader<gltf2::MaterialSpecular>::Read, &gltf2::MaterialExtensions::mMaterialSpecular)));
-
-const auto MATERIAL_READER = std::move(json::Reader<gltf2::Material>()
-                                         .Register(*new json::Property<gltf2::Material, std::string_view>("name", json::Read::StringView, &gltf2::Material::mName))
-                                         .Register(*json::MakeProperty("pbrMetallicRoughness", json::ObjectReader<gltf2::Material::Pbr>::Read, &gltf2::Material::mPbrMetallicRoughness))
-                                         .Register(*json::MakeProperty("normalTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::Material::mNormalTexture))
-                                         .Register(*json::MakeProperty("occlusionTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::Material::mOcclusionTexture))
-                                         .Register(*json::MakeProperty("emissiveTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::Material::mEmissiveTexture))
-                                         .Register(*json::MakeProperty("emissiveFactor", gltf2::ReadDaliVector<Vector3>, &gltf2::Material::mEmissiveFactor))
-                                         .Register(*json::MakeProperty("alphaMode", gltf2::ReadStringEnum<gltf2::AlphaMode>, &gltf2::Material::mAlphaMode))
-                                         .Register(*json::MakeProperty("alphaCutoff", json::Read::Number<float>, &gltf2::Material::mAlphaCutoff))
-                                         .Register(*json::MakeProperty("doubleSided", json::Read::Boolean, &gltf2::Material::mDoubleSided))
-                                         .Register(*json::MakeProperty("extensions", json::ObjectReader<gltf2::MaterialExtensions>::Read, &gltf2::Material::mMaterialExtensions)));
+const json::Reader<gltf2::Buffer>& GetBufferReader()
+{
+  static const auto BUFFER_READER = std::move(json::Reader<gltf2::Buffer>()
+                                                .Register(*json::MakeProperty("byteLength", json::Read::Number<uint32_t>, &gltf2::Buffer::mByteLength))
+                                                .Register(*json::MakeProperty("uri", json::Read::StringView, &gltf2::Buffer::mUri)));
+  return BUFFER_READER;
+}
+
+const json::Reader<gltf2::BufferView>& GetBufferViewReader()
+{
+  static const auto BUFFER_VIEW_READER = std::move(json::Reader<gltf2::BufferView>()
+                                                     .Register(*json::MakeProperty("buffer", gltf2::RefReader<gltf2::Document>::Read<gltf2::Buffer, &gltf2::Document::mBuffers>, &gltf2::BufferView::mBuffer))
+                                                     .Register(*json::MakeProperty("byteOffset", json::Read::Number<uint32_t>, &gltf2::BufferView::mByteOffset))
+                                                     .Register(*json::MakeProperty("byteLength", json::Read::Number<uint32_t>, &gltf2::BufferView::mByteLength))
+                                                     .Register(*json::MakeProperty("byteStride", json::Read::Number<uint32_t>, &gltf2::BufferView::mByteStride))
+                                                     .Register(*json::MakeProperty("target", json::Read::Number<uint32_t>, &gltf2::BufferView::mTarget)));
+  return BUFFER_VIEW_READER;
+}
+
+const json::Reader<gltf2::BufferViewClient>& GetBufferViewClientReader()
+{
+  static const auto BUFFER_VIEW_CLIENT_READER = std::move(json::Reader<gltf2::BufferViewClient>()
+                                                            .Register(*json::MakeProperty("bufferView", gltf2::RefReader<gltf2::Document>::Read<gltf2::BufferView, &gltf2::Document::mBufferViews>, &gltf2::BufferViewClient::mBufferView))
+                                                            .Register(*json::MakeProperty("byteOffset", json::Read::Number<uint32_t>, &gltf2::BufferViewClient::mByteOffset)));
+  return BUFFER_VIEW_CLIENT_READER;
+}
+
+const json::Reader<gltf2::ComponentTypedBufferViewClient>& GetComponentTypedBufferViewClientReader()
+{
+  static const auto COMPONENT_TYPED_BUFFER_VIEW_CLIENT_READER = std::move(json::Reader<gltf2::ComponentTypedBufferViewClient>()
+                                                                            .Register(*new json::Property<gltf2::ComponentTypedBufferViewClient, gltf2::Ref<gltf2::BufferView>>("bufferView", gltf2::RefReader<gltf2::Document>::Read<gltf2::BufferView, &gltf2::Document::mBufferViews>, &gltf2::ComponentTypedBufferViewClient::mBufferView))
+                                                                            .Register(*new json::Property<gltf2::ComponentTypedBufferViewClient, uint32_t>("byteOffset", json::Read::Number<uint32_t>, &gltf2::ComponentTypedBufferViewClient::mByteOffset))
+                                                                            .Register(*json::MakeProperty("componentType", json::Read::Enum<gltf2::Component::Type>, &gltf2::ComponentTypedBufferViewClient::mComponentType)));
+  return COMPONENT_TYPED_BUFFER_VIEW_CLIENT_READER;
+}
+
+const json::Reader<gltf2::Accessor::Sparse>& GetAccessorSparseReader()
+{
+  static const auto ACCESSOR_SPARSE_READER = std::move(json::Reader<gltf2::Accessor::Sparse>()
+                                                         .Register(*json::MakeProperty("count", json::Read::Number<uint32_t>, &gltf2::Accessor::Sparse::mCount))
+                                                         .Register(*json::MakeProperty("indices", json::ObjectReader<gltf2::ComponentTypedBufferViewClient>::Read, &gltf2::Accessor::Sparse::mIndices))
+                                                         .Register(*json::MakeProperty("values", json::ObjectReader<gltf2::BufferViewClient>::Read, &gltf2::Accessor::Sparse::mValues)));
+  return ACCESSOR_SPARSE_READER;
+}
+
+const json::Reader<gltf2::Accessor>& GetAccessorReader()
+{
+  static const auto ACCESSOR_READER = std::move(json::Reader<gltf2::Accessor>()
+                                                  .Register(*new json::Property<gltf2::Accessor, gltf2::Ref<gltf2::BufferView>>("bufferView",
+                                                                                                                                gltf2::RefReader<gltf2::Document>::Read<gltf2::BufferView, &gltf2::Document::mBufferViews>,
+                                                                                                                                &gltf2::Accessor::mBufferView))
+                                                  .Register(*new json::Property<gltf2::Accessor, uint32_t>("byteOffset",
+                                                                                                           json::Read::Number<uint32_t>,
+                                                                                                           &gltf2::Accessor::mByteOffset))
+                                                  .Register(*new json::Property<gltf2::Accessor, gltf2::Component::Type>("componentType",
+                                                                                                                         json::Read::Enum<gltf2::Component::Type>,
+                                                                                                                         &gltf2::Accessor::mComponentType))
+                                                  .Register(*new json::Property<gltf2::Accessor, std::string_view>("name", json::Read::StringView, &gltf2::Accessor::mName))
+                                                  .Register(*json::MakeProperty("count", json::Read::Number<uint32_t>, &gltf2::Accessor::mCount))
+                                                  .Register(*json::MakeProperty("normalized", json::Read::Boolean, &gltf2::Accessor::mNormalized))
+                                                  .Register(*json::MakeProperty("type", gltf2::ReadStringEnum<gltf2::AccessorType>, &gltf2::Accessor::mType))
+                                                  .Register(*json::MakeProperty("min", json::Read::Array<float, json::Read::Number>, &gltf2::Accessor::mMin))
+                                                  .Register(*json::MakeProperty("max", json::Read::Array<float, json::Read::Number>, &gltf2::Accessor::mMax))
+                                                  .Register(*new json::Property<gltf2::Accessor, gltf2::Accessor::Sparse>("sparse", json::ObjectReader<gltf2::Accessor::Sparse>::Read, &gltf2::Accessor::SetSparse)));
+  return ACCESSOR_READER;
+}
+
+const json::Reader<gltf2::Image>& GetImageReader()
+{
+  static const auto IMAGE_READER = std::move(json::Reader<gltf2::Image>()
+                                               .Register(*new json::Property<gltf2::Image, std::string_view>("name", json::Read::StringView, &gltf2::Material::mName))
+                                               .Register(*json::MakeProperty("uri", json::Read::StringView, &gltf2::Image::mUri))
+                                               .Register(*json::MakeProperty("mimeType", json::Read::StringView, &gltf2::Image::mMimeType))
+                                               .Register(*json::MakeProperty("bufferView", gltf2::RefReader<gltf2::Document>::Read<gltf2::BufferView, &gltf2::Document::mBufferViews>, &gltf2::Image::mBufferView)));
+  return IMAGE_READER;
+}
+
+const json::Reader<gltf2::Sampler>& GetSamplerReader()
+{
+  static const auto SAMPLER_READER = std::move(json::Reader<gltf2::Sampler>()
+                                                 .Register(*json::MakeProperty("minFilter", json::Read::Enum<gltf2::Filter::Type>, &gltf2::Sampler::mMinFilter))
+                                                 .Register(*json::MakeProperty("magFilter", json::Read::Enum<gltf2::Filter::Type>, &gltf2::Sampler::mMagFilter))
+                                                 .Register(*json::MakeProperty("wrapS", json::Read::Enum<gltf2::Wrap::Type>, &gltf2::Sampler::mWrapS))
+                                                 .Register(*json::MakeProperty("wrapT", json::Read::Enum<gltf2::Wrap::Type>, &gltf2::Sampler::mWrapT)));
+  return SAMPLER_READER;
+}
+
+const json::Reader<gltf2::Texture>& GetTextureReader()
+{
+  static const auto TEXURE_READER = std::move(json::Reader<gltf2::Texture>()
+                                                .Register(*json::MakeProperty("source", gltf2::RefReader<gltf2::Document>::Read<gltf2::Image, &gltf2::Document::mImages>, &gltf2::Texture::mSource))
+                                                .Register(*json::MakeProperty("sampler", gltf2::RefReader<gltf2::Document>::Read<gltf2::Sampler, &gltf2::Document::mSamplers>, &gltf2::Texture::mSampler)));
+  return TEXURE_READER;
+}
+
+const json::Reader<gltf2::TextureInfo>& GetTextureInfoReader()
+{
+  static const auto TEXURE_INFO_READER = std::move(json::Reader<gltf2::TextureInfo>()
+                                                     .Register(*json::MakeProperty("index", gltf2::RefReader<gltf2::Document>::Read<gltf2::Texture, &gltf2::Document::mTextures>, &gltf2::TextureInfo::mTexture))
+                                                     .Register(*json::MakeProperty("texCoord", json::Read::Number<uint32_t>, &gltf2::TextureInfo::mTexCoord))
+                                                     .Register(*json::MakeProperty("scale", json::Read::Number<float>, &gltf2::TextureInfo::mScale))
+                                                     .Register(*json::MakeProperty("strength", json::Read::Number<float>, &gltf2::TextureInfo::mStrength)));
+  return TEXURE_INFO_READER;
+}
+
+const json::Reader<gltf2::Material::Pbr>& GetMaterialPbrReader()
+{
+  static const auto MATERIAL_PBR_READER = std::move(json::Reader<gltf2::Material::Pbr>()
+                                                      .Register(*json::MakeProperty("baseColorFactor", gltf2::ReadDaliVector<Vector4>, &gltf2::Material::Pbr::mBaseColorFactor))
+                                                      .Register(*json::MakeProperty("baseColorTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::Material::Pbr::mBaseColorTexture))
+                                                      .Register(*json::MakeProperty("metallicFactor", json::Read::Number<float>, &gltf2::Material::Pbr::mMetallicFactor))
+                                                      .Register(*json::MakeProperty("roughnessFactor", json::Read::Number<float>, &gltf2::Material::Pbr::mRoughnessFactor))
+                                                      .Register(*json::MakeProperty("metallicRoughnessTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::Material::Pbr::mMetallicRoughnessTexture)));
+  return MATERIAL_PBR_READER;
+}
+
+const json::Reader<gltf2::MaterialSpecular>& GetMaterialSpecularReader()
+{
+  static const auto MATERIAL_SPECULAR_READER = std::move(json::Reader<gltf2::MaterialSpecular>()
+                                                           .Register(*json::MakeProperty("specularFactor", json::Read::Number<float>, &gltf2::MaterialSpecular::mSpecularFactor))
+                                                           .Register(*json::MakeProperty("specularTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::MaterialSpecular::mSpecularTexture))
+                                                           .Register(*json::MakeProperty("specularColorFactor", gltf2::ReadDaliVector<Vector3>, &gltf2::MaterialSpecular::mSpecularColorFactor))
+                                                           .Register(*json::MakeProperty("specularColorTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::MaterialSpecular::mSpecularColorTexture)));
+  return MATERIAL_SPECULAR_READER;
+}
+
+const json::Reader<gltf2::MaterialIor>& GetMaterialIorReader()
+{
+  static const auto MATERIAL_IOR_READER = std::move(json::Reader<gltf2::MaterialIor>()
+                                                      .Register(*json::MakeProperty("ior", json::Read::Number<float>, &gltf2::MaterialIor::mIor)));
+  return MATERIAL_IOR_READER;
+}
+
+const json::Reader<gltf2::MaterialExtensions>& GetMaterialExtensionsReader()
+{
+  static const auto MATERIAL_EXTENSION_READER = std::move(json::Reader<gltf2::MaterialExtensions>()
+                                                            .Register(*json::MakeProperty("KHR_materials_ior", json::ObjectReader<gltf2::MaterialIor>::Read, &gltf2::MaterialExtensions::mMaterialIor))
+                                                            .Register(*json::MakeProperty("KHR_materials_specular", json::ObjectReader<gltf2::MaterialSpecular>::Read, &gltf2::MaterialExtensions::mMaterialSpecular)));
+  return MATERIAL_EXTENSION_READER;
+}
+
+const json::Reader<gltf2::Material>& GetMaterialReader()
+{
+  static const auto MATERIAL_READER = std::move(json::Reader<gltf2::Material>()
+                                                  .Register(*new json::Property<gltf2::Material, std::string_view>("name", json::Read::StringView, &gltf2::Material::mName))
+                                                  .Register(*json::MakeProperty("pbrMetallicRoughness", json::ObjectReader<gltf2::Material::Pbr>::Read, &gltf2::Material::mPbrMetallicRoughness))
+                                                  .Register(*json::MakeProperty("normalTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::Material::mNormalTexture))
+                                                  .Register(*json::MakeProperty("occlusionTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::Material::mOcclusionTexture))
+                                                  .Register(*json::MakeProperty("emissiveTexture", json::ObjectReader<gltf2::TextureInfo>::Read, &gltf2::Material::mEmissiveTexture))
+                                                  .Register(*json::MakeProperty("emissiveFactor", gltf2::ReadDaliVector<Vector3>, &gltf2::Material::mEmissiveFactor))
+                                                  .Register(*json::MakeProperty("alphaMode", gltf2::ReadStringEnum<gltf2::AlphaMode>, &gltf2::Material::mAlphaMode))
+                                                  .Register(*json::MakeProperty("alphaCutoff", json::Read::Number<float>, &gltf2::Material::mAlphaCutoff))
+                                                  .Register(*json::MakeProperty("doubleSided", json::Read::Boolean, &gltf2::Material::mDoubleSided))
+                                                  .Register(*json::MakeProperty("extensions", json::ObjectReader<gltf2::MaterialExtensions>::Read, &gltf2::Material::mMaterialExtensions)));
+  return MATERIAL_READER;
+}
 
 std::map<gltf2::Attribute::Type, gltf2::Ref<gltf2::Accessor>> ReadMeshPrimitiveAttributes(const json_value_s& j)
 {
@@ -209,130 +270,182 @@ std::vector<std::map<gltf2::Attribute::Type, gltf2::Ref<gltf2::Accessor>>> ReadM
   return result;
 }
 
-const auto MESH_PRIMITIVE_READER = std::move(json::Reader<gltf2::Mesh::Primitive>()
-                                               .Register(*json::MakeProperty("attributes", ReadMeshPrimitiveAttributes, &gltf2::Mesh::Primitive::mAttributes))
-                                               .Register(*json::MakeProperty("indices", gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>, &gltf2::Mesh::Primitive::mIndices))
-                                               .Register(*json::MakeProperty("material", gltf2::RefReader<gltf2::Document>::Read<gltf2::Material, &gltf2::Document::mMaterials>, &gltf2::Mesh::Primitive::mMaterial))
-                                               .Register(*json::MakeProperty("mode", json::Read::Enum<gltf2::Mesh::Primitive::Mode>, &gltf2::Mesh::Primitive::mMode))
-                                               .Register(*json::MakeProperty("targets", ReadMeshPrimitiveTargets, &gltf2::Mesh::Primitive::mTargets)));
-
-const auto MESH_READER = std::move(json::Reader<gltf2::Mesh>()
-                                     .Register(*new json::Property<gltf2::Mesh, std::string_view>("name", json::Read::StringView, &gltf2::Mesh::mName))
-                                     .Register(*json::MakeProperty("primitives",
-                                                                   json::Read::Array<gltf2::Mesh::Primitive, json::ObjectReader<gltf2::Mesh::Primitive>::Read>,
-                                                                   &gltf2::Mesh::mPrimitives))
-                                     .Register(*json::MakeProperty("weights", json::Read::Array<float, json::Read::Number>, &gltf2::Mesh::mWeights)));
-
-const auto SKIN_READER = std::move(json::Reader<gltf2::Skin>()
-                                     .Register(*new json::Property<gltf2::Skin, std::string_view>("name", json::Read::StringView, &gltf2::Skin::mName))
-                                     .Register(*json::MakeProperty("inverseBindMatrices",
-                                                                   gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>,
-                                                                   &gltf2::Skin::mInverseBindMatrices))
-                                     .Register(*json::MakeProperty("skeleton",
-                                                                   gltf2::RefReader<gltf2::Document>::Read<gltf2::Node, &gltf2::Document::mNodes>,
-                                                                   &gltf2::Skin::mSkeleton))
-                                     .Register(*json::MakeProperty("joints",
-                                                                   json::Read::Array<gltf2::Ref<gltf2::Node>, gltf2::RefReader<gltf2::Document>::Read<gltf2::Node, &gltf2::Document::mNodes>>,
-                                                                   &gltf2::Skin::mJoints)));
-
-const auto CAMERA_PERSPECTIVE_READER = std::move(json::Reader<gltf2::Camera::Perspective>()
-                                                   .Register(*json::MakeProperty("aspectRatio", json::Read::Number<float>, &gltf2::Camera::Perspective::mAspectRatio))
-                                                   .Register(*json::MakeProperty("yfov", json::Read::Number<float>, &gltf2::Camera::Perspective::mYFov))
-                                                   .Register(*json::MakeProperty("zfar", json::Read::Number<float>, &gltf2::Camera::Perspective::mZFar))
-                                                   .Register(*json::MakeProperty("znear", json::Read::Number<float>, &gltf2::Camera::Perspective::mZNear))); // TODO: infinite perspective projection, where znear is omitted
-
-const auto CAMERA_ORTHOGRAPHIC_READER = std::move(json::Reader<gltf2::Camera::Orthographic>()
-                                                    .Register(*json::MakeProperty("xmag", json::Read::Number<float>, &gltf2::Camera::Orthographic::mXMag))
-                                                    .Register(*json::MakeProperty("ymag", json::Read::Number<float>, &gltf2::Camera::Orthographic::mYMag))
-                                                    .Register(*json::MakeProperty("zfar", json::Read::Number<float>, &gltf2::Camera::Orthographic::mZFar))
-                                                    .Register(*json::MakeProperty("znear", json::Read::Number<float>, &gltf2::Camera::Orthographic::mZNear)));
-
-const auto CAMERA_READER = std::move(json::Reader<gltf2::Camera>()
-                                       .Register(*new json::Property<gltf2::Camera, std::string_view>("name", json::Read::StringView, &gltf2::Camera::mName))
-                                       .Register(*json::MakeProperty("type", json::Read::StringView, &gltf2::Camera::mType))
-                                       .Register(*json::MakeProperty("perspective", json::ObjectReader<gltf2::Camera::Perspective>::Read, &gltf2::Camera::mPerspective))
-                                       .Register(*json::MakeProperty("orthographic", json::ObjectReader<gltf2::Camera::Orthographic>::Read, &gltf2::Camera::mOrthographic)));
-
-const auto NODE_READER = std::move(json::Reader<gltf2::Node>()
-                                     .Register(*new json::Property<gltf2::Node, std::string_view>("name", json::Read::StringView, &gltf2::Node::mName))
-                                     .Register(*json::MakeProperty("translation", gltf2::ReadDaliVector<Vector3>, &gltf2::Node::mTranslation))
-                                     .Register(*json::MakeProperty("rotation", gltf2::ReadQuaternion, &gltf2::Node::mRotation))
-                                     .Register(*json::MakeProperty("scale", gltf2::ReadDaliVector<Vector3>, &gltf2::Node::mScale))
-                                     .Register(*new json::Property<gltf2::Node, Matrix>("matrix", gltf2::ReadDaliVector<Matrix>, &gltf2::Node::SetMatrix))
-                                     .Register(*json::MakeProperty("camera", gltf2::RefReader<gltf2::Document>::Read<gltf2::Camera, &gltf2::Document::mCameras>, &gltf2::Node::mCamera))
-                                     .Register(*json::MakeProperty("children", json::Read::Array<gltf2::Ref<gltf2::Node>, gltf2::RefReader<gltf2::Document>::Read<gltf2::Node, &gltf2::Document::mNodes>>, &gltf2::Node::mChildren))
-                                     .Register(*json::MakeProperty("mesh", gltf2::RefReader<gltf2::Document>::Read<gltf2::Mesh, &gltf2::Document::mMeshes>, &gltf2::Node::mMesh))
-                                     .Register(*json::MakeProperty("skin", gltf2::RefReader<gltf2::Document>::Read<gltf2::Skin, &gltf2::Document::mSkins>, &gltf2::Node::mSkin)));
-
-const auto ANIMATION_SAMPLER_READER = std::move(json::Reader<gltf2::Animation::Sampler>()
-                                                  .Register(*json::MakeProperty("input", gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>, &gltf2::Animation::Sampler::mInput))
-                                                  .Register(*json::MakeProperty("output", gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>, &gltf2::Animation::Sampler::mOutput))
-                                                  .Register(*json::MakeProperty("interpolation", gltf2::ReadStringEnum<gltf2::Animation::Sampler::Interpolation>, &gltf2::Animation::Sampler::mInterpolation)));
-
-const auto ANIMATION_TARGET_READER = std::move(json::Reader<gltf2::Animation::Channel::Target>()
-                                                 .Register(*json::MakeProperty("node", gltf2::RefReader<gltf2::Document>::Read<gltf2::Node, &gltf2::Document::mNodes>, &gltf2::Animation::Channel::Target::mNode))
-                                                 .Register(*json::MakeProperty("path", gltf2::ReadStringEnum<gltf2::Animation::Channel::Target>, &gltf2::Animation::Channel::Target::mPath)));
-
-const auto ANIMATION_CHANNEL_READER = std::move(json::Reader<gltf2::Animation::Channel>()
-                                                  .Register(*json::MakeProperty("target", json::ObjectReader<gltf2::Animation::Channel::Target>::Read, &gltf2::Animation::Channel::mTarget))
-                                                  .Register(*json::MakeProperty("sampler", gltf2::RefReader<gltf2::Animation>::Read<gltf2::Animation::Sampler, &gltf2::Animation::mSamplers>, &gltf2::Animation::Channel::mSampler)));
-
-const auto ANIMATION_READER = std::move(json::Reader<gltf2::Animation>()
-                                          .Register(*new json::Property<gltf2::Animation, std::string_view>("name", json::Read::StringView, &gltf2::Animation::mName))
-                                          .Register(*json::MakeProperty("samplers",
-                                                                        json::Read::Array<gltf2::Animation::Sampler, json::ObjectReader<gltf2::Animation::Sampler>::Read>,
-                                                                        &gltf2::Animation::mSamplers))
-                                          .Register(*json::MakeProperty("channels",
-                                                                        json::Read::Array<gltf2::Animation::Channel, json::ObjectReader<gltf2::Animation::Channel>::Read>,
-                                                                        &gltf2::Animation::mChannels)));
-
-const auto SCENE_READER = std::move(json::Reader<gltf2::Scene>()
-                                      .Register(*new json::Property<gltf2::Scene, std::string_view>("name", json::Read::StringView, &gltf2::Scene::mName))
-                                      .Register(*json::MakeProperty("nodes",
-                                                                    json::Read::Array<gltf2::Ref<gltf2::Node>, gltf2::RefReader<gltf2::Document>::Read<gltf2::Node, &gltf2::Document::mNodes>>,
-                                                                    &gltf2::Scene::mNodes)));
-
-const auto DOCUMENT_READER = std::move(json::Reader<gltf2::Document>()
-                                         .Register(*json::MakeProperty("buffers",
-                                                                       json::Read::Array<gltf2::Buffer, json::ObjectReader<gltf2::Buffer>::Read>,
-                                                                       &gltf2::Document::mBuffers))
-                                         .Register(*json::MakeProperty("bufferViews",
-                                                                       json::Read::Array<gltf2::BufferView, json::ObjectReader<gltf2::BufferView>::Read>,
-                                                                       &gltf2::Document::mBufferViews))
-                                         .Register(*json::MakeProperty("accessors",
-                                                                       json::Read::Array<gltf2::Accessor, json::ObjectReader<gltf2::Accessor>::Read>,
-                                                                       &gltf2::Document::mAccessors))
-                                         .Register(*json::MakeProperty("images",
-                                                                       json::Read::Array<gltf2::Image, json::ObjectReader<gltf2::Image>::Read>,
-                                                                       &gltf2::Document::mImages))
-                                         .Register(*json::MakeProperty("samplers",
-                                                                       json::Read::Array<gltf2::Sampler, json::ObjectReader<gltf2::Sampler>::Read>,
-                                                                       &gltf2::Document::mSamplers))
-                                         .Register(*json::MakeProperty("textures",
-                                                                       json::Read::Array<gltf2::Texture, json::ObjectReader<gltf2::Texture>::Read>,
-                                                                       &gltf2::Document::mTextures))
-                                         .Register(*json::MakeProperty("materials",
-                                                                       json::Read::Array<gltf2::Material, json::ObjectReader<gltf2::Material>::Read>,
-                                                                       &gltf2::Document::mMaterials))
-                                         .Register(*json::MakeProperty("meshes",
-                                                                       json::Read::Array<gltf2::Mesh, json::ObjectReader<gltf2::Mesh>::Read>,
-                                                                       &gltf2::Document::mMeshes))
-                                         .Register(*json::MakeProperty("skins",
-                                                                       json::Read::Array<gltf2::Skin, json::ObjectReader<gltf2::Skin>::Read>,
-                                                                       &gltf2::Document::mSkins))
-                                         .Register(*json::MakeProperty("cameras",
-                                                                       json::Read::Array<gltf2::Camera, json::ObjectReader<gltf2::Camera>::Read>,
-                                                                       &gltf2::Document::mCameras))
-                                         .Register(*json::MakeProperty("nodes",
-                                                                       json::Read::Array<gltf2::Node, json::ObjectReader<gltf2::Node>::Read>,
-                                                                       &gltf2::Document::mNodes))
-                                         .Register(*json::MakeProperty("animations",
-                                                                       ReadAnimationArray,
-                                                                       &gltf2::Document::mAnimations))
-                                         .Register(*json::MakeProperty("scenes",
-                                                                       json::Read::Array<gltf2::Scene, json::ObjectReader<gltf2::Scene>::Read>,
-                                                                       &gltf2::Document::mScenes))
-                                         .Register(*json::MakeProperty("scene", gltf2::RefReader<gltf2::Document>::Read<gltf2::Scene, &gltf2::Document::mScenes>, &gltf2::Document::mScene)));
+const json::Reader<gltf2::Mesh::Primitive>& GetMeshPrimitiveReader()
+{
+  static const auto MESH_PRIMITIVE_READER = std::move(json::Reader<gltf2::Mesh::Primitive>()
+                                                        .Register(*json::MakeProperty("attributes", ReadMeshPrimitiveAttributes, &gltf2::Mesh::Primitive::mAttributes))
+                                                        .Register(*json::MakeProperty("indices", gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>, &gltf2::Mesh::Primitive::mIndices))
+                                                        .Register(*json::MakeProperty("material", gltf2::RefReader<gltf2::Document>::Read<gltf2::Material, &gltf2::Document::mMaterials>, &gltf2::Mesh::Primitive::mMaterial))
+                                                        .Register(*json::MakeProperty("mode", json::Read::Enum<gltf2::Mesh::Primitive::Mode>, &gltf2::Mesh::Primitive::mMode))
+                                                        .Register(*json::MakeProperty("targets", ReadMeshPrimitiveTargets, &gltf2::Mesh::Primitive::mTargets)));
+  return MESH_PRIMITIVE_READER;
+}
+
+const json::Reader<gltf2::Mesh>& GetMeshReader()
+{
+  static const auto MESH_READER = std::move(json::Reader<gltf2::Mesh>()
+                                              .Register(*new json::Property<gltf2::Mesh, std::string_view>("name", json::Read::StringView, &gltf2::Mesh::mName))
+                                              .Register(*json::MakeProperty("primitives",
+                                                                            json::Read::Array<gltf2::Mesh::Primitive, json::ObjectReader<gltf2::Mesh::Primitive>::Read>,
+                                                                            &gltf2::Mesh::mPrimitives))
+                                              .Register(*json::MakeProperty("weights", json::Read::Array<float, json::Read::Number>, &gltf2::Mesh::mWeights)));
+  return MESH_READER;
+}
+
+const json::Reader<gltf2::Skin>& GetSkinReader()
+{
+  static const auto SKIN_READER = std::move(json::Reader<gltf2::Skin>()
+                                              .Register(*new json::Property<gltf2::Skin, std::string_view>("name", json::Read::StringView, &gltf2::Skin::mName))
+                                              .Register(*json::MakeProperty("inverseBindMatrices",
+                                                                            gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>,
+                                                                            &gltf2::Skin::mInverseBindMatrices))
+                                              .Register(*json::MakeProperty("skeleton",
+                                                                            gltf2::RefReader<gltf2::Document>::Read<gltf2::Node, &gltf2::Document::mNodes>,
+                                                                            &gltf2::Skin::mSkeleton))
+                                              .Register(*json::MakeProperty("joints",
+                                                                            json::Read::Array<gltf2::Ref<gltf2::Node>, gltf2::RefReader<gltf2::Document>::Read<gltf2::Node, &gltf2::Document::mNodes>>,
+                                                                            &gltf2::Skin::mJoints)));
+  return SKIN_READER;
+}
+
+const json::Reader<gltf2::Camera::Perspective>& GetCameraPerspectiveReader()
+{
+  static const auto CAMERA_PERSPECTIVE_READER = std::move(json::Reader<gltf2::Camera::Perspective>()
+                                                            .Register(*json::MakeProperty("aspectRatio", json::Read::Number<float>, &gltf2::Camera::Perspective::mAspectRatio))
+                                                            .Register(*json::MakeProperty("yfov", json::Read::Number<float>, &gltf2::Camera::Perspective::mYFov))
+                                                            .Register(*json::MakeProperty("zfar", json::Read::Number<float>, &gltf2::Camera::Perspective::mZFar))
+                                                            .Register(*json::MakeProperty("znear", json::Read::Number<float>, &gltf2::Camera::Perspective::mZNear))); // TODO: infinite perspective projection, where znear is omitted
+  return CAMERA_PERSPECTIVE_READER;
+}
+
+const json::Reader<gltf2::Camera::Orthographic>& GetCameraOrthographicReader()
+{
+  static const auto CAMERA_ORTHOGRAPHIC_READER = std::move(json::Reader<gltf2::Camera::Orthographic>()
+                                                             .Register(*json::MakeProperty("xmag", json::Read::Number<float>, &gltf2::Camera::Orthographic::mXMag))
+                                                             .Register(*json::MakeProperty("ymag", json::Read::Number<float>, &gltf2::Camera::Orthographic::mYMag))
+                                                             .Register(*json::MakeProperty("zfar", json::Read::Number<float>, &gltf2::Camera::Orthographic::mZFar))
+                                                             .Register(*json::MakeProperty("znear", json::Read::Number<float>, &gltf2::Camera::Orthographic::mZNear)));
+  return CAMERA_ORTHOGRAPHIC_READER;
+}
+
+const json::Reader<gltf2::Camera>& GetCameraReader()
+{
+  static const auto CAMERA_READER = std::move(json::Reader<gltf2::Camera>()
+                                                .Register(*new json::Property<gltf2::Camera, std::string_view>("name", json::Read::StringView, &gltf2::Camera::mName))
+                                                .Register(*json::MakeProperty("type", json::Read::StringView, &gltf2::Camera::mType))
+                                                .Register(*json::MakeProperty("perspective", json::ObjectReader<gltf2::Camera::Perspective>::Read, &gltf2::Camera::mPerspective))
+                                                .Register(*json::MakeProperty("orthographic", json::ObjectReader<gltf2::Camera::Orthographic>::Read, &gltf2::Camera::mOrthographic)));
+  return CAMERA_READER;
+}
+
+const json::Reader<gltf2::Node>& GetNodeReader()
+{
+  static const auto NODE_READER = std::move(json::Reader<gltf2::Node>()
+                                              .Register(*new json::Property<gltf2::Node, std::string_view>("name", json::Read::StringView, &gltf2::Node::mName))
+                                              .Register(*json::MakeProperty("translation", gltf2::ReadDaliVector<Vector3>, &gltf2::Node::mTranslation))
+                                              .Register(*json::MakeProperty("rotation", gltf2::ReadQuaternion, &gltf2::Node::mRotation))
+                                              .Register(*json::MakeProperty("scale", gltf2::ReadDaliVector<Vector3>, &gltf2::Node::mScale))
+                                              .Register(*new json::Property<gltf2::Node, Matrix>("matrix", gltf2::ReadDaliVector<Matrix>, &gltf2::Node::SetMatrix))
+                                              .Register(*json::MakeProperty("camera", gltf2::RefReader<gltf2::Document>::Read<gltf2::Camera, &gltf2::Document::mCameras>, &gltf2::Node::mCamera))
+                                              .Register(*json::MakeProperty("children", json::Read::Array<gltf2::Ref<gltf2::Node>, gltf2::RefReader<gltf2::Document>::Read<gltf2::Node, &gltf2::Document::mNodes>>, &gltf2::Node::mChildren))
+                                              .Register(*json::MakeProperty("mesh", gltf2::RefReader<gltf2::Document>::Read<gltf2::Mesh, &gltf2::Document::mMeshes>, &gltf2::Node::mMesh))
+                                              .Register(*json::MakeProperty("skin", gltf2::RefReader<gltf2::Document>::Read<gltf2::Skin, &gltf2::Document::mSkins>, &gltf2::Node::mSkin)));
+  return NODE_READER;
+}
+
+const json::Reader<gltf2::Animation::Sampler>& GetAnimationSamplerReader()
+{
+  static const auto ANIMATION_SAMPLER_READER = std::move(json::Reader<gltf2::Animation::Sampler>()
+                                                           .Register(*json::MakeProperty("input", gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>, &gltf2::Animation::Sampler::mInput))
+                                                           .Register(*json::MakeProperty("output", gltf2::RefReader<gltf2::Document>::Read<gltf2::Accessor, &gltf2::Document::mAccessors>, &gltf2::Animation::Sampler::mOutput))
+                                                           .Register(*json::MakeProperty("interpolation", gltf2::ReadStringEnum<gltf2::Animation::Sampler::Interpolation>, &gltf2::Animation::Sampler::mInterpolation)));
+  return ANIMATION_SAMPLER_READER;
+}
+
+const json::Reader<gltf2::Animation::Channel::Target>& GetAnimationChannelTargetReader()
+{
+  static const auto ANIMATION_TARGET_READER = std::move(json::Reader<gltf2::Animation::Channel::Target>()
+                                                          .Register(*json::MakeProperty("node", gltf2::RefReader<gltf2::Document>::Read<gltf2::Node, &gltf2::Document::mNodes>, &gltf2::Animation::Channel::Target::mNode))
+                                                          .Register(*json::MakeProperty("path", gltf2::ReadStringEnum<gltf2::Animation::Channel::Target>, &gltf2::Animation::Channel::Target::mPath)));
+  return ANIMATION_TARGET_READER;
+}
+
+const json::Reader<gltf2::Animation::Channel>& GetAnimationChannelReader()
+{
+  static const auto ANIMATION_CHANNEL_READER = std::move(json::Reader<gltf2::Animation::Channel>()
+                                                           .Register(*json::MakeProperty("target", json::ObjectReader<gltf2::Animation::Channel::Target>::Read, &gltf2::Animation::Channel::mTarget))
+                                                           .Register(*json::MakeProperty("sampler", gltf2::RefReader<gltf2::Animation>::Read<gltf2::Animation::Sampler, &gltf2::Animation::mSamplers>, &gltf2::Animation::Channel::mSampler)));
+  return ANIMATION_CHANNEL_READER;
+}
+
+const json::Reader<gltf2::Animation>& GetAnimationReader()
+{
+  static const auto ANIMATION_READER = std::move(json::Reader<gltf2::Animation>()
+                                                   .Register(*new json::Property<gltf2::Animation, std::string_view>("name", json::Read::StringView, &gltf2::Animation::mName))
+                                                   .Register(*json::MakeProperty("samplers",
+                                                                                 json::Read::Array<gltf2::Animation::Sampler, json::ObjectReader<gltf2::Animation::Sampler>::Read>,
+                                                                                 &gltf2::Animation::mSamplers))
+                                                   .Register(*json::MakeProperty("channels",
+                                                                                 json::Read::Array<gltf2::Animation::Channel, json::ObjectReader<gltf2::Animation::Channel>::Read>,
+                                                                                 &gltf2::Animation::mChannels)));
+  return ANIMATION_READER;
+}
+
+const json::Reader<gltf2::Scene>& GetSceneReader()
+{
+  static const auto SCENE_READER = std::move(json::Reader<gltf2::Scene>()
+                                               .Register(*new json::Property<gltf2::Scene, std::string_view>("name", json::Read::StringView, &gltf2::Scene::mName))
+                                               .Register(*json::MakeProperty("nodes",
+                                                                             json::Read::Array<gltf2::Ref<gltf2::Node>, gltf2::RefReader<gltf2::Document>::Read<gltf2::Node, &gltf2::Document::mNodes>>,
+                                                                             &gltf2::Scene::mNodes)));
+  return SCENE_READER;
+}
+
+const json::Reader<gltf2::Document>& GetDocumentReader()
+{
+  static const auto DOCUMENT_READER = std::move(json::Reader<gltf2::Document>()
+                                                  .Register(*json::MakeProperty("buffers",
+                                                                                json::Read::Array<gltf2::Buffer, json::ObjectReader<gltf2::Buffer>::Read>,
+                                                                                &gltf2::Document::mBuffers))
+                                                  .Register(*json::MakeProperty("bufferViews",
+                                                                                json::Read::Array<gltf2::BufferView, json::ObjectReader<gltf2::BufferView>::Read>,
+                                                                                &gltf2::Document::mBufferViews))
+                                                  .Register(*json::MakeProperty("accessors",
+                                                                                json::Read::Array<gltf2::Accessor, json::ObjectReader<gltf2::Accessor>::Read>,
+                                                                                &gltf2::Document::mAccessors))
+                                                  .Register(*json::MakeProperty("images",
+                                                                                json::Read::Array<gltf2::Image, json::ObjectReader<gltf2::Image>::Read>,
+                                                                                &gltf2::Document::mImages))
+                                                  .Register(*json::MakeProperty("samplers",
+                                                                                json::Read::Array<gltf2::Sampler, json::ObjectReader<gltf2::Sampler>::Read>,
+                                                                                &gltf2::Document::mSamplers))
+                                                  .Register(*json::MakeProperty("textures",
+                                                                                json::Read::Array<gltf2::Texture, json::ObjectReader<gltf2::Texture>::Read>,
+                                                                                &gltf2::Document::mTextures))
+                                                  .Register(*json::MakeProperty("materials",
+                                                                                json::Read::Array<gltf2::Material, json::ObjectReader<gltf2::Material>::Read>,
+                                                                                &gltf2::Document::mMaterials))
+                                                  .Register(*json::MakeProperty("meshes",
+                                                                                json::Read::Array<gltf2::Mesh, json::ObjectReader<gltf2::Mesh>::Read>,
+                                                                                &gltf2::Document::mMeshes))
+                                                  .Register(*json::MakeProperty("skins",
+                                                                                json::Read::Array<gltf2::Skin, json::ObjectReader<gltf2::Skin>::Read>,
+                                                                                &gltf2::Document::mSkins))
+                                                  .Register(*json::MakeProperty("cameras",
+                                                                                json::Read::Array<gltf2::Camera, json::ObjectReader<gltf2::Camera>::Read>,
+                                                                                &gltf2::Document::mCameras))
+                                                  .Register(*json::MakeProperty("nodes",
+                                                                                json::Read::Array<gltf2::Node, json::ObjectReader<gltf2::Node>::Read>,
+                                                                                &gltf2::Document::mNodes))
+                                                  .Register(*json::MakeProperty("animations",
+                                                                                ReadAnimationArray,
+                                                                                &gltf2::Document::mAnimations))
+                                                  .Register(*json::MakeProperty("scenes",
+                                                                                json::Read::Array<gltf2::Scene, json::ObjectReader<gltf2::Scene>::Read>,
+                                                                                &gltf2::Document::mScenes))
+                                                  .Register(*json::MakeProperty("scene", gltf2::RefReader<gltf2::Document>::Read<gltf2::Scene, &gltf2::Document::mScenes>, &gltf2::Document::mScene)));
+  return DOCUMENT_READER;
+}
 
 void ConvertBuffer(const gltf2::Buffer& buffer, decltype(ResourceBundle::mBuffers)& outBuffers, const std::string& resourcePath)
 {
@@ -420,9 +533,16 @@ TextureDefinition ConvertTextureInfo(const gltf2::TextureInfo& textureInfo, Conv
   }
 }
 
+void AddTextureStage(uint32_t semantic, MaterialDefinition& materialDefinition, gltf2::TextureInfo textureInfo, const Dali::Scene3D::Loader::ImageMetadata &metaData, ConversionContext& context)
+{
+  materialDefinition.mTextureStages.push_back({semantic, ConvertTextureInfo(textureInfo, context, metaData)});
+  materialDefinition.mFlags |= semantic;
+}
+
 void ConvertMaterial(const gltf2::Material& material, const std::unordered_map<std::string, ImageMetadata>& imageMetaData, decltype(ResourceBundle::mMaterials)& outMaterials, ConversionContext& context)
 {
-  auto getTextureMetaData = [](const std::unordered_map<std::string, ImageMetadata>& metaData, const gltf2::TextureInfo& info) {
+  auto getTextureMetaData = [](const std::unordered_map<std::string, ImageMetadata>& metaData, const gltf2::TextureInfo& info)
+  {
     if(!info.mTexture->mSource->mUri.empty())
     {
       if(auto search = metaData.find(info.mTexture->mSource->mUri.data()); search != metaData.end())
@@ -438,12 +558,14 @@ void ConvertMaterial(const gltf2::Material& material, const std::unordered_map<s
   auto& pbr = material.mPbrMetallicRoughness;
   if(material.mAlphaMode == gltf2::AlphaMode::BLEND)
   {
-    materialDefinition.mIsOpaque = false;
+    materialDefinition.mAlphaModeType = Scene3D::Material::AlphaModeType::BLEND;
+    materialDefinition.mIsOpaque      = false;
     materialDefinition.mFlags |= MaterialDefinition::TRANSPARENCY;
   }
   else if(material.mAlphaMode == gltf2::AlphaMode::MASK)
   {
-    materialDefinition.mIsMask = true;
+    materialDefinition.mAlphaModeType = Scene3D::Material::AlphaModeType::MASK;
+    materialDefinition.mIsMask        = true;
     materialDefinition.SetAlphaCutoff(std::min(1.f, std::max(0.f, material.mAlphaCutoff)));
   }
 
@@ -452,10 +574,7 @@ void ConvertMaterial(const gltf2::Material& material, const std::unordered_map<s
   materialDefinition.mTextureStages.reserve(!!pbr.mBaseColorTexture + !!pbr.mMetallicRoughnessTexture + !!material.mNormalTexture + !!material.mOcclusionTexture + !!material.mEmissiveTexture);
   if(pbr.mBaseColorTexture)
   {
-    const auto semantic = MaterialDefinition::ALBEDO;
-    materialDefinition.mTextureStages.push_back({semantic, ConvertTextureInfo(pbr.mBaseColorTexture, context, getTextureMetaData(imageMetaData, pbr.mBaseColorTexture))});
-    // TODO: and there had better be one
-    materialDefinition.mFlags |= semantic;
+    AddTextureStage(MaterialDefinition::ALBEDO, materialDefinition, pbr.mBaseColorTexture, getTextureMetaData(imageMetaData, pbr.mBaseColorTexture), context);
   }
   else
   {
@@ -467,11 +586,11 @@ void ConvertMaterial(const gltf2::Material& material, const std::unordered_map<s
 
   if(pbr.mMetallicRoughnessTexture)
   {
-    const auto semantic = MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
-                          MaterialDefinition::GLTF_CHANNELS;
-    materialDefinition.mTextureStages.push_back({semantic, ConvertTextureInfo(pbr.mMetallicRoughnessTexture, context, getTextureMetaData(imageMetaData, pbr.mMetallicRoughnessTexture))});
-    // TODO: and there had better be one
-    materialDefinition.mFlags |= semantic;
+    AddTextureStage(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | MaterialDefinition::GLTF_CHANNELS,
+                    materialDefinition,
+                    pbr.mMetallicRoughnessTexture,
+                    getTextureMetaData(imageMetaData, pbr.mMetallicRoughnessTexture),
+                    context);
   }
   else
   {
@@ -481,10 +600,7 @@ void ConvertMaterial(const gltf2::Material& material, const std::unordered_map<s
   materialDefinition.mNormalScale = material.mNormalTexture.mScale;
   if(material.mNormalTexture)
   {
-    const auto semantic = MaterialDefinition::NORMAL;
-    materialDefinition.mTextureStages.push_back({semantic, ConvertTextureInfo(material.mNormalTexture, context, getTextureMetaData(imageMetaData, material.mNormalTexture))});
-    // TODO: and there had better be one
-    materialDefinition.mFlags |= semantic;
+    AddTextureStage(MaterialDefinition::NORMAL, materialDefinition, material.mNormalTexture, getTextureMetaData(imageMetaData, material.mNormalTexture), context);
   }
   else
   {
@@ -493,42 +609,33 @@ void ConvertMaterial(const gltf2::Material& material, const std::unordered_map<s
 
   if(material.mOcclusionTexture)
   {
-    const auto semantic = MaterialDefinition::OCCLUSION;
-    materialDefinition.mTextureStages.push_back({semantic, ConvertTextureInfo(material.mOcclusionTexture, context, getTextureMetaData(imageMetaData, material.mOcclusionTexture))});
-    // TODO: and there had better be one
-    materialDefinition.mFlags |= semantic;
+    AddTextureStage(MaterialDefinition::OCCLUSION, materialDefinition, material.mOcclusionTexture, getTextureMetaData(imageMetaData, material.mOcclusionTexture), context);
     materialDefinition.mOcclusionStrength = material.mOcclusionTexture.mStrength;
   }
 
   materialDefinition.mEmissiveFactor = material.mEmissiveFactor;
   if(material.mEmissiveTexture)
   {
-    const auto semantic = MaterialDefinition::EMISSIVE;
-    materialDefinition.mTextureStages.push_back({semantic, ConvertTextureInfo(material.mEmissiveTexture, context, getTextureMetaData(imageMetaData, material.mEmissiveTexture))});
-    // TODO: and there had better be one
-    materialDefinition.mFlags |= semantic;
+    AddTextureStage(MaterialDefinition::EMISSIVE, materialDefinition, material.mEmissiveTexture, getTextureMetaData(imageMetaData, material.mEmissiveTexture), context);
+    materialDefinition.mEmissiveFactor = material.mEmissiveFactor;
   }
 
   if(!Dali::Equals(material.mMaterialExtensions.mMaterialIor.mIor, gltf2::UNDEFINED_FLOAT_VALUE))
   {
-    float ior                              = material.mMaterialExtensions.mMaterialIor.mIor;
-    materialDefinition.mDielectricSpecular = powf((ior - 1.0f) / (ior + 1.0f), 2.0f);
+    materialDefinition.mIor                = material.mMaterialExtensions.mMaterialIor.mIor;
+    materialDefinition.mDielectricSpecular = powf((materialDefinition.mIor - 1.0f) / (materialDefinition.mIor + 1.0f), 2.0f);
   }
   materialDefinition.mSpecularFactor      = material.mMaterialExtensions.mMaterialSpecular.mSpecularFactor;
   materialDefinition.mSpecularColorFactor = material.mMaterialExtensions.mMaterialSpecular.mSpecularColorFactor;
 
   if(material.mMaterialExtensions.mMaterialSpecular.mSpecularTexture)
   {
-    const auto semantic = MaterialDefinition::SPECULAR;
-    materialDefinition.mTextureStages.push_back({semantic, ConvertTextureInfo(material.mMaterialExtensions.mMaterialSpecular.mSpecularTexture, context, getTextureMetaData(imageMetaData, material.mMaterialExtensions.mMaterialSpecular.mSpecularTexture))});
-    materialDefinition.mFlags |= semantic;
+    AddTextureStage(MaterialDefinition::SPECULAR, materialDefinition, material.mMaterialExtensions.mMaterialSpecular.mSpecularTexture, getTextureMetaData(imageMetaData, material.mMaterialExtensions.mMaterialSpecular.mSpecularTexture), context);
   }
 
   if(material.mMaterialExtensions.mMaterialSpecular.mSpecularColorTexture)
   {
-    const auto semantic = MaterialDefinition::SPECULAR_COLOR;
-    materialDefinition.mTextureStages.push_back({semantic, ConvertTextureInfo(material.mMaterialExtensions.mMaterialSpecular.mSpecularColorTexture, context, getTextureMetaData(imageMetaData, material.mMaterialExtensions.mMaterialSpecular.mSpecularColorTexture))});
-    materialDefinition.mFlags |= semantic;
+    AddTextureStage(MaterialDefinition::SPECULAR_COLOR, materialDefinition, material.mMaterialExtensions.mMaterialSpecular.mSpecularColorTexture, getTextureMetaData(imageMetaData, material.mMaterialExtensions.mMaterialSpecular.mSpecularColorTexture), context);
   }
 
   materialDefinition.mDoubleSided = material.mDoubleSided;
@@ -624,7 +731,15 @@ void ConvertMeshes(const gltf2::Document& document, ConversionContext& context)
       auto& attribs                 = primitive.mAttributes;
       meshDefinition.mPrimitiveType = GLTF2_TO_DALI_PRIMITIVES[primitive.mMode];
 
-      auto& accPositions        = *attribs.find(gltf2::Attribute::POSITION)->second;
+      auto positionIter = attribs.find(gltf2::Attribute::POSITION);
+
+      if(positionIter == attribs.end())
+      {
+        DALI_LOG_ERROR("Primitive mesh dosn't have POSITION atrributes!");
+        continue;
+      }
+
+      auto& accPositions        = *positionIter->second;
       meshDefinition.mPositions = ConvertMeshPrimitiveAccessor(accPositions);
       // glTF2 support vector4 tangent for mesh.
       // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview
@@ -780,7 +895,8 @@ void ConvertNode(gltf2::Node const& node, const Index gltfIndex, Index parentInd
   auto& resources = output.mResources;
 
   const auto index    = scene.GetNodeCount();
-  auto       weakNode = scene.AddNode([&]() {
+  auto       weakNode = scene.AddNode([&]()
+                                {
     std::unique_ptr<NodeDefinition> nodeDefinition{new NodeDefinition()};
 
     nodeDefinition->mParentIdx = parentIndex;
@@ -964,7 +1080,7 @@ float LoadKeyFrames(ConversionContext& context, const gltf2::Animation::Channel&
   return duration;
 }
 
-float LoadBlendShapeKeyFrames(ConversionContext& context, const gltf2::Animation::Channel& channel, Index nodeIndex, uint32_t& propertyIndex, std::vector<Dali::Scene3D::Loader::AnimatedProperty>& properties)
+float LoadBlendShapeKeyFrames(ConversionContext& context, const gltf2::Animation::Channel& channel, Index nodeIndex, uint32_t& propertyIndex, AnimationDefinition& animationDefinition)
 {
   const gltf2::Accessor& input  = *channel.mSampler->mInput;
   const gltf2::Accessor& output = *channel.mSampler->mOutput;
@@ -980,7 +1096,7 @@ float LoadBlendShapeKeyFrames(ConversionContext& context, const gltf2::Animation
   const auto  remainingSize = sizeof(weightNameBuffer) - prefixSize;
   for(uint32_t weightIndex = 0u, endWeightIndex = channel.mSampler->mOutput->mCount / channel.mSampler->mInput->mCount; weightIndex < endWeightIndex; ++weightIndex)
   {
-    AnimatedProperty& animatedProperty = properties[propertyIndex++];
+    AnimatedProperty animatedProperty;
 
     animatedProperty.mNodeIndex = nodeIndex;
     snprintf(pWeightName, remainingSize, "%d]", weightIndex);
@@ -1000,11 +1116,28 @@ float LoadBlendShapeKeyFrames(ConversionContext& context, const gltf2::Animation
     }
 
     animatedProperty.mTimePeriod = {0.f, duration};
+
+    animationDefinition.SetProperty(propertyIndex++, std::move(animatedProperty));
   }
 
   return duration;
 }
 
+template<typename T>
+float LoadAnimation(AnimationDefinition& animationDefinition, Index nodeIndex, Index propertyIndex, const std::string& propertyName, const gltf2::Animation::Channel& channel, ConversionContext& context)
+{
+  AnimatedProperty animatedProperty;
+  animatedProperty.mNodeIndex    = nodeIndex;
+  animatedProperty.mPropertyName = propertyName;
+
+  animatedProperty.mKeyFrames  = KeyFrames::New();
+  float duration               = LoadKeyFrames<T>(context, channel, animatedProperty.mKeyFrames, channel.mTarget.mPath);
+  animatedProperty.mTimePeriod = {0.f, duration};
+
+  animationDefinition.SetProperty(propertyIndex, std::move(animatedProperty));
+  return duration;
+}
+
 void ConvertAnimations(const gltf2::Document& document, ConversionContext& context)
 {
   auto& output = context.mOutput;
@@ -1013,11 +1146,11 @@ void ConvertAnimations(const gltf2::Document& document, ConversionContext& conte
 
   for(const auto& animation : document.mAnimations)
   {
-    AnimationDefinition animationDef;
+    AnimationDefinition animationDefinition;
 
     if(!animation.mName.empty())
     {
-      animationDef.mName = animation.mName;
+      animationDefinition.SetName(animation.mName.data());
     }
 
     uint32_t numberOfProperties = 0u;
@@ -1032,7 +1165,7 @@ void ConvertAnimations(const gltf2::Document& document, ConversionContext& conte
         numberOfProperties++;
       }
     }
-    animationDef.mProperties.resize(numberOfProperties);
+    animationDefinition.ReserveSize(numberOfProperties);
 
     Index propertyIndex = 0u;
     for(const auto& channel : animation.mChannels)
@@ -1044,46 +1177,22 @@ void ConvertAnimations(const gltf2::Document& document, ConversionContext& conte
       {
         case gltf2::Animation::Channel::Target::TRANSLATION:
         {
-          AnimatedProperty& animatedProperty = animationDef.mProperties[propertyIndex];
-
-          animatedProperty.mNodeIndex    = nodeIndex;
-          animatedProperty.mPropertyName = POSITION_PROPERTY;
-
-          animatedProperty.mKeyFrames = KeyFrames::New();
-          duration                    = LoadKeyFrames<Vector3>(context, channel, animatedProperty.mKeyFrames, channel.mTarget.mPath);
-
-          animatedProperty.mTimePeriod = {0.f, duration};
+          duration = LoadAnimation<Vector3>(animationDefinition, nodeIndex, propertyIndex, POSITION_PROPERTY.data(), channel, context);
           break;
         }
         case gltf2::Animation::Channel::Target::ROTATION:
         {
-          AnimatedProperty& animatedProperty = animationDef.mProperties[propertyIndex];
-
-          animatedProperty.mNodeIndex    = nodeIndex;
-          animatedProperty.mPropertyName = ORIENTATION_PROPERTY;
-
-          animatedProperty.mKeyFrames = KeyFrames::New();
-          duration                    = LoadKeyFrames<Quaternion>(context, channel, animatedProperty.mKeyFrames, channel.mTarget.mPath);
-
-          animatedProperty.mTimePeriod = {0.f, duration};
+          duration = LoadAnimation<Quaternion>(animationDefinition, nodeIndex, propertyIndex, ORIENTATION_PROPERTY.data(), channel, context);
           break;
         }
         case gltf2::Animation::Channel::Target::SCALE:
         {
-          AnimatedProperty& animatedProperty = animationDef.mProperties[propertyIndex];
-
-          animatedProperty.mNodeIndex    = nodeIndex;
-          animatedProperty.mPropertyName = SCALE_PROPERTY;
-
-          animatedProperty.mKeyFrames = KeyFrames::New();
-          duration                    = LoadKeyFrames<Vector3>(context, channel, animatedProperty.mKeyFrames, channel.mTarget.mPath);
-
-          animatedProperty.mTimePeriod = {0.f, duration};
+          duration = LoadAnimation<Vector3>(animationDefinition, nodeIndex, propertyIndex, SCALE_PROPERTY.data(), channel, context);
           break;
         }
         case gltf2::Animation::Channel::Target::WEIGHTS:
         {
-          duration = LoadBlendShapeKeyFrames(context, channel, nodeIndex, propertyIndex, animationDef.mProperties);
+          duration = LoadBlendShapeKeyFrames(context, channel, nodeIndex, propertyIndex, animationDefinition);
 
           break;
         }
@@ -1094,12 +1203,12 @@ void ConvertAnimations(const gltf2::Document& document, ConversionContext& conte
         }
       }
 
-      animationDef.mDuration = std::max(duration, animationDef.mDuration);
+      animationDefinition.SetDuration(std::max(duration, animationDefinition.GetDuration()));
 
       ++propertyIndex;
     }
 
-    output.mAnimationDefinitions.push_back(std::move(animationDef));
+    output.mAnimationDefinitions.push_back(std::move(animationDefinition));
   }
 }
 
@@ -1203,33 +1312,33 @@ void ProduceShaders(ShaderDefinitionFactory& shaderFactory, Dali::Scene3D::Loade
 
 void SetObjectReaders()
 {
-  json::SetObjectReader(BUFFER_READER);
-  json::SetObjectReader(BUFFER_VIEW_READER);
-  json::SetObjectReader(BUFFER_VIEW_CLIENT_READER);
-  json::SetObjectReader(COMPONENT_TYPED_BUFFER_VIEW_CLIENT_READER);
-  json::SetObjectReader(ACCESSOR_SPARSE_READER);
-  json::SetObjectReader(ACCESSOR_READER);
-  json::SetObjectReader(IMAGE_READER);
-  json::SetObjectReader(SAMPLER_READER);
-  json::SetObjectReader(TEXURE_READER);
-  json::SetObjectReader(TEXURE_INFO_READER);
-  json::SetObjectReader(MATERIAL_PBR_READER);
-  json::SetObjectReader(MATERIAL_SPECULAR_READER);
-  json::SetObjectReader(MATERIAL_IOR_READER);
-  json::SetObjectReader(MATERIAL_EXTENSION_READER);
-  json::SetObjectReader(MATERIAL_READER);
-  json::SetObjectReader(MESH_PRIMITIVE_READER);
-  json::SetObjectReader(MESH_READER);
-  json::SetObjectReader(SKIN_READER);
-  json::SetObjectReader(CAMERA_PERSPECTIVE_READER);
-  json::SetObjectReader(CAMERA_ORTHOGRAPHIC_READER);
-  json::SetObjectReader(CAMERA_READER);
-  json::SetObjectReader(NODE_READER);
-  json::SetObjectReader(ANIMATION_SAMPLER_READER);
-  json::SetObjectReader(ANIMATION_TARGET_READER);
-  json::SetObjectReader(ANIMATION_CHANNEL_READER);
-  json::SetObjectReader(ANIMATION_READER);
-  json::SetObjectReader(SCENE_READER);
+  json::SetObjectReader(GetBufferReader());
+  json::SetObjectReader(GetBufferViewReader());
+  json::SetObjectReader(GetBufferViewClientReader());
+  json::SetObjectReader(GetComponentTypedBufferViewClientReader());
+  json::SetObjectReader(GetAccessorSparseReader());
+  json::SetObjectReader(GetAccessorReader());
+  json::SetObjectReader(GetImageReader());
+  json::SetObjectReader(GetSamplerReader());
+  json::SetObjectReader(GetTextureReader());
+  json::SetObjectReader(GetTextureInfoReader());
+  json::SetObjectReader(GetMaterialPbrReader());
+  json::SetObjectReader(GetMaterialSpecularReader());
+  json::SetObjectReader(GetMaterialIorReader());
+  json::SetObjectReader(GetMaterialExtensionsReader());
+  json::SetObjectReader(GetMaterialReader());
+  json::SetObjectReader(GetMeshPrimitiveReader());
+  json::SetObjectReader(GetMeshReader());
+  json::SetObjectReader(GetSkinReader());
+  json::SetObjectReader(GetCameraPerspectiveReader());
+  json::SetObjectReader(GetCameraOrthographicReader());
+  json::SetObjectReader(GetCameraReader());
+  json::SetObjectReader(GetNodeReader());
+  json::SetObjectReader(GetAnimationSamplerReader());
+  json::SetObjectReader(GetAnimationChannelTargetReader());
+  json::SetObjectReader(GetAnimationChannelReader());
+  json::SetObjectReader(GetAnimationReader());
+  json::SetObjectReader(GetSceneReader());
 }
 
 void SetDefaultEnvironmentMap(const gltf2::Document& document, ConversionContext& context)
@@ -1263,7 +1372,7 @@ const std::string_view GetRendererModelIdentification()
 
 void ReadDocument(const json_object_s& jsonObject, gltf2::Document& document)
 {
-  DOCUMENT_READER.Read(jsonObject, document);
+  GetDocumentReader().Read(jsonObject, document);
 }
 
 void ReadDocumentFromParsedData(const json_object_s& jsonObject, gltf2::Document& document)
index 3bd2c9a..457140e 100644 (file)
@@ -18,7 +18,6 @@
  */
 
 // EXTERNAL INCLUDES
-#include <dali/devel-api/threading/mutex.h>
 #include <dali/public-api/common/dali-common.h>
 
 // INTERNAL INCLUDES
index ec6e95f..88cfad7 100644 (file)
@@ -121,43 +121,47 @@ bool ReadQuadHelper(const TreeNode* tn, const std::array<T*, 4>& quad)
   return true;
 }
 
-const std::map<std::string, Property::Value (*)(const TreeNode*)> kTypeIds{
-  // NONE
-  {"boolean", [](const TreeNode* tn) {
-     return ReadPrimitiveHelper<bool>(tn, ReadBool);
-   }},
-  {"float", [](const TreeNode* tn) {
-     return ReadPrimitiveHelper<float>(tn, ReadFloat);
-   }},
-  {"integer", [](const TreeNode* tn) {
-     return ReadPrimitiveHelper<int>(tn, ReadInt);
-   }},
-  {"vector2", ReadVectorHelper<Vector2>},
-  {"vector3", ReadVectorHelper<Vector3>},
-  {"vector4", ReadVectorHelper<Vector4>},
-  {"matrix3", ReadVectorHelper<Matrix3>},
-  {"matrix", ReadVectorHelper<Matrix>},
-  {"rectangle", [](const TreeNode* tn) {
-     Rect<int> value;
-     if(ReadQuadHelper<int>(tn, {&value.x, &value.y, &value.width, &value.height}))
-     {
-       return Property::Value(value);
-     }
-     return Property::Value();
-   }},
-  {"rotation", ReadRotationHelper},
-  // STRING - not particularly animatable
-  // ARRAY - not particularly animatable
-  // MAP - not particularly animatable
-  {"extents", [](const TreeNode* tn) {
-     Extents value;
-     if(ReadQuadHelper<uint16_t>(tn, {&value.start, &value.end, &value.top, &value.bottom}))
-     {
-       return Property::Value(value);
-     }
-     return Property::Value();
-   }},
-};
+const std::map<std::string_view, Property::Value (*)(const TreeNode*)>& GetTypeIds()
+{
+  static const std::map<std::string_view, Property::Value (*)(const TreeNode*)> kTypeIds{
+    // NONE
+    {"boolean", [](const TreeNode* tn) {
+       return ReadPrimitiveHelper<bool>(tn, ReadBool);
+     }},
+    {"float", [](const TreeNode* tn) {
+       return ReadPrimitiveHelper<float>(tn, ReadFloat);
+     }},
+    {"integer", [](const TreeNode* tn) {
+       return ReadPrimitiveHelper<int>(tn, ReadInt);
+     }},
+    {"vector2", ReadVectorHelper<Vector2>},
+    {"vector3", ReadVectorHelper<Vector3>},
+    {"vector4", ReadVectorHelper<Vector4>},
+    {"matrix3", ReadVectorHelper<Matrix3>},
+    {"matrix", ReadVectorHelper<Matrix>},
+    {"rectangle", [](const TreeNode* tn) {
+       Rect<int> value;
+       if(ReadQuadHelper<int>(tn, {&value.x, &value.y, &value.width, &value.height}))
+       {
+         return Property::Value(value);
+       }
+       return Property::Value();
+     }},
+    {"rotation", ReadRotationHelper},
+    // STRING - not particularly animatable
+    // ARRAY - not particularly animatable
+    // MAP - not particularly animatable
+    {"extents", [](const TreeNode* tn) {
+       Extents value;
+       if(ReadQuadHelper<uint16_t>(tn, {&value.start, &value.end, &value.top, &value.bottom}))
+       {
+         return Property::Value(value);
+       }
+       return Property::Value();
+     }},
+  };
+  return kTypeIds;
+}
 
 Property::Value (*const kArrayPropertyProcessors[])(const TreeNode*){
   ReadVectorHelper<Matrix>,
@@ -469,8 +473,8 @@ Property::Value ReadPropertyValue(const Toolkit::TreeNode& tn)
     auto jsonType = tn.GetChild("type");
     if(jsonType && jsonType->GetType() == TreeNode::STRING)
     {
-      auto iFind = kTypeIds.find(jsonType->GetString());
-      if(iFind != kTypeIds.end())
+      auto iFind = GetTypeIds().find(jsonType->GetString());
+      if(iFind != GetTypeIds().end())
       {
         propValue = iFind->second(tn.GetChild("value"));
       }
diff --git a/dali-scene3d/internal/model-components/material-impl.cpp b/dali-scene3d/internal/model-components/material-impl.cpp
new file mode 100644 (file)
index 0000000..7cfb847
--- /dev/null
@@ -0,0 +1,815 @@
+/*
+ * Copyright (c) 2023 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/model-components/material-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/object/type-registry-helper.h>
+#include <dali/public-api/object/type-registry.h>
+#include <dali/public-api/rendering/sampler.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-scene3d/internal/model-components/material-modify-observer.h>
+#include <dali-scene3d/public-api/loader/node-definition.h>
+#include <dali-scene3d/public-api/loader/renderer-state.h>
+#include <dali-scene3d/public-api/loader/utils.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+namespace
+{
+/**
+ * Creates Material through type registry
+ */
+BaseHandle Create()
+{
+  return Scene3D::Material::New();
+}
+
+// Setup properties, signals and actions using the type-registry.
+DALI_TYPE_REGISTRATION_BEGIN(Scene3D::Material, Dali::BaseHandle, Create);
+DALI_TYPE_REGISTRATION_END()
+
+static constexpr uint32_t         OFFSET_FOR_DIFFUSE_CUBE_TEXTURE  = 2u;
+static constexpr uint32_t         OFFSET_FOR_SPECULAR_CUBE_TEXTURE = 1u;
+static constexpr uint32_t         INVALID_INDEX                    = 0u;
+static constexpr uint32_t         ALPHA_CUTOFF_FLAG                = Scene3D::Loader::MaterialDefinition::Flags::SUBSURFACE << 1;
+static constexpr std::string_view THREE_TEX_KEYWORD                = "THREE_TEX";
+static constexpr std::string_view GLTF_CHANNELS_KEYWORD            = "GLTF_CHANNELS";
+
+enum TextureIndex
+{
+  BASE_COLOR,
+  METALLIC_ROUGHNESS,
+  NORMAL,
+  OCCLUSION,
+  EMISSIVE,
+  SPECULAR,
+  SPECULAR_COLOR,
+  TEXTURE_TYPE_NUMBER,
+};
+
+} // unnamed namespace
+
+MaterialPtr Material::New()
+{
+  MaterialPtr material = new Material();
+
+  material->Initialize();
+
+  return material;
+}
+
+Material::Material()
+: mName(),
+  mModifyFlag(MaterialModifyObserver::ModifyFlag::NONE),
+  mObserverNotifying(false)
+{
+  mAsyncImageLoader = Dali::Toolkit::AsyncImageLoader::New();
+  mAsyncImageLoader.ImageLoadedSignal().Connect(this, &Material::TextureLoadComplete);
+  mTextureInformations.assign(TEXTURE_TYPE_NUMBER, TextureInformation());
+  mTextureInformations[BASE_COLOR].mSemantic = Scene3D::Loader::MaterialDefinition::ALBEDO;
+  // TODO : How we support dli manner
+  mTextureInformations[METALLIC_ROUGHNESS].mSemantic = Scene3D::Loader::MaterialDefinition::METALLIC | Scene3D::Loader::MaterialDefinition::ROUGHNESS |
+                                                       Scene3D::Loader::MaterialDefinition::GLTF_CHANNELS;
+  mTextureInformations[NORMAL].mSemantic         = Scene3D::Loader::MaterialDefinition::NORMAL;
+  mTextureInformations[OCCLUSION].mSemantic      = Scene3D::Loader::MaterialDefinition::OCCLUSION;
+  mTextureInformations[EMISSIVE].mSemantic       = Scene3D::Loader::MaterialDefinition::EMISSIVE;
+  mTextureInformations[SPECULAR].mSemantic       = Scene3D::Loader::MaterialDefinition::SPECULAR;
+  mTextureInformations[SPECULAR_COLOR].mSemantic = Scene3D::Loader::MaterialDefinition::SPECULAR_COLOR;
+
+  mTextureInformations[BASE_COLOR].mDefineKeyword         = "BASECOLOR_TEX";
+  mTextureInformations[METALLIC_ROUGHNESS].mDefineKeyword = "METALLIC_ROUGHNESS_TEX";
+  mTextureInformations[NORMAL].mDefineKeyword             = "NORMAL_TEX";
+  mTextureInformations[OCCLUSION].mDefineKeyword          = "OCCLUSION";
+  mTextureInformations[EMISSIVE].mDefineKeyword           = "EMISSIVE";
+  mTextureInformations[SPECULAR].mDefineKeyword           = "MATERIAL_SPECULAR_TEXTURE";
+  mTextureInformations[SPECULAR_COLOR].mDefineKeyword     = "MATERIAL_SPECULAR_COLOR_TEXTURE";
+
+  mTextureInformations[TextureIndex::EMISSIVE].mFactor = Vector4::ZERO;
+}
+
+Material::~Material() = default;
+
+void Material::Initialize()
+{
+}
+
+void Material::SetProperty(Dali::Property::Index index, Dali::Property::Value propertyValue)
+{
+  bool needToApply = true;
+  switch(index)
+  {
+    case Dali::Scene3D::Material::Property::NAME:
+    {
+      std::string name;
+      if(propertyValue.Get(name))
+      {
+        mName = name;
+      }
+      needToApply = false;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::BASE_COLOR_URL:
+    {
+      std::string baseColorUrl;
+      if(propertyValue.Get(baseColorUrl))
+      {
+        RequestTextureLoad(mTextureInformations[TextureIndex::BASE_COLOR], baseColorUrl);
+        needToApply = false;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::BASE_COLOR_FACTOR:
+    {
+      Vector4 baseColorFactor;
+      if(propertyValue.Get(baseColorFactor))
+      {
+        mTextureInformations[TextureIndex::BASE_COLOR].mFactor = baseColorFactor;
+        mModifyFlag |= MaterialModifyObserver::ModifyFlag::UNIFORM;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::METALLIC_ROUGHNESS_URL:
+    {
+      std::string metallicRoughnessUrl;
+      if(propertyValue.Get(metallicRoughnessUrl))
+      {
+        RequestTextureLoad(mTextureInformations[TextureIndex::METALLIC_ROUGHNESS], metallicRoughnessUrl);
+        needToApply = false;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::METALLIC_FACTOR:
+    {
+      float metallicFactor;
+      if(propertyValue.Get(metallicFactor))
+      {
+        mTextureInformations[TextureIndex::METALLIC_ROUGHNESS].mFactor.x = metallicFactor;
+        mModifyFlag |= MaterialModifyObserver::ModifyFlag::UNIFORM;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::ROUGHNESS_FACTOR:
+    {
+      float roughnessFactor;
+      if(propertyValue.Get(roughnessFactor))
+      {
+        mTextureInformations[TextureIndex::METALLIC_ROUGHNESS].mFactor.y = roughnessFactor;
+        mModifyFlag |= MaterialModifyObserver::ModifyFlag::UNIFORM;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::NORMAL_URL:
+    {
+      std::string normalUrl;
+      if(propertyValue.Get(normalUrl))
+      {
+        RequestTextureLoad(mTextureInformations[TextureIndex::NORMAL], normalUrl);
+        needToApply = false;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::NORMAL_SCALE:
+    {
+      float normalScale;
+      if(propertyValue.Get(normalScale))
+      {
+        mTextureInformations[TextureIndex::NORMAL].mFactor.x = normalScale;
+        mModifyFlag |= MaterialModifyObserver::ModifyFlag::UNIFORM;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::OCCLUSION_URL:
+    {
+      std::string occlusionUrl;
+      if(propertyValue.Get(occlusionUrl))
+      {
+        RequestTextureLoad(mTextureInformations[TextureIndex::OCCLUSION], occlusionUrl);
+        needToApply = false;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::OCCLUSION_STRENGTH:
+    {
+      float occlusionStrength;
+      if(propertyValue.Get(occlusionStrength))
+      {
+        mTextureInformations[TextureIndex::OCCLUSION].mFactor.x = occlusionStrength;
+        mModifyFlag |= MaterialModifyObserver::ModifyFlag::UNIFORM;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::EMISSIVE_URL:
+    {
+      std::string emissiveUrl;
+      if(propertyValue.Get(emissiveUrl))
+      {
+        RequestTextureLoad(mTextureInformations[TextureIndex::EMISSIVE], emissiveUrl);
+        needToApply = false;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::EMISSIVE_FACTOR:
+    {
+      Vector3 emissiveFactor;
+      if(propertyValue.Get(emissiveFactor))
+      {
+        mTextureInformations[TextureIndex::EMISSIVE].mFactor = emissiveFactor;
+        mModifyFlag |= MaterialModifyObserver::ModifyFlag::UNIFORM;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::ALPHA_MODE:
+    {
+      Dali::Scene3D::Material::AlphaModeType alphaMode;
+      if(propertyValue.Get(alphaMode))
+      {
+        mAlphaMode = alphaMode;
+        mModifyFlag |= MaterialModifyObserver::ModifyFlag::UNIFORM;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::ALPHA_CUTOFF:
+    {
+      float alphaCutoff;
+      if(propertyValue.Get(alphaCutoff))
+      {
+        mAlphaCutoff = alphaCutoff;
+        mModifyFlag |= MaterialModifyObserver::ModifyFlag::UNIFORM;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::DOUBLE_SIDED:
+    {
+      bool doubleSided;
+      if(propertyValue.Get(doubleSided))
+      {
+        mDoubleSided = doubleSided;
+        mModifyFlag |= MaterialModifyObserver::ModifyFlag::UNIFORM;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::IOR:
+    {
+      float ior;
+      if(propertyValue.Get(ior))
+      {
+        mIor = ior;
+        mModifyFlag |= MaterialModifyObserver::ModifyFlag::UNIFORM;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::SPECULAR_URL:
+    {
+      std::string specularUrl;
+      if(propertyValue.Get(specularUrl))
+      {
+        RequestTextureLoad(mTextureInformations[TextureIndex::SPECULAR], specularUrl);
+        needToApply = false;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::SPECULAR_FACTOR:
+    {
+      float specularFactor;
+      if(propertyValue.Get(specularFactor))
+      {
+        mTextureInformations[TextureIndex::SPECULAR].mFactor.x = specularFactor;
+        mModifyFlag |= MaterialModifyObserver::ModifyFlag::UNIFORM;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::SPECULAR_COLOR_URL:
+    {
+      std::string specularColorUrl;
+      if(propertyValue.Get(specularColorUrl))
+      {
+        RequestTextureLoad(mTextureInformations[TextureIndex::SPECULAR_COLOR], specularColorUrl);
+        needToApply = false;
+      }
+      break;
+    }
+    case Dali::Scene3D::Material::Property::SPECULAR_COLOR_FACTOR:
+    {
+      Vector3 specularColorFactor;
+      if(propertyValue.Get(specularColorFactor))
+      {
+        mTextureInformations[TextureIndex::SPECULAR_COLOR].mFactor = specularColorFactor;
+        mModifyFlag |= MaterialModifyObserver::ModifyFlag::UNIFORM;
+      }
+      break;
+    }
+  }
+
+  if(needToApply)
+  {
+    Apply();
+  }
+}
+
+Dali::Property::Value Material::GetProperty(Dali::Property::Index index) const
+{
+  Dali::Property::Value value;
+  switch(index)
+  {
+    case Dali::Scene3D::Material::Property::NAME:
+    {
+      value = mName;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::BASE_COLOR_URL:
+    {
+      value = mTextureInformations[TextureIndex::BASE_COLOR].mUrl;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::BASE_COLOR_FACTOR:
+    {
+      value = mTextureInformations[TextureIndex::BASE_COLOR].mFactor;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::METALLIC_ROUGHNESS_URL:
+    {
+      value = mTextureInformations[TextureIndex::METALLIC_ROUGHNESS].mUrl;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::METALLIC_FACTOR:
+    {
+      value = mTextureInformations[TextureIndex::METALLIC_ROUGHNESS].mFactor.x;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::ROUGHNESS_FACTOR:
+    {
+      value = mTextureInformations[TextureIndex::METALLIC_ROUGHNESS].mFactor.y;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::NORMAL_URL:
+    {
+      value = mTextureInformations[TextureIndex::NORMAL].mUrl;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::NORMAL_SCALE:
+    {
+      value = mTextureInformations[TextureIndex::NORMAL].mFactor.x;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::OCCLUSION_URL:
+    {
+      value = mTextureInformations[TextureIndex::OCCLUSION].mUrl;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::OCCLUSION_STRENGTH:
+    {
+      value = mTextureInformations[TextureIndex::OCCLUSION].mFactor.x;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::EMISSIVE_URL:
+    {
+      value = mTextureInformations[TextureIndex::EMISSIVE].mUrl;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::EMISSIVE_FACTOR:
+    {
+      value = Vector3(mTextureInformations[TextureIndex::EMISSIVE].mFactor);
+      break;
+    }
+    case Dali::Scene3D::Material::Property::ALPHA_MODE:
+    {
+      value = mAlphaMode;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::ALPHA_CUTOFF:
+    {
+      value = mAlphaCutoff;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::DOUBLE_SIDED:
+    {
+      value = mDoubleSided;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::IOR:
+    {
+      value = mIor;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::SPECULAR_URL:
+    {
+      value = mTextureInformations[TextureIndex::SPECULAR].mUrl;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::SPECULAR_FACTOR:
+    {
+      value = mTextureInformations[TextureIndex::SPECULAR].mFactor.x;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::SPECULAR_COLOR_URL:
+    {
+      value = mTextureInformations[TextureIndex::SPECULAR_COLOR].mUrl;
+      break;
+    }
+    case Dali::Scene3D::Material::Property::SPECULAR_COLOR_FACTOR:
+    {
+      value = Vector3(mTextureInformations[TextureIndex::SPECULAR_COLOR].mFactor);
+      break;
+    }
+  }
+  return value;
+}
+
+void Material::SetTextureInformation(Scene3D::Material::TextureType index, TextureInformation&& textureInformation)
+{
+  mTextureInformations[index].mFactor  = textureInformation.mFactor;
+  mTextureInformations[index].mSampler = textureInformation.mSampler;
+  mTextureInformations[index].mTexture = textureInformation.mTexture;
+  mTextureInformations[index].mUrl     = std::move(textureInformation.mUrl);
+}
+
+void Material::SetTexture(Scene3D::Material::TextureType index, Dali::Texture texture)
+{
+  if(static_cast<uint32_t>(index) < static_cast<uint32_t>(TextureIndex::TEXTURE_TYPE_NUMBER))
+  {
+    if(mTextureInformations[index].mTexture != texture)
+    {
+      if(mTextureInformations[index].mLoadingTaskId != INVALID_INDEX)
+      {
+        mAsyncImageLoader.Cancel(mTextureInformations[index].mLoadingTaskId);
+        mTextureInformations[index].mLoadingTaskId = INVALID_INDEX;
+      }
+      mTextureInformations[index].mTexture = texture;
+      if(IsResourceReady())
+      {
+        ResourcesLoadComplete();
+      }
+    }
+  }
+}
+
+Dali::Texture Material::GetTexture(Scene3D::Material::TextureType index)
+{
+  if(static_cast<uint32_t>(index) < static_cast<uint32_t>(TextureIndex::TEXTURE_TYPE_NUMBER))
+  {
+    return mTextureInformations[index].mTexture;
+  }
+  return Dali::Texture();
+}
+
+TextureSet Material::GetTextureSet()
+{
+  TextureSet textures = TextureSet::New();
+  for(uint32_t i = 0, count = 0; i < TEXTURE_TYPE_NUMBER; ++i)
+  {
+    if(!mTextureInformations[i].mTexture)
+    {
+      continue;
+    }
+    textures.SetTexture(count, mTextureInformations[i].mTexture);
+    Sampler sampler = mTextureInformations[i].mSampler;
+    if(!sampler)
+    {
+      auto samplerFlag = Scene3D::Loader::SamplerFlags::FILTER_LINEAR | (Scene3D::Loader::SamplerFlags::FILTER_LINEAR << Scene3D::Loader::SamplerFlags::FILTER_MAG_SHIFT) |
+                         (Scene3D::Loader::SamplerFlags::WRAP_REPEAT << Scene3D::Loader::SamplerFlags::WRAP_S_SHIFT) | (Scene3D::Loader::SamplerFlags::WRAP_REPEAT << Scene3D::Loader::SamplerFlags::WRAP_T_SHIFT);
+      sampler = Scene3D::Loader::SamplerFlags::MakeSampler(samplerFlag);
+    }
+    textures.SetSampler(count, sampler);
+    count++;
+  }
+  return textures;
+}
+
+void Material::SetSampler(Scene3D::Material::TextureType index, Dali::Sampler sampler)
+{
+  if(static_cast<uint32_t>(index) < static_cast<uint32_t>(TextureIndex::TEXTURE_TYPE_NUMBER))
+  {
+    mTextureInformations[index].mSampler = sampler;
+  }
+}
+
+Dali::Sampler Material::GetSampler(Scene3D::Material::TextureType index)
+{
+  if(static_cast<uint32_t>(index) < static_cast<uint32_t>(TextureIndex::TEXTURE_TYPE_NUMBER))
+  {
+    return mTextureInformations[index].mSampler;
+  }
+  return Dali::Sampler();
+}
+
+std::string Material::GetVertexShader()
+{
+  return mShaderData.mVertexShaderSource;
+}
+
+std::string Material::GetFragmentShader()
+{
+  return mShaderData.mFragmentShaderSource;
+}
+
+void Material::Apply()
+{
+  if(IsResourceReady())
+  {
+    UpdateMaterialData();
+    NotifyObserver();
+    return;
+  }
+  // The cases this material is applied to primitive are,
+  // 1. material is added on Primitive.
+  // When material is added on Primitive (1 case)
+  //   1-1. when IsResourceReady() returns true,
+  //     Primitive can takes information from Material
+  //   1-2. if false.
+  //     Material will noti to primitives when all resources are ready.
+  // 2. Some properties are changed
+  //   2-1. when IsResourceReady() returns true,
+  //     Call NotifyObserver directly.
+  //   2-2. if false.
+  //     Material will noti to primitives when all resources are ready.
+}
+
+void Material::AddObserver(MaterialModifyObserver* observer)
+{
+  for(auto& observerEntity : mObservers)
+  {
+    if(observerEntity.first == observer)
+    {
+      observerEntity.second = true;
+      return;
+    }
+  }
+  mObservers.push_back({observer, true});
+}
+
+void Material::RemoveObserver(MaterialModifyObserver* observer)
+{
+  // Block during notifying to observer
+  if(mObserverNotifying)
+  {
+    for(uint32_t i = 0; i < mObservers.size(); ++i)
+    {
+      if(mObservers[i].first == observer)
+      {
+        mObservers[i].second = false;
+        return;
+      }
+    }
+  }
+  else
+  {
+    for(uint32_t i = 0; i < mObservers.size(); ++i)
+    {
+      if(mObservers[i].first == observer)
+      {
+        mObservers.erase(mObservers.begin() + i);
+        return;
+      }
+    }
+  }
+}
+
+void Material::UpdateMaterialData()
+{
+  uint32_t materialFlag = 0u;
+  if(mAlphaMode == Dali::Scene3D::Material::AlphaModeType::BLEND)
+  {
+    mIsOpaque = false;
+    mIsMask   = false;
+    materialFlag |= Scene3D::Loader::MaterialDefinition::TRANSPARENCY;
+  }
+  else if(mAlphaMode == Dali::Scene3D::Material::AlphaModeType::MASK)
+  {
+    mIsOpaque = true;
+    mIsMask   = true;
+  }
+  const bool hasTransparency = MaskMatch(materialFlag, Scene3D::Loader::MaterialDefinition::TRANSPARENCY);
+
+  for(auto&& textureInformation : mTextureInformations)
+  {
+    if(!textureInformation.mTexture)
+    {
+      continue;
+    }
+    materialFlag |= textureInformation.mSemantic;
+  }
+
+  if(mMaterialFlag != materialFlag || mShaderData.mVertexShaderSource.empty() || mShaderData.mFragmentShaderSource.empty())
+  {
+    mModifyFlag |= MaterialModifyObserver::ModifyFlag::SHADER;
+
+    mMaterialFlag                     = materialFlag;
+    mShaderData.mVertexShaderSource   = SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_VERT.data();
+    mShaderData.mFragmentShaderSource = SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_FRAG.data();
+
+    std::vector<std::string> defines;
+    defines.push_back(THREE_TEX_KEYWORD.data());
+    for(auto&& textureInformation : mTextureInformations)
+    {
+      if(!textureInformation.mTexture)
+      {
+        continue;
+      }
+      defines.push_back(textureInformation.mDefineKeyword);
+    }
+    defines.push_back(GLTF_CHANNELS_KEYWORD.data());
+
+    for(const auto& define : defines)
+    {
+      Scene3D::Loader::ShaderDefinition::ApplyDefine(mShaderData.mFragmentShaderSource, define);
+    }
+  }
+
+  // Finish to make all the material flag according to the gltf2-util.
+  // Then make defines as fallowing shader-definition-factory.
+
+  // The renderer State below can be used in primitive to set renderer properties.
+
+  // for renderer setting
+  mRendererState = Scene3D::Loader::RendererState::DEPTH_TEST;
+  if(!mDoubleSided)
+  {
+    mRendererState |= Scene3D::Loader::RendererState::CULL_BACK;
+  }
+
+  if(hasTransparency)
+  {
+    mRendererState = (mRendererState | Scene3D::Loader::RendererState::ALPHA_BLEND);
+  }
+}
+
+bool Material::IsResourceReady()
+{
+  for(auto&& textureInformation : mTextureInformations)
+  {
+    if(!textureInformation.IsReady())
+    {
+      return false;
+    }
+  }
+  return true;
+}
+
+void Material::SetRendererUniform(Dali::Renderer renderer)
+{
+  renderer.RegisterProperty("uColorFactor", mTextureInformations[TextureIndex::BASE_COLOR].mFactor);
+  renderer.RegisterProperty("uMetallicFactor", mTextureInformations[TextureIndex::METALLIC_ROUGHNESS].mFactor.x);
+  renderer.RegisterProperty("uRoughnessFactor", mTextureInformations[TextureIndex::METALLIC_ROUGHNESS].mFactor.y);
+  renderer.RegisterProperty("uNormalScale", mTextureInformations[TextureIndex::NORMAL].mFactor.x);
+  if(mTextureInformations[TextureIndex::OCCLUSION].mTexture)
+  {
+    renderer.RegisterProperty("uOcclusionStrength", mTextureInformations[TextureIndex::OCCLUSION].mFactor.x);
+  }
+  renderer.RegisterProperty("uEmissiveFactor", Vector3(mTextureInformations[TextureIndex::EMISSIVE].mFactor));
+  float dielectricSpecular = (Dali::Equals(mIor, -1.0)) ? 0.04f : powf((mIor - 1.0f) / (mIor + 1.0f), 2.0f);
+  renderer.RegisterProperty("uDielectricSpecular", dielectricSpecular);
+  renderer.RegisterProperty("uSpecularFactor", mTextureInformations[TextureIndex::SPECULAR].mFactor.x);
+  renderer.RegisterProperty("uSpecularColorFactor", Vector3(mTextureInformations[TextureIndex::SPECULAR_COLOR].mFactor));
+
+  float opaque = mIsOpaque ? 1.0f : 0.0f;
+  float mask   = mIsMask ? 1.0f : 0.0f;
+  renderer.RegisterProperty("uOpaque", opaque);
+  renderer.RegisterProperty("uMask", mask);
+  renderer.RegisterProperty("uAlphaThreshold", mAlphaCutoff);
+
+  renderer.RegisterProperty("uCubeMatrix", Matrix::IDENTITY);
+
+  renderer.RegisterProperty(Scene3D::Loader::NodeDefinition::GetIblMaxLodUniformName().data(), 6.f);
+  renderer.RegisterProperty(Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), 1.0f);
+  renderer.RegisterProperty(Scene3D::Loader::NodeDefinition::GetIblYDirectionUniformName().data(), Vector3(1.0f, -1.0, 1.0));
+
+  Scene3D::Loader::RendererState::Apply(mRendererState, renderer);
+}
+
+uint32_t Material::GetSpecularImageBasedLightTextureOffset()
+{
+  return OFFSET_FOR_SPECULAR_CUBE_TEXTURE;
+}
+
+uint32_t Material::GetDiffuseImageBasedLightTextureOffset()
+{
+  return OFFSET_FOR_DIFFUSE_CUBE_TEXTURE;
+}
+
+std::string_view Material::GetImageBasedLightScaleFactorName()
+{
+  return Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName();
+}
+
+std::string_view Material::GetImageBasedLightMaxLodUniformName()
+{
+  return Dali::Scene3D::Loader::NodeDefinition::GetIblMaxLodUniformName();
+}
+
+void Material::ResetFlag()
+{
+  mModifyFlag = MaterialModifyObserver::ModifyFlag::NONE;
+}
+
+void Material::NotifyObserver()
+{
+  if(mModifyFlag != MaterialModifyObserver::ModifyFlag::NONE && IsResourceReady())
+  {
+    if(mObserverNotifying)
+    {
+      DALI_LOG_ERROR("Notify during observing is not allowed.");
+      return;
+    }
+
+    Dali::Scene3D::Material handle(this); // Keep itself's life during notify
+
+    // Copy the flag due to the flag can be changed during observe.
+    MaterialModifyObserver::ModifyFlag copiedFlag = mModifyFlag;
+    mModifyFlag                                   = MaterialModifyObserver::ModifyFlag::NONE;
+
+    // Need to block mObserver container change during observe
+    mObserverNotifying = true;
+    for(uint32_t i = 0; i < mObservers.size(); ++i)
+    {
+      if(mObservers[i].second)
+      {
+        mObservers[i].first->OnMaterialModified(handle, copiedFlag);
+      }
+    }
+    mObserverNotifying = false;
+
+    // Resolve observer queue during notify
+    mObservers.erase(std::remove_if(mObservers.begin(), mObservers.end(), [](auto& e) { return !e.second; }), mObservers.end());
+  }
+}
+
+void Material::RequestTextureLoad(TextureInformation& textureInformation, const std::string& url)
+{
+  if(textureInformation.mUrl == url)
+  {
+    return;
+  }
+
+  textureInformation.mUrl = url;
+  if(textureInformation.mLoadingTaskId != INVALID_INDEX)
+  {
+    mAsyncImageLoader.Cancel(textureInformation.mLoadingTaskId);
+    textureInformation.mLoadingTaskId = INVALID_INDEX;
+  }
+
+  if(url.empty())
+  {
+    textureInformation.mTexture.Reset();
+    return;
+  }
+  textureInformation.mLoadingTaskId = mAsyncImageLoader.Load(url);
+}
+
+void Material::TextureLoadComplete(uint32_t loadedTaskId, PixelData pixelData)
+{
+  for(auto&& textureInformation : mTextureInformations)
+  {
+    if(textureInformation.mLoadingTaskId != loadedTaskId)
+    {
+      continue;
+    }
+
+    if(pixelData)
+    {
+      textureInformation.mTexture = Texture::New(Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight());
+      textureInformation.mTexture.Upload(pixelData);
+    }
+    textureInformation.mLoadingTaskId = INVALID_INDEX;
+    break;
+  }
+
+  if(IsResourceReady())
+  {
+    ResourcesLoadComplete();
+  }
+}
+
+void Material::ResourcesLoadComplete()
+{
+  mModifyFlag |= MaterialModifyObserver::ModifyFlag::TEXTURE;
+  Apply();
+}
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
diff --git a/dali-scene3d/internal/model-components/material-impl.h b/dali-scene3d/internal/model-components/material-impl.h
new file mode 100644 (file)
index 0000000..563bf82
--- /dev/null
@@ -0,0 +1,335 @@
+#ifndef DALI_SCENE3D_MODEL_COMPONENTS_MATERIAL_IMPL_H
+#define DALI_SCENE3D_MODEL_COMPONENTS_MATERIAL_IMPL_H
+
+/*
+ * Copyright (c) 2023 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-toolkit/public-api/image-loader/async-image-loader.h>
+#include <dali/public-api/common/dali-common.h>
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <dali/public-api/object/base-object.h>
+#include <dali/public-api/object/property-value.h>
+#include <dali/public-api/object/property.h>
+#include <dali/public-api/rendering/texture.h>
+#include <set>
+#include <utility>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/model-components/material-modify-observer.h>
+#include <dali-scene3d/public-api/loader/material-definition.h>
+#include <dali-scene3d/public-api/loader/shader-definition.h>
+#include <dali-scene3d/public-api/model-components/material.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+using MaterialPtr = IntrusivePtr<Material>;
+
+/**
+ * @brief This is the internal base class for Material of model.
+ *
+ * @SINCE_2_2.99
+ */
+class Material : public BaseObject, public ConnectionTracker
+{
+private:
+  using ObserverContainer = std::vector<std::pair<MaterialModifyObserver*, bool>>;
+
+public:
+  /**
+   * @brief Pair of texture handle and it's own scale factor. Texture handle can be empty
+   */
+  struct TextureInformation
+  {
+    bool IsReady()
+    {
+      return mLoadingTaskId == 0u;
+    }
+
+    std::string   mUrl;
+    Dali::Texture mTexture;
+    Vector4       mFactor{Vector4::ONE};
+    Dali::Sampler mSampler;
+    uint32_t      mLoadingTaskId{0u};
+    uint32_t      mSemantic;
+    std::string   mDefineKeyword;
+  };
+
+  using TextureInformationContainer = std::vector<TextureInformation>;
+
+public:
+  // Creation & Destruction
+  /**
+   * @brief Create a new Material object.
+   * @return A smart-pointer to the newly allocated Material.
+   */
+  static MaterialPtr New();
+
+protected:
+  /**
+   * @brief Construct a new Material.
+   */
+  Material();
+
+  /**
+   * @brief Second-phase constructor.
+   */
+  void Initialize();
+
+  /**
+   * @brief Virtual destructor.
+   */
+  virtual ~Material();
+
+public:
+  /**
+   * @copydoc Dali::Scene3D::Material::SetProperty()
+   */
+  void SetProperty(Dali::Property::Index index, Dali::Property::Value propertyValue);
+
+  /**
+   * @copydoc Dali::Scene3D::Material::GetProperty()
+   */
+  Dali::Property::Value GetProperty(Dali::Property::Index index) const;
+
+  /**
+   * @brief Set a texture information for the material.
+   *
+   * @param[in] index The index of the texture to set.
+   * @param[in] textureInformation The texture information to set.
+   *
+   * @note This function moves the value of textureInformation.
+   */
+  void SetTextureInformation(Scene3D::Material::TextureType index, TextureInformation&& textureInformation);
+
+  /**
+   * @brief Set a texture for the material.
+   *
+   * @param[in] index The index of the texture to set.
+   * @param[in] texture The texture to set.
+   */
+  void SetTexture(Scene3D::Material::TextureType index, Dali::Texture texture);
+
+  /**
+   * @brief Get texture for the material.
+   *
+   * @param[in] index The index of the texture to get.
+   *
+   * @return The texture at the given index.
+   */
+  Dali::Texture GetTexture(Scene3D::Material::TextureType index);
+
+  /**
+   * @brief Get the texture set for this material.
+   *
+   * @return The texture set for this material.
+   */
+  TextureSet GetTextureSet();
+
+  /**
+   * @brief Set a sampler for the material.
+   *
+   * @param[in] index The index of the sampler to set.
+   * @param[in] sampler The sampler to set.
+   */
+  void SetSampler(Scene3D::Material::TextureType index, Dali::Sampler sampler);
+
+  /**
+   * @brief Get a sampler for the material.
+
+   * @param[in] index The index of the sampler to get.
+   *
+   * @return The sampler at the given index.
+   */
+  Dali::Sampler GetSampler(Scene3D::Material::TextureType index);
+
+  /**
+   * @brief Get vertex shader code for this material.
+   *
+   * @return Vertex shader code for this material.
+   */
+  std::string GetVertexShader();
+
+  /**
+   * @brief Get fragment shader code for this material.
+   *
+   * @return Fragment shader code for this material.
+   */
+  std::string GetFragmentShader();
+
+public:
+  /**
+   * @brief Add observer to this material.
+   *
+   * @param[in] observer Pointer of observer.
+   */
+  void AddObserver(MaterialModifyObserver* observer);
+
+  /**
+   * @brief Remove observer from this material.
+   *
+   * @param[in] observer Pointer of observer.
+   */
+  void RemoveObserver(MaterialModifyObserver* observer);
+
+  /**
+   * @brief Update material data.
+   */
+  void UpdateMaterialData();
+
+  /**
+   * @brief Set uniform value to the Renderer.
+   *
+   * @param[in] renderer Renderer object.
+   */
+  void SetRendererUniform(Dali::Renderer renderer);
+
+  /**
+   * @brief Get specular image based light texture offset.
+   *
+   * @return Specular image based light texture offset.
+   */
+  uint32_t GetSpecularImageBasedLightTextureOffset();
+
+  /**
+   * @brief Get diffuse image based light texture offset.
+   *
+   * @return Diffuse image based light texture offset.
+   */
+  uint32_t GetDiffuseImageBasedLightTextureOffset();
+
+  /**
+   * @brief Get image based light scale factor name.
+   *
+   * @return Image based light scale factor name.
+   */
+  std::string_view GetImageBasedLightScaleFactorName();
+
+  /**
+   * @brief Get image based light max lod uniform name.
+   *
+   * @return Image based light max lod uniform name.
+   */
+  std::string_view GetImageBasedLightMaxLodUniformName();
+
+  /**
+   * @brief Check if resource is ready.
+   *
+   * @return True if resource is ready, false otherwise.
+   */
+  bool IsResourceReady();
+
+  /**
+   * @brief Reset dirty flag of this material.
+   */
+  void ResetFlag();
+
+private:
+  /**
+   * @brief Check modify flag and send observers the material changeness.
+   * It will clean up modify flag
+   */
+  void NotifyObserver();
+
+  /**
+   * @brief Request loading an image from a URL and store it in TextureInformation.
+   *
+   * @param[in] textureInformation TextureInformation object to store loaded texture information.
+   * @param[in] url URL of the image to load.
+   */
+  void RequestTextureLoad(TextureInformation& textureInformation, const std::string& url);
+
+  /**
+   * @brief Called when loading requested by RequestTextureLoad is complete.
+   *
+   * @param[in] loadedTaskId ID of the loaded texture.
+   * @param[in] pixelData PixelData of the loaded texture.
+   */
+  void TextureLoadComplete(uint32_t loadedTaskId, PixelData pixelData);
+
+  /**
+   * @brief Called when all requested textures are loaded.
+   */
+  void ResourcesLoadComplete();
+
+  /**
+   * @brief Update the material using each attribute of this material and send a notification to the ModelPrimitive class.
+   */
+  void Apply();
+
+private:
+  // Delete copy & move operator
+  Material(const Material&) = delete;
+  Material(Material&&)      = delete;
+  Material& operator=(const Material& rhs) = delete;
+  Material& operator=(Material&& rhs) = delete;
+
+private:
+  ObserverContainer mObservers{}; ///< List of observers who need to be notified after some properties are changed.
+
+  TextureInformationContainer     mTextureInformations;
+  Dali::Toolkit::AsyncImageLoader mAsyncImageLoader;
+
+  std::string                            mName;                                                   ///< Material name
+  Dali::Scene3D::Material::AlphaModeType mAlphaMode   = Scene3D::Material::AlphaModeType::OPAQUE; ///< Alpha mode
+  float                                  mAlphaCutoff = 0.5f;                                     ///< Alpha cutoff value
+  bool                                   mDoubleSided = false;                                    ///< Whether to render both sides
+  float                                  mIor         = -1.0f;                                    ///< Index of refraction (TODO: Magic number)
+  MaterialModifyObserver::ModifyFlag     mModifyFlag  = MaterialModifyObserver::ModifyFlag::NONE; ///< Modified dirty flags
+
+  Scene3D::Loader::ShaderDefinition::RawData mShaderData;
+
+  uint32_t                             mMaterialFlag  = 0u;
+  Scene3D::Loader::RendererState::Type mRendererState = Scene3D::Loader::RendererState::NONE;
+
+  bool mIsOpaque = true;
+  bool mIsMask   = false;
+  bool mObserverNotifying; ///< True if observe is notify now. If then, we should not change the mObservers.
+};
+
+} // namespace Internal
+
+// Helpers for public-api forwarding methods
+
+inline Internal::Material& GetImplementation(Dali::Scene3D::Material& material)
+{
+  DALI_ASSERT_ALWAYS(material && "Material handle is empty");
+
+  BaseObject& handle = material.GetBaseObject();
+
+  return static_cast<Internal::Material&>(handle);
+}
+
+inline const Internal::Material& GetImplementation(const Dali::Scene3D::Material& material)
+{
+  DALI_ASSERT_ALWAYS(material && "Material handle is empty");
+
+  const BaseObject& handle = material.GetBaseObject();
+
+  return static_cast<const Internal::Material&>(handle);
+}
+
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_MODEL_COMPONENTS_MATERIAL_IMPL_H
diff --git a/dali-scene3d/internal/model-components/material-modify-observer.h b/dali-scene3d/internal/model-components/material-modify-observer.h
new file mode 100644 (file)
index 0000000..4b0c93d
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef DALI_SCENE3D_MODEL_COMPONENTS_MATERIAL_MODIFY_OBSERVER_H
+#define DALI_SCENE3D_MODEL_COMPONENTS_MATERIAL_MODIFY_OBSERVER_H
+
+/*
+ * Copyright (c) 2023 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/devel-api/common/bitwise-enum.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/model-components/material.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+/**
+ * @brief MaterialModifyObserver is a class to notify that the material has changed.
+ */
+class MaterialModifyObserver
+{
+public:
+  /**
+   * @brief Flag which informations are changed.
+   */
+  enum ModifyFlag
+  {
+    NONE    = 0,
+    TEXTURE = 1 << 0,
+    SHADER  = 1 << 1,
+    UNIFORM = 1 << 2,
+  };
+
+  /**
+    * @brief Notify to observers that texture is changed.
+    *
+    * @param[in] material The handle material itself. TODO : Are we need this?
+    * @param[in] flag Flag which informations are changed.
+    */
+  virtual void OnMaterialModified(Dali::Scene3D::Material material, ModifyFlag flag) = 0;
+};
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+// specialization has to be done in the same namespace
+template<>
+struct EnableBitMaskOperators<Scene3D::Internal::MaterialModifyObserver::ModifyFlag>
+{
+  static const bool ENABLE = true;
+};
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_MODEL_COMPONENTS_MATERIAL_MODIFY_OBSERVER_H
diff --git a/dali-scene3d/internal/model-components/model-node-data-impl.cpp b/dali-scene3d/internal/model-components/model-node-data-impl.cpp
new file mode 100644 (file)
index 0000000..308dad9
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2023 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/model-components/model-node-data-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/object/type-registry-helper.h>
+#include <dali/public-api/object/type-registry.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/model-components/model-primitive-impl.h>
+#include <dali-scene3d/public-api/model-components/model-node.h>
+
+namespace
+{
+} // namespace
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+namespace
+{
+/**
+ * Creates control through type registry
+ */
+BaseHandle Create()
+{
+  return Scene3D::ModelNode::New();
+}
+
+// Setup properties, signals and actions using the type-registry.
+DALI_TYPE_REGISTRATION_BEGIN(Scene3D::ModelNode, Dali::CustomActor, Create);
+DALI_TYPE_REGISTRATION_END()
+} // unnamed namespace
+
+ModelNode::Impl::Impl(ModelNode& modelNodeImpl)
+: mModelNodeImpl(modelNodeImpl)
+{
+}
+
+ModelNode::Impl::~Impl()
+{
+  for(auto&& primitive : mModelPrimitiveContainer)
+  {
+    GetImplementation(primitive).RemovePrimitiveObserver(this);
+  }
+  for(auto&& boneData : mBoneDataContainer)
+  {
+    boneData.primitive.Reset();
+    if(boneData.constraint)
+    {
+      boneData.constraint.Remove();
+      boneData.constraint.Reset();
+    }
+  }
+}
+
+void ModelNode::Impl::OnSceneConnection(int depth)
+{
+}
+
+void ModelNode::Impl::OnSceneDisconnection()
+{
+}
+
+void ModelNode::Impl::OnRendererCreated(Renderer renderer)
+{
+  mModelNodeImpl.Self().AddRenderer(renderer);
+}
+
+// Public Method
+
+void ModelNode::Impl::AddModelPrimitive(Scene3D::ModelPrimitive modelPrimitive)
+{
+  for(auto&& primitive : mModelPrimitiveContainer)
+  {
+    if(primitive == modelPrimitive)
+    {
+      return;
+    }
+  }
+
+  mModelPrimitiveContainer.push_back(modelPrimitive);
+
+  Actor self = mModelNodeImpl.Self();
+
+  GetImplementation(modelPrimitive).AddPrimitiveObserver(this);
+  if(mDiffuseTexture && mSpecularTexture)
+  {
+    GetImplementation(modelPrimitive).SetImageBasedLightTexture(mDiffuseTexture, mSpecularTexture, mIblScaleFactor, mSpecularMipmapLevels);
+  }
+
+  Dali::Renderer renderer = GetImplementation(modelPrimitive).GetRenderer();
+  if(renderer)
+  {
+    uint32_t rendererCount = self.GetRendererCount();
+    bool     exist         = false;
+    for(uint32_t i = 0; i < rendererCount; ++i)
+    {
+      if(renderer == self.GetRendererAt(i))
+      {
+        exist = true;
+        break;
+      }
+    }
+    if(!exist)
+    {
+      self.AddRenderer(renderer);
+    }
+  }
+}
+
+void ModelNode::Impl::RemoveModelPrimitive(Scene3D::ModelPrimitive modelPrimitive)
+{
+  uint32_t primitiveCount = GetModelPrimitiveCount();
+  for(uint32_t i = 0; i < primitiveCount; ++i)
+  {
+    if(mModelPrimitiveContainer[i] != modelPrimitive)
+    {
+      continue;
+    }
+    RemoveModelPrimitive(i);
+    break;
+  }
+}
+
+void ModelNode::Impl::RemoveModelPrimitive(uint32_t index)
+{
+  if(index >= mModelPrimitiveContainer.size())
+  {
+    return;
+  }
+
+  Actor self = mModelNodeImpl.Self();
+  GetImplementation(mModelPrimitiveContainer[index]).RemovePrimitiveObserver(this);
+
+  Dali::Renderer renderer = GetImplementation(mModelPrimitiveContainer[index]).GetRenderer();
+  if(renderer)
+  {
+    self.RemoveRenderer(renderer);
+  }
+
+  mModelPrimitiveContainer.erase(mModelPrimitiveContainer.begin() + index);
+}
+
+Scene3D::ModelPrimitive ModelNode::Impl::GetModelPrimitive(uint32_t index) const
+{
+  if(index < mModelPrimitiveContainer.size())
+  {
+    return mModelPrimitiveContainer[index];
+  }
+  return Scene3D::ModelPrimitive();
+}
+
+void ModelNode::Impl::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
+{
+  mDiffuseTexture       = diffuseTexture;
+  mSpecularTexture      = specularTexture;
+  mIblScaleFactor       = iblScaleFactor;
+  mSpecularMipmapLevels = specularMipmapLevels;
+  for(auto&& primitive : mModelPrimitiveContainer)
+  {
+    GetImplementation(primitive).SetImageBasedLightTexture(diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
+  }
+}
+
+void ModelNode::Impl::SetImageBasedLightScaleFactor(float iblScaleFactor)
+{
+  mIblScaleFactor = iblScaleFactor;
+  for(auto&& primitive : mModelPrimitiveContainer)
+  {
+    GetImplementation(primitive).SetImageBasedLightScaleFactor(iblScaleFactor);
+  }
+}
+
+void ModelNode::Impl::SetBlendShapeData(Scene3D::Loader::BlendShapes::BlendShapeData& data, Scene3D::ModelPrimitive primitive)
+{
+  GetImplementation(primitive).SetBlendShapeData(data);
+}
+
+void ModelNode::Impl::SetBoneMatrix(const Matrix& inverseMatrix, Scene3D::ModelPrimitive primitive, Scene3D::Loader::Index& boneIndex)
+{
+  Dali::Scene3D::Loader::Skinning::BoneData boneData;
+  boneData.primitive = primitive;
+  boneData.boneIndex = boneIndex;
+  char propertyNameBuffer[32];
+  snprintf(propertyNameBuffer, sizeof(propertyNameBuffer), "%s[%d]", Dali::Scene3D::Loader::Skinning::BONE_UNIFORM_NAME, boneIndex);
+  boneData.propertyName  = propertyNameBuffer;
+  boneData.inverseMatrix = inverseMatrix;
+  mBoneDataContainer.push_back(std::move(boneData));
+
+  UpdateBoneMatrix(primitive);
+}
+
+void ModelNode::Impl::UpdateBoneMatrix(Scene3D::ModelPrimitive primitive)
+{
+  for(auto&& boneData : mBoneDataContainer)
+  {
+    if(boneData.primitive != primitive)
+    {
+      continue;
+    }
+
+    Dali::Renderer renderer = GetImplementation(primitive).GetRenderer();
+    if(!renderer)
+    {
+      continue;
+    }
+
+    Dali::Shader shader = renderer.GetShader();
+    if(!shader)
+    {
+      continue;
+    }
+
+    if(boneData.constraint)
+    {
+      boneData.constraint.Remove();
+      boneData.constraint.Reset();
+    }
+
+    if(shader.GetPropertyIndex(boneData.propertyName) == Property::INVALID_INDEX)
+    {
+      auto propBoneXform = shader.RegisterProperty(boneData.propertyName, Matrix{false});
+
+      Matrix inverseMatrix = boneData.inverseMatrix;
+      // Constrain bone matrix to joint transform.
+      boneData.constraint = Constraint::New<Matrix>(shader, propBoneXform, [inverseMatrix](Matrix& output, const PropertyInputContainer& inputs)
+                                                    { Matrix::Multiply(output, inverseMatrix, inputs[0]->GetMatrix()); });
+
+      Actor joint           = mModelNodeImpl.Self();
+      auto  propJointMatrix = joint.GetPropertyIndex(Dali::Scene3D::Loader::Skinning::JOINT_MATRIX);
+      boneData.constraint.AddSource(Source{joint, propJointMatrix});
+      boneData.constraint.Apply();
+    }
+    break;
+  }
+}
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
diff --git a/dali-scene3d/internal/model-components/model-node-data-impl.h b/dali-scene3d/internal/model-components/model-node-data-impl.h
new file mode 100644 (file)
index 0000000..b19a82f
--- /dev/null
@@ -0,0 +1,162 @@
+#ifndef DALI_SCENE3D_MODEL_COMPONENTS_MODEL_NODE_DATA_IMPL_H
+#define DALI_SCENE3D_MODEL_COMPONENTS_MODEL_NODE_DATA_IMPL_H
+
+/*
+ * Copyright (c) 2023 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/animation/constraints.h>
+#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/object/type-registry.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/model-components/model-node-impl.h>
+#include <dali-scene3d/internal/model-components/model-primitive-modify-observer.h>
+#include <dali-scene3d/public-api/loader/skinning-details.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+/**
+ * @brief Holds the Implementation for the internal model node class
+ */
+class ModelNode::Impl : public ModelPrimitiveModifyObserver
+{
+public:
+  using ModelPrimitiveContainer = std::vector<Scene3D::ModelPrimitive>;
+  using BoneDataContainer       = std::vector<Dali::Scene3D::Loader::Skinning::BoneData>;
+
+  /**
+   * @brief Constructor.
+   * @param[in] modelNodeImpl The control which owns this implementation
+   */
+  Impl(ModelNode& modelNodeImpl);
+
+  /**
+   * @brief Destructor.
+   */
+  ~Impl();
+
+public:
+  /**
+   * @copydoc Dali::Scene3D::Internal::ModelNode::OnSceneConnection()
+   */
+  void OnSceneConnection(int depth);
+
+  /**
+   * @copydoc Dali::Scene3D::Internal::ModelNode::OnSceneConnection()
+   */
+  void OnSceneDisconnection();
+
+public: // Public Method
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::GetModelPrimitiveCount()
+   */
+  inline uint32_t GetModelPrimitiveCount() const
+  {
+    return static_cast<uint32_t>(mModelPrimitiveContainer.size());
+  }
+
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::AddModelPrimitive()
+   */
+  void AddModelPrimitive(Scene3D::ModelPrimitive modelPrimitive);
+
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::RemoveModelPrimitive()
+   */
+  void RemoveModelPrimitive(Scene3D::ModelPrimitive modelPrimitive);
+
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::RemoveModelPrimitive()
+   */
+  void RemoveModelPrimitive(uint32_t index);
+
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::GetModelPrimitive()
+   */
+  Scene3D::ModelPrimitive GetModelPrimitive(uint32_t index) const;
+
+  /**
+   * @brief Sets the diffuse and specular image-based lighting textures for a ModelPrimitive.
+   *
+   * @param[in] diffuseTexture The diffuse texture.
+   * @param[in] specularTexture The specular texture.
+   * @param[in] iblScaleFactor The scale factor for the image-based lighting.
+   * @param[in] specularMipmapLevels The number of mipmap levels for the specular texture.
+   */
+  void SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels);
+
+  /**
+   * @brief Sets the scale factor for image-based lighting.
+   *
+   * @param[in] iblScaleFactor The scale factor for image-based lighting.
+   */
+  void SetImageBasedLightScaleFactor(float iblScaleFactor);
+
+  /**
+   * @brief Sets the blend shape data for a ModelPrimitive.
+   *
+   * @param[in] data The blend shape data.
+   * @param[in] primitive The ModelPrimitive to set the blend shape data for.
+   */
+  void SetBlendShapeData(Scene3D::Loader::BlendShapes::BlendShapeData& data, Scene3D::ModelPrimitive primitive);
+
+  /**
+   * @brief Sets the bone matrix for a ModelPrimitive and bone index.
+   *
+   * @param[in] inverseMatrix The inverse matrix of the bone.
+   * @param[in] primitive The ModelPrimitive to set the bone matrix for.
+   * @param[in] boneIndex The index of the bone to set the matrix for.
+   */
+  void SetBoneMatrix(const Matrix& inverseMatrix, Scene3D::ModelPrimitive primitive, Scene3D::Loader::Index& boneIndex);
+
+  /**
+   * @brief Called when a Renderer of ModelPrimitive is created.
+   *
+   * @param[in] renderer The Renderer that is created.
+   */
+  void OnRendererCreated(Renderer renderer) override;
+
+private:
+  /**
+   * @brief Updates the bone matrix for a ModelPrimitive.
+   *
+   * @param[in] primitive The ModelPrimitive to set the bone matrix for.
+   */
+  void UpdateBoneMatrix(Scene3D::ModelPrimitive primitive);
+
+private:
+  ModelNode&              mModelNodeImpl;           ///< Owner of this data
+  ModelPrimitiveContainer mModelPrimitiveContainer; ///< List of model primitives
+  BoneDataContainer       mBoneDataContainer;
+  Dali::Texture           mSpecularTexture;
+  Dali::Texture           mDiffuseTexture;
+  float                   mIblScaleFactor{1.0f};
+  uint32_t                mSpecularMipmapLevels{1u};
+};
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_MODEL_COMPONENTS_MODEL_NODE_DATA_IMPL_H
diff --git a/dali-scene3d/internal/model-components/model-node-impl.cpp b/dali-scene3d/internal/model-components/model-node-impl.cpp
new file mode 100644 (file)
index 0000000..60dca00
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2023 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/model-components/model-node-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/model-components/model-node-data-impl.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+Dali::Scene3D::ModelNode ModelNode::New()
+{
+  // Create the implementation, temporarily owned on stack
+  IntrusivePtr<ModelNode> nodeImpl = new ModelNode();
+
+  // Pass ownership to handle
+  Scene3D::ModelNode handle(*nodeImpl);
+
+  // Second-phase init of the implementation
+  // This can only be done after the CustomActor connection has been made...
+  nodeImpl->Initialize();
+
+  return handle;
+}
+
+ModelNode::ModelNode()
+: CustomActorImpl(ActorFlags::DISABLE_SIZE_NEGOTIATION),
+  mImpl(new Impl(*this))
+{
+}
+
+ModelNode::~ModelNode()
+{
+}
+
+void ModelNode::Initialize()
+{
+  OnInitialize();
+}
+
+void ModelNode::OnInitialize()
+{
+}
+
+void ModelNode::OnSceneConnection(int depth)
+{
+  mImpl->OnSceneConnection(depth);
+}
+
+void ModelNode::OnSceneDisconnection()
+{
+  mImpl->OnSceneDisconnection();
+}
+
+void ModelNode::OnChildAdd(Actor& child)
+{
+}
+
+void ModelNode::OnChildRemove(Actor& child)
+{
+}
+
+void ModelNode::OnPropertySet(Property::Index index, const Property::Value& propertyValue)
+{
+}
+
+void ModelNode::OnSizeSet(const Vector3& targetSize)
+{
+}
+
+void ModelNode::OnSizeAnimation(Animation& animation, const Vector3& targetSize)
+{
+  // @todo size negotiate background to new size, animate as well?
+}
+
+void ModelNode::OnRelayout(const Vector2& size, RelayoutContainer& container)
+{
+}
+
+void ModelNode::OnSetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension)
+{
+}
+
+Vector3 ModelNode::GetNaturalSize()
+{
+  return Vector3::ZERO;
+}
+
+float ModelNode::CalculateChildSize(const Dali::Actor& child, Dimension::Type dimension)
+{
+  return 0.0f;
+}
+
+float ModelNode::GetHeightForWidth(float width)
+{
+  return 0.0f;
+}
+
+float ModelNode::GetWidthForHeight(float height)
+{
+  return 0.0f;
+}
+
+bool ModelNode::RelayoutDependentOnChildren(Dimension::Type dimension)
+{
+  return false;
+}
+
+void ModelNode::OnCalculateRelayoutSize(Dimension::Type dimension)
+{
+}
+
+void ModelNode::OnLayoutNegotiated(float size, Dimension::Type dimension)
+{
+}
+
+ModelNode& GetImplementation(Dali::Scene3D::ModelNode& handle)
+{
+  CustomActorImpl& customInterface = handle.GetImplementation();
+  ModelNode& impl = dynamic_cast<Internal::ModelNode&>(customInterface);
+  return impl;
+}
+
+const ModelNode& GetImplementation(const Dali::Scene3D::ModelNode& handle)
+{
+  const CustomActorImpl& customInterface = handle.GetImplementation();
+  // downcast to control
+  const ModelNode& impl = dynamic_cast<const Internal::ModelNode&>(customInterface);
+  return impl;
+}
+
+// Public Method
+
+uint32_t ModelNode::GetModelPrimitiveCount() const
+{
+  return mImpl->GetModelPrimitiveCount();
+}
+
+void ModelNode::AddModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive)
+{
+  mImpl->AddModelPrimitive(modelPrimitive);
+}
+
+void ModelNode::RemoveModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive)
+{
+  mImpl->RemoveModelPrimitive(modelPrimitive);
+}
+
+void ModelNode::RemoveModelPrimitive(uint32_t index)
+{
+  mImpl->RemoveModelPrimitive(index);
+}
+
+Dali::Scene3D::ModelPrimitive ModelNode::GetModelPrimitive(uint32_t index) const
+{
+  return mImpl->GetModelPrimitive(index);
+}
+
+Scene3D::ModelNode ModelNode::FindChildModelNodeByName(std::string_view nodeName)
+{
+  Actor childActor = Self().FindChildByName(nodeName);
+  return Scene3D::ModelNode::DownCast(childActor);
+}
+
+void ModelNode::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
+{
+  mImpl->SetImageBasedLightTexture(diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
+}
+
+void ModelNode::SetImageBasedLightScaleFactor(float iblScaleFactor)
+{
+  mImpl->SetImageBasedLightScaleFactor(iblScaleFactor);
+}
+
+void ModelNode::SetBlendShapeData(Scene3D::Loader::BlendShapes::BlendShapeData& data, Scene3D::ModelPrimitive primitive)
+{
+  mImpl->SetBlendShapeData(data, primitive);
+}
+
+void ModelNode::SetBoneMatrix(const Matrix& inverseMatrix, Scene3D::ModelPrimitive primitive, Scene3D::Loader::Index& boneIndex)
+{
+  mImpl->SetBoneMatrix(inverseMatrix, primitive, boneIndex);
+}
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
diff --git a/dali-scene3d/internal/model-components/model-node-impl.h b/dali-scene3d/internal/model-components/model-node-impl.h
new file mode 100644 (file)
index 0000000..d216bb5
--- /dev/null
@@ -0,0 +1,293 @@
+#ifndef DALI_SCENE3D_MODEL_COMPONENTS_MODEL_NODE_IMPL_H
+#define DALI_SCENE3D_MODEL_COMPONENTS_MODEL_NODE_IMPL_H
+
+/*
+ * Copyright (c) 2023 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/actors/custom-actor-impl.h>
+#include <dali/public-api/common/dali-common.h>
+#include <memory> // for std::unique_ptr
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/loader/mesh-definition.h>
+#include <dali-scene3d/public-api/model-components/model-node.h>
+#include <dali-scene3d/public-api/model-components/model-primitive.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+/**
+ * @addtogroup dali_toolkit_controls_model
+ * @{
+ */
+
+namespace Internal
+{
+/**
+ * @brief This is the internal base class for custom node of model.
+ *
+ * @SINCE_2_2.99
+ */
+class DALI_SCENE3D_API ModelNode : public CustomActorImpl
+{
+public:
+  // Creation & Destruction
+  /**
+   * @brief Creates a new ModelNodeImpl instance that does not require touch by default.
+   *
+   * If touch is required, then the user can connect to this class' touch signal.
+   * @SINCE_2_2.99
+   * @return A handle to the ModelNode instance
+   */
+  static Scene3D::ModelNode New();
+
+protected:
+  /**
+   * @brief Virtual destructor.
+   * @SINCE_2_2.99
+   */
+  virtual ~ModelNode();
+
+protected: // From CustomActorImpl
+  /**
+   * @copydoc CustomActorImpl::OnSceneConnection()
+   * @note If overridden, then an up-call to ModelNode::OnSceneConnection MUST be made at the end.
+   */
+  void OnSceneConnection(int depth) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnSceneDisconnection()
+   * @note If overridden, then an up-call to ModelNode::OnSceneDisconnection MUST be made at the end.
+   */
+  void OnSceneDisconnection() override;
+
+  /**
+   * @copydoc CustomActorImpl::OnChildAdd()
+   * @note If overridden, then an up-call to ModelNode::OnChildAdd MUST be made at the end.
+   */
+  void OnChildAdd(Actor& child) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnChildRemove()
+   * @note If overridden, then an up-call to ModelNode::OnChildRemove MUST be made at the end.
+   */
+  void OnChildRemove(Actor& child) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnPropertySet()
+   * @note If overridden, then an up-call to ModelNode::OnChildRemove MUST be made at the end.
+   */
+  void OnPropertySet(Property::Index index, const Property::Value& propertyValue) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnSizeSet()
+   * @note If overridden, then an up-call to ModelNode::OnSizeSet MUST be made at the end.
+   */
+  void OnSizeSet(const Vector3& targetSize) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnSizeAnimation()
+   * @note If overridden, then an up-call to ModelNode::OnSizeAnimation MUST be made at the end.
+   */
+  void OnSizeAnimation(Animation& animation, const Vector3& targetSize) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnRelayout()
+   */
+  void OnRelayout(const Vector2& size, RelayoutContainer& container) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnSetResizePolicy()
+   */
+  void OnSetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension) override;
+
+  /**
+   * @copydoc CustomActorImpl::GetNaturalSize()
+   */
+  Vector3 GetNaturalSize() override;
+
+  /**
+   * @copydoc CustomActorImpl::CalculateChildSize()
+   */
+  float CalculateChildSize(const Dali::Actor& child, Dimension::Type dimension) override;
+
+  /**
+   * @copydoc CustomActorImpl::GetHeightForWidth()
+   */
+  float GetHeightForWidth(float width) override;
+
+  /**
+   * @copydoc CustomActorImpl::GetWidthForHeight()
+   */
+  float GetWidthForHeight(float height) override;
+
+  /**
+   * @copydoc CustomActorImpl::RelayoutDependentOnChildren()
+   */
+  bool RelayoutDependentOnChildren(Dimension::Type dimension = Dimension::ALL_DIMENSIONS) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnCalculateRelayoutSize()
+   */
+  void OnCalculateRelayoutSize(Dimension::Type dimension) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnLayoutNegotiated()
+   */
+  void OnLayoutNegotiated(float size, Dimension::Type dimension) override;
+
+protected:
+  // Construction
+
+  /**
+   * @brief ModelNode constructor.
+   *
+   * @SINCE_2_2.99
+   */
+  ModelNode();
+
+  /**
+   * @brief Second phase initialization.
+   * @SINCE_2_2.99
+   */
+  void Initialize();
+
+public: // API for derived classes to override
+  // Lifecycle
+
+  /**
+   * @brief This method is called after the Node has been initialized.
+   *
+   * Derived classes should do any second phase initialization by overriding this method.
+   * @SINCE_2_2.99
+   */
+  virtual void OnInitialize();
+
+public: // Public Method
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::GetModelPrimitiveCount()
+   */
+  uint32_t GetModelPrimitiveCount() const;
+
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::AddModelPrimitive()
+   */
+  void AddModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive);
+
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::RemoveModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive)
+   */
+  void RemoveModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive);
+
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::RemoveModelPrimitive(uint32_t index)
+   */
+  void RemoveModelPrimitive(uint32_t index);
+
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::GetModelPrimitive()
+   */
+  Dali::Scene3D::ModelPrimitive GetModelPrimitive(uint32_t index) const;
+
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::FindChildModelNodeByName()
+   */
+  Scene3D::ModelNode FindChildModelNodeByName(std::string_view nodeName);
+
+  /**
+   * @brief Sets the diffuse and specular image-based lighting textures for a ModelPrimitive.
+   *
+   * @param[in] diffuseTexture The diffuse texture.
+   * @param[in] specularTexture The specular texture.
+   * @param[in] iblScaleFactor The scale factor for the image-based lighting.
+   * @param[in] specularMipmapLevels The number of mipmap levels for the specular texture.
+   */
+  void SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels);
+
+  /**
+   * @brief Sets the scale factor for image-based lighting.
+   *
+   * @param[in] iblScaleFactor The scale factor for image-based lighting.
+   */
+  void SetImageBasedLightScaleFactor(float iblScaleFactor);
+
+  /**
+   * @brief Sets the blend shape data for a ModelPrimitive.
+   *
+   * @param[in] data The blend shape data.
+   * @param[in] primitive The ModelPrimitive to set the blend shape data for.
+   */
+  void SetBlendShapeData(Scene3D::Loader::BlendShapes::BlendShapeData& data, Scene3D::ModelPrimitive primitive);
+
+  /**
+   * @brief Sets the bone matrix for a ModelPrimitive and bone index.
+   *
+   * @param[in] inverseMatrix The inverse matrix of the bone.
+   * @param[in] primitive The ModelPrimitive to set the bone matrix for.
+   * @param[in] boneIndex The index of the bone to set the matrix for.
+   */
+  void SetBoneMatrix(const Matrix& inverseMatrix, Scene3D::ModelPrimitive primitive, Scene3D::Loader::Index& boneIndex);
+
+private:
+  /// @cond internal
+
+  // Not copyable or movable
+  DALI_INTERNAL            ModelNode(const ModelNode&) = delete; ///< Deleted copy constructor.
+  DALI_INTERNAL            ModelNode(ModelNode&&)      = delete; ///< Deleted move constructor.
+  DALI_INTERNAL ModelNode& operator=(const ModelNode&) = delete; ///< Deleted copy assignment operator.
+  DALI_INTERNAL ModelNode& operator=(ModelNode&&)      = delete; ///< Deleted move assignment operator.
+
+public:
+  class DALI_INTERNAL Impl; // Class declaration is public so we can internally add devel API's to the ModelNode's Impl
+
+private:
+  const std::unique_ptr<Impl> mImpl;
+  /// @endcond
+};
+
+/**
+ * @brief Gets implementation from the handle.
+ *
+ * @SINCE_2_2.99
+ * @param handle
+ * @return Implementation
+ * @pre handle is initialized and points to a node
+ */
+DALI_SCENE3D_API Internal::ModelNode& GetImplementation(Dali::Scene3D::ModelNode& handle);
+
+/**
+ * @brief Gets implementation from the handle.
+ *
+ * @SINCE_2_2.99
+ * @param handle
+ * @return Implementation
+ * @pre Handle is initialized and points to a node.
+ */
+DALI_SCENE3D_API const Internal::ModelNode& GetImplementation(const Dali::Scene3D::ModelNode& handle);
+
+} // namespace Internal
+
+/**
+ * @}
+ */
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_MODEL_COMPONENTS_MODEL_NODE_IMPL_H
diff --git a/dali-scene3d/internal/model-components/model-primitive-impl.cpp b/dali-scene3d/internal/model-components/model-primitive-impl.cpp
new file mode 100644 (file)
index 0000000..4dbb047
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2023 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/model-components/model-primitive-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/type-registry-helper.h>
+#include <dali/public-api/object/type-registry.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/model-components/material-impl.h>
+#include <dali-scene3d/public-api/loader/environment-definition.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+namespace
+{
+/**
+ * Creates control through type registry
+ */
+BaseHandle Create()
+{
+  return Scene3D::ModelPrimitive::New();
+}
+
+// Setup properties, signals and actions using the type-registry.
+DALI_TYPE_REGISTRATION_BEGIN(Scene3D::ModelPrimitive, Dali::BaseHandle, Create);
+DALI_TYPE_REGISTRATION_END()
+
+constexpr std::string_view MORPH_KEYWORD             = "MORPH";
+constexpr std::string_view MORPH_POSITION_KEYWORD    = "MORPH_POSITION";
+constexpr std::string_view MORPH_NORMAL_KEYWORD      = "MORPH_NORMAL";
+constexpr std::string_view MORPH_TANGENT_KEYWORD     = "MORPH_TANGENT";
+constexpr std::string_view MORPH_VERSION_2_0_KEYWORD = "MORPH_VERSION_2_0";
+} // unnamed namespace
+
+ModelPrimitivePtr ModelPrimitive::New()
+{
+  ModelPrimitivePtr primitive = new ModelPrimitive();
+
+  primitive->Initialize();
+
+  return primitive;
+}
+
+ModelPrimitive::ModelPrimitive()
+{
+}
+
+ModelPrimitive::~ModelPrimitive()
+{
+  if(mMaterial)
+  {
+    GetImplementation(mMaterial).RemoveObserver(this);
+  }
+  mMaterial.Reset();
+}
+
+void ModelPrimitive::Initialize()
+{
+}
+
+void ModelPrimitive::SetRenderer(Dali::Renderer renderer)
+{
+  mRenderer   = renderer;
+  mGeometry   = renderer.GetGeometry();
+  mTextureSet = renderer.GetTextures();
+  mShader     = renderer.GetShader();
+}
+
+Dali::Renderer ModelPrimitive::GetRenderer() const
+{
+  return mRenderer;
+}
+
+void ModelPrimitive::SetGeometry(Dali::Geometry geometry)
+{
+  mGeometry = geometry;
+  CreateRenderer();
+}
+
+Dali::Geometry ModelPrimitive::GetGeometry() const
+{
+  return mGeometry;
+}
+
+void ModelPrimitive::SetMaterial(Dali::Scene3D::Material material, bool updateRenderer)
+{
+  if(!material)
+  {
+    return;
+  }
+
+  if(mMaterial != material)
+  {
+    // Stop observe from previous material.
+    if(mMaterial)
+    {
+      GetImplementation(mMaterial).RemoveObserver(this);
+    }
+
+    mMaterial = material;
+
+    // Start observe from new material.
+
+    if(mMaterial)
+    {
+      GetImplementation(mMaterial).AddObserver(this);
+    }
+
+    if(updateRenderer)
+    {
+      mIsMaterialChanged = true;
+      if(GetImplementation(mMaterial).IsResourceReady())
+      {
+        GetImplementation(mMaterial).UpdateMaterialData();
+        ApplyMaterialToRenderer();
+      }
+    }
+    UpdateImageBasedLightTexture();
+  }
+}
+
+Dali::Scene3D::Material ModelPrimitive::GetMaterial() const
+{
+  return mMaterial;
+}
+
+void ModelPrimitive::AddPrimitiveObserver(ModelPrimitiveModifyObserver* observer)
+{
+  mObservers.insert(observer);
+}
+
+void ModelPrimitive::RemovePrimitiveObserver(ModelPrimitiveModifyObserver* observer)
+{
+  mObservers.erase(observer);
+}
+
+void ModelPrimitive::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
+{
+  mDiffuseTexture       = diffuseTexture;
+  mSpecularTexture      = specularTexture;
+  mIblScaleFactor       = iblScaleFactor;
+  mSpecularMipmapLevels = specularMipmapLevels;
+
+  UpdateImageBasedLightTexture();
+}
+
+void ModelPrimitive::SetImageBasedLightScaleFactor(float iblScaleFactor)
+{
+  mIblScaleFactor = iblScaleFactor;
+  if(mRenderer && mMaterial)
+  {
+    mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), iblScaleFactor);
+  }
+}
+
+void ModelPrimitive::SetBlendShapeData(Scene3D::Loader::BlendShapes::BlendShapeData& data)
+{
+  mBlendShapeData = std::move(data);
+  Scene3D::Loader::BlendShapes::ConfigureProperties(mBlendShapeData, mShader);
+}
+
+void ModelPrimitive::SetBlendShapeGeometry(Dali::Texture blendShapeGeometry)
+{
+  mBlendShapeGeometry = blendShapeGeometry;
+}
+
+void ModelPrimitive::SetBlendShapeOptions(bool hasPositions, bool hasNormals, bool hasTangents)
+{
+  mHasPositions = hasPositions;
+  mHasNormals   = hasNormals;
+  mHasTangents  = hasTangents;
+}
+
+void ModelPrimitive::SetSkinned(bool isSkinned)
+{
+  mHasSkinning = isSkinned;
+}
+
+// From MaterialModifyObserver
+
+void ModelPrimitive::OnMaterialModified(Dali::Scene3D::Material material, MaterialModifyObserver::ModifyFlag flag)
+{
+  ApplyMaterialToRenderer(flag);
+}
+
+void ModelPrimitive::ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag flag)
+{
+  uint32_t shaderFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::SHADER));
+  if(mIsMaterialChanged || shaderFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::SHADER))
+  {
+    std::string vertexShader   = GetImplementation(mMaterial).GetVertexShader();
+    std::string fragmentShader = GetImplementation(mMaterial).GetFragmentShader();
+
+    std::vector<std::string> defines;
+    defines.push_back("VEC4_TANGENT");
+    if(mHasSkinning)
+    {
+      defines.push_back("SKINNING");
+    }
+    if(mHasPositions || mHasNormals || mHasTangents)
+    {
+      if(mHasPositions)
+      {
+        defines.push_back(MORPH_POSITION_KEYWORD.data());
+      }
+      if(mHasNormals)
+      {
+        defines.push_back(MORPH_NORMAL_KEYWORD.data());
+      }
+      if(mHasTangents)
+      {
+        defines.push_back(MORPH_TANGENT_KEYWORD.data());
+      }
+      defines.push_back(MORPH_KEYWORD.data());
+      if(mBlendShapeData.version == Scene3D::Loader::BlendShapes::Version::VERSION_2_0)
+      {
+        defines.push_back(MORPH_VERSION_2_0_KEYWORD.data());
+      }
+    }
+    for(const auto& define : defines)
+    {
+      Scene3D::Loader::ShaderDefinition::ApplyDefine(vertexShader, define);
+    }
+
+    mShader.Reset();
+    mShader = Shader::New(vertexShader, fragmentShader);
+    if(mBlendShapeData.version != Scene3D::Loader::BlendShapes::Version::INVALID && mBlendShapeData.mActor.GetHandle())
+    {
+      Scene3D::Loader::BlendShapes::ConfigureProperties(mBlendShapeData, mShader);
+    }
+
+    if(!mRenderer)
+    {
+      CreateRenderer();
+    }
+    else
+    {
+      mRenderer.SetShader(mShader);
+    }
+  }
+
+  uint32_t textureFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::TEXTURE));
+  if(mIsMaterialChanged || textureFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::TEXTURE))
+  {
+    mTextureSet = GetImplementation(mMaterial).GetTextureSet();
+
+    if(mBlendShapeGeometry)
+    {
+      TextureSet newTextureSet = TextureSet::New();
+      newTextureSet.SetTexture(0u, mBlendShapeGeometry);
+
+      const unsigned int numberOfTextures = mTextureSet.GetTextureCount();
+      for(unsigned int index = 0u; index < numberOfTextures; ++index)
+      {
+        const unsigned int newIndex = index + 1u;
+        newTextureSet.SetTexture(newIndex, mTextureSet.GetTexture(index));
+        newTextureSet.SetSampler(newIndex, mTextureSet.GetSampler(index));
+      }
+
+      mTextureSet = newTextureSet;
+    }
+
+    uint32_t textureCount = mTextureSet.GetTextureCount();
+    Texture  brdfTexture  = Scene3D::Loader::EnvironmentDefinition::GetBrdfTexture();
+    if(!mSpecularTexture || !mDiffuseTexture)
+    {
+      Scene3D::Loader::EnvironmentMapData environmentMapData;
+      environmentMapData.mPixelData.resize(6);
+      for(auto& face : environmentMapData.mPixelData)
+      {
+        face.push_back(PixelData::New(new uint8_t[3]{0xff, 0xff, 0xff}, 3, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY));
+      }
+      environmentMapData.SetEnvironmentMapType(Dali::Scene3D::EnvironmentMapType::CUBEMAP);
+      Texture iblTexture = environmentMapData.GetTexture();
+      mDiffuseTexture    = iblTexture;
+      mSpecularTexture   = iblTexture;
+    }
+
+    mTextureSet.SetTexture(textureCount++, brdfTexture);
+    mTextureSet.SetTexture(textureCount++, mDiffuseTexture);
+    mTextureSet.SetTexture(textureCount, mSpecularTexture);
+
+    auto specularSampler = Sampler::New();
+    specularSampler.SetWrapMode(WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
+    specularSampler.SetFilterMode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR);
+    mTextureSet.SetSampler(textureCount, specularSampler);
+
+    if(!mRenderer)
+    {
+      CreateRenderer();
+    }
+    else
+    {
+      mRenderer.SetTextures(mTextureSet);
+    }
+  }
+
+  uint32_t uniformFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::UNIFORM));
+  if(mIsMaterialChanged || uniformFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::UNIFORM))
+  {
+    if(!mRenderer)
+    {
+      mNeedToSetRendererUniform = true;
+    }
+    else
+    {
+      UpdateRendererUniform();
+    }
+  }
+  mIsMaterialChanged = false;
+}
+
+void ModelPrimitive::CreateRenderer()
+{
+  if(!mShader || !mGeometry || !mTextureSet || mRenderer)
+  {
+    return;
+  }
+
+  mRenderer = Renderer::New(mGeometry, mShader);
+  mRenderer.SetTextures(mTextureSet);
+  UpdateRendererUniform();
+  for(auto* observer : mObservers)
+  {
+    observer->OnRendererCreated(mRenderer);
+  }
+}
+
+void ModelPrimitive::UpdateImageBasedLightTexture()
+{
+  if(mRenderer && mMaterial)
+  {
+    Dali::TextureSet textures = mRenderer.GetTextures();
+    if(!textures)
+    {
+      return;
+    }
+
+    uint32_t textureCount = textures.GetTextureCount();
+    if(textureCount > 2u &&
+       (textures.GetTexture(textureCount - GetImplementation(mMaterial).GetDiffuseImageBasedLightTextureOffset()) != mDiffuseTexture ||
+        textures.GetTexture(textureCount - GetImplementation(mMaterial).GetSpecularImageBasedLightTextureOffset()) != mSpecularTexture))
+    {
+      Dali::TextureSet newTextures = Dali::TextureSet::New();
+
+      for(uint32_t index = 0u; index < textureCount; ++index)
+      {
+        Dali::Texture texture = textures.GetTexture(index);
+        if(index == textureCount - GetImplementation(mMaterial).GetDiffuseImageBasedLightTextureOffset())
+        {
+          texture = mDiffuseTexture;
+        }
+        else if(index == textureCount - GetImplementation(mMaterial).GetSpecularImageBasedLightTextureOffset())
+        {
+          texture = mSpecularTexture;
+        }
+
+        newTextures.SetTexture(index, texture);
+        newTextures.SetSampler(index, textures.GetSampler(index));
+      }
+
+      mRenderer.SetTextures(newTextures);
+    }
+    mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName() .data(), mIblScaleFactor);
+    mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightMaxLodUniformName().data(), static_cast<float>(mSpecularMipmapLevels));
+  }
+}
+
+void ModelPrimitive::UpdateRendererUniform()
+{
+  mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), mIblScaleFactor);
+  mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightMaxLodUniformName().data(), static_cast<float>(mSpecularMipmapLevels));
+  GetImplementation(mMaterial).SetRendererUniform(mRenderer);
+}
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
diff --git a/dali-scene3d/internal/model-components/model-primitive-impl.h b/dali-scene3d/internal/model-components/model-primitive-impl.h
new file mode 100644 (file)
index 0000000..b7d26b4
--- /dev/null
@@ -0,0 +1,259 @@
+#ifndef DALI_SCENE3D_MODEL_COMPONENTS_MODEL_PRIMITIVE_IMPL_H
+#define DALI_SCENE3D_MODEL_COMPONENTS_MODEL_PRIMITIVE_IMPL_H
+
+/*
+ * Copyright (c) 2023 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/dali-common.h>
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/object/base-object.h>
+#include <dali/public-api/object/property-value.h>
+#include <dali/public-api/object/property.h>
+#include <set>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/model-components/material-modify-observer.h>
+#include <dali-scene3d/internal/model-components/model-primitive-modify-observer.h>
+#include <dali-scene3d/public-api/loader/blend-shape-details.h>
+#include <dali-scene3d/public-api/loader/mesh-definition.h>
+#include <dali-scene3d/public-api/model-components/material.h>
+#include <dali-scene3d/public-api/model-components/model-primitive.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+using ModelPrimitivePtr = IntrusivePtr<ModelPrimitive>;
+
+/**
+ * @brief TODO : Explain me.
+ * Use Vector4 Tangent data
+ * Same ModelPrimitive all shares IBL
+ */
+class ModelPrimitive : public BaseObject, public MaterialModifyObserver
+{
+private:
+  using ModelPrimitiveModifyObserverContainer = std::set<ModelPrimitiveModifyObserver*>;
+
+public:
+  // Creation & Destruction
+  /**
+   * @brief Create a new ModelPrimitive object.
+   * @return A smart-pointer to the newly allocated ModelPrimitive.
+   */
+  static ModelPrimitivePtr New();
+
+protected:
+  /**
+   * @brief Construct a new ModelPrimitive.
+   */
+  ModelPrimitive();
+
+  /**
+   * @brief Second-phase constructor.
+   */
+  void Initialize();
+
+  /**
+   * @brief Virtual destructor.
+   */
+  virtual ~ModelPrimitive();
+
+public:
+  /**
+   * @brief Set Renderer that is created by Scene3D::Loader internally.
+   */
+  void SetRenderer(Dali::Renderer renderer);
+
+  /**
+   * @copydoc Dali::Scene3D::ModelPrimitive::GetRenderer()
+   */
+  Dali::Renderer GetRenderer() const;
+
+  /**
+   * @copydoc Dali::Scene3D::ModelPrimitive::SetGeometry()
+   */
+  void SetGeometry(Dali::Geometry geometry);
+
+  /**
+   * @copydoc Dali::Scene3D::ModelPrimitive::GetGeometry()
+   */
+  Dali::Geometry GetGeometry() const;
+
+  /**
+   * @copydoc Dali::Scene3D::ModelPrimitive::SetMaterial()
+   */
+  void SetMaterial(Dali::Scene3D::Material material, bool updateRenderer = true);
+
+  /**
+   * @copydoc Dali::Scene3D::ModelPrimitive::GetMaterial()
+   */
+  Dali::Scene3D::Material GetMaterial() const;
+
+  /**
+   * @brief Adds a primitive observer to this model primitive.
+   *
+   * @param[in] observer The observer to add.
+   */
+  void AddPrimitiveObserver(ModelPrimitiveModifyObserver* observer);
+
+  /**
+   * @brief Removes a primitive observer from this model primitive.
+   *
+   * @param[in] observer The observer to remove.
+   */
+  void RemovePrimitiveObserver(ModelPrimitiveModifyObserver* observer);
+
+  /**
+   * @brief Sets the image-based lighting texture for this model primitive.
+   *
+   * @param[in] diffuseTexture The diffuse texture.
+   * @param[in] specularTexture The specular texture.
+   * @param[in] iblScaleFactor The scale factor to set for image-based lighting.
+   * @param[in] specularMipmapLevels The number of mipmap levels of specular texture.
+   */
+  void SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels);
+
+  /**
+   * @brief Sets the scale factor for image-based lighting for this model primitive.
+   *
+   * @param[in] iblScaleFactor The scale factor to set for image-based lighting.
+   */
+  void SetImageBasedLightScaleFactor(float iblScaleFactor);
+
+  /**
+   * @brief Sets the blend shape data for this model primitive.
+   *
+   * @param[in] data The blend shape data to set.
+   */
+  void SetBlendShapeData(Scene3D::Loader::BlendShapes::BlendShapeData& data);
+
+  /**
+   * @brief Sets the blend shape geometry for this model primitive.
+   *
+   * @param[in] blendShapeGeometry The blend shape geometry to set.
+   */
+  void SetBlendShapeGeometry(Dali::Texture blendShapeGeometry);
+
+  /**
+   * @brief Sets the blend shape options for this model primitive.
+   *
+   * @param[in] hasPositions Whether or not this model primitive has positions for blend shapes.
+   * @param[in] hasNormals Whether or not this model primitive has normals for blend shapes.
+   * @param[in] hasTangents Whether or not this model primitive has tangents for blend shapes.
+   */
+  void SetBlendShapeOptions(bool hasPositions, bool hasNormals, bool hasTangents);
+
+  /**
+   * @brief Sets whether or not this model primitive is skinned.
+   *
+   * @param[in] isSkinned Whether or not this model primitive is skinned.
+   */
+  void SetSkinned(bool isSkinned);
+
+private: // From MaterialModifyObserver
+  /**
+   * @copydoc Dali::Scene3D::Internal::Material::MaterialModifyObserver::OnMaterialModified()
+   */
+  void OnMaterialModified(Dali::Scene3D::Material material, MaterialModifyObserver::ModifyFlag flag) override;
+
+private:
+  /**
+   * @brief Apply materials data into renderer.
+   */
+  void ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag flag = MaterialModifyObserver::ModifyFlag::NONE);
+
+  /**
+   * @brief Updates the uniform of renderer.
+   */
+  void UpdateRendererUniform();
+
+  /**
+   * @brief Creates a renderer.
+   */
+  void CreateRenderer();
+
+  /**
+   * @brief Updates the image-based lighting texture.
+   */
+  void UpdateImageBasedLightTexture();
+
+private:
+  // Delete copy & move operator
+  ModelPrimitive(const ModelPrimitive&)                = delete;
+  ModelPrimitive(ModelPrimitive&&)                     = delete;
+  ModelPrimitive& operator=(const ModelPrimitive& rhs) = delete;
+  ModelPrimitive& operator=(ModelPrimitive&& rhs) noexcept = delete;
+
+private:
+  ModelPrimitiveModifyObserverContainer mObservers{};
+
+  // For Renderer
+  Dali::Renderer          mRenderer;
+  Dali::Geometry          mGeometry;
+  Dali::Shader            mShader;
+  Dali::TextureSet        mTextureSet;
+  Dali::Scene3D::Material mMaterial;
+
+  // For IBL
+  Dali::Texture mSpecularTexture;
+  Dali::Texture mDiffuseTexture;
+  float         mIblScaleFactor{1.0f};
+  uint32_t      mSpecularMipmapLevels{1u};
+
+  // For blend shape
+  Scene3D::Loader::BlendShapes::BlendShapeData mBlendShapeData;
+  Dali::Texture                                mBlendShapeGeometry;
+  bool                                         mHasSkinning  = false;
+  bool                                         mHasPositions = false;
+  bool                                         mHasNormals   = false;
+  bool                                         mHasTangents  = false;
+
+  bool mIsMaterialChanged        = false;
+  bool mNeedToSetRendererUniform = false;
+};
+
+} // namespace Internal
+
+// Helpers for public-api forwarding methods
+
+inline Internal::ModelPrimitive& GetImplementation(Dali::Scene3D::ModelPrimitive& modelPrimitive)
+{
+  DALI_ASSERT_ALWAYS(modelPrimitive && "ModelPrimitive handle is empty");
+
+  BaseObject& handle = modelPrimitive.GetBaseObject();
+
+  return static_cast<Internal::ModelPrimitive&>(handle);
+}
+
+inline const Internal::ModelPrimitive& GetImplementation(const Dali::Scene3D::ModelPrimitive& modelPrimitive)
+{
+  DALI_ASSERT_ALWAYS(modelPrimitive && "ModelPrimitive handle is empty");
+
+  const BaseObject& handle = modelPrimitive.GetBaseObject();
+
+  return static_cast<const Internal::ModelPrimitive&>(handle);
+}
+
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_MODEL_COMPONENTS_MODEL_PRIMITIVE_IMPL_H
diff --git a/dali-scene3d/internal/model-components/model-primitive-modify-observer.h b/dali-scene3d/internal/model-components/model-primitive-modify-observer.h
new file mode 100644 (file)
index 0000000..a576792
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef DALI_SCENE3D_MODEL_COMPONENTS_MODEL_PRIMITIVE_MODIFY_OBSERVER_H
+#define DALI_SCENE3D_MODEL_COMPONENTS_MODEL_PRIMITIVE_MODIFY_OBSERVER_H
+
+/*
+ * Copyright (c) 2023 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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/model-components/model-primitive.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+/**
+ * @brief Observer class that notifies ModelNode when a Renderer of ModelPrimitive is created.
+ */
+class ModelPrimitiveModifyObserver
+{
+public:
+  /**
+   * @brief Called when a Renderer of ModelPrimitive is created.
+   *
+   * @param[in] renderer The Renderer that is created.
+   */
+  virtual void OnRendererCreated(Renderer renderer) = 0;
+};
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_MODEL_COMPONENTS_MODEL_PRIMITIVE_MODIFY_OBSERVER_H
index 6f01a44..1b297fb 100644 (file)
@@ -20,6 +20,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/controls/model/model-impl.h>
+#include <dali-scene3d/public-api/model-components/model-node.h>
 
 namespace Dali
 {
@@ -62,11 +63,21 @@ Model::Model(Dali::Internal::CustomActor* internal)
   VerifyCustomActorPointer<Internal::Model>(internal);
 }
 
-const Actor Model::GetModelRoot() const
+const ModelNode Model::GetModelRoot() const
 {
   return GetImpl(*this).GetModelRoot();
 }
 
+void Model::AddModelNode(ModelNode modelNode)
+{
+  return GetImpl(*this).AddModelNode(modelNode);
+}
+
+void Model::RemoveModelNode(ModelNode modelNode)
+{
+  return GetImpl(*this).RemoveModelNode(modelNode);
+}
+
 void Model::SetChildrenSensitive(bool enable)
 {
   GetImpl(*this).SetChildrenSensitive(enable);
@@ -132,6 +143,11 @@ bool Model::ApplyCamera(uint32_t index, Dali::CameraActor camera) const
   return GetImpl(*this).ApplyCamera(index, camera);
 }
 
+ModelNode Model::FindChildModelNodeByName(std::string_view nodeName)
+{
+  return GetImpl(*this).FindChildModelNodeByName(nodeName);
+}
+
 } // namespace Scene3D
 
 } // namespace Dali
index 39e7723..9b060d9 100644 (file)
@@ -26,6 +26,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/public-api/api.h>
+#include <dali-scene3d/public-api/model-components/model-node.h>
 
 namespace Dali
 {
@@ -75,10 +76,11 @@ public:
    * @SINCE_2_1.41
    * @param[in] modelUrl model file path.(e.g., glTF, and DLI).
    * @param[in] resourceDirectoryUrl resource file path that includes binary, image etc.
+   * @note If modelUrl is empty, it will not load resouces. Only ModelRoot will be created.
    * @note If resourceDirectoryUrl is empty, the parent directory path of modelUrl is used for resource path.
    * @return A handle to a newly allocated Dali resource
    */
-  static Model New(const std::string& modelUrl, const std::string& resourceDirectoryUrl = std::string());
+  static Model New(const std::string& modelUrl = std::string(), const std::string& resourceDirectoryUrl = std::string());
 
   /**
    * @brief Creates an uninitialized Model.
@@ -146,12 +148,29 @@ public:
   static Model DownCast(BaseHandle handle);
 
   /**
-   * @brief Retrieves model root Actor.
+   * @brief Retrieves model root Node.
    *
    * @SINCE_2_1.41
-   * @return Root Actor of the model.
+   * @return Root Node of the model.
    */
-  const Actor GetModelRoot() const;
+  const ModelNode GetModelRoot() const;
+
+  /**
+   * @brief Add new ModelNode to this Model.
+   * This modelNode will become child of ModelRoot.
+   *
+   * @SINCE_2_2.99
+   * @param[in] modelNode the root of ModelNode tree to be added.
+   */
+  void AddModelNode(ModelNode modelNode);
+
+  /**
+   * @brief Remove ModelNode from this Model.
+   *
+   * @SINCE_2_2.99
+   * @param[in] modelNode the root of ModelNode tree to be removed.
+   */
+  void RemoveModelNode(ModelNode modelNode);
 
   /**
    * @brief Whether allow this model's children actor to use events.
@@ -296,6 +315,14 @@ public:
    */
   bool ApplyCamera(uint32_t index, Dali::CameraActor camera) const;
 
+  /**
+   * @brief Returns a child ModelNode object with a name that matches nodeName.
+   *
+   * @param[in] nodeName The name of the child ModelNode object you want to find.
+   * @return Returns a child ModelNode object with a name that matches nodeName. If there is no corresponding child ModelNode object, it returns an empty ModelNode object.
+   */
+  ModelNode FindChildModelNodeByName(std::string_view nodeName);
+
 public: // Not intended for application developers
   /// @cond internal
   /**
index dffdfc8..a15ea78 100644 (file)
@@ -32,4 +32,7 @@ set(scene3d_src_files ${scene3d_src_files}
        ${scene3d_public_api_dir}/loader/string-callback.cpp
        ${scene3d_public_api_dir}/loader/utils.cpp
        ${scene3d_public_api_dir}/loader/view-projection.cpp
+       ${scene3d_public_api_dir}/model-components/material.cpp
+       ${scene3d_public_api_dir}/model-components/model-node.cpp
+       ${scene3d_public_api_dir}/model-components/model-primitive.cpp
 )
\ No newline at end of file
index 2aecc50..9c82704 100644 (file)
@@ -29,22 +29,26 @@ namespace
 #define DALI_ALPHA_FUNCTION_ENTRY(x) { #x, AlphaFunction::x }
 // clang-format on
 
-std::unordered_map<std::string, AlphaFunction> sFunctions{
-  DALI_ALPHA_FUNCTION_ENTRY(DEFAULT),
-  DALI_ALPHA_FUNCTION_ENTRY(LINEAR),
-  DALI_ALPHA_FUNCTION_ENTRY(REVERSE),
-  DALI_ALPHA_FUNCTION_ENTRY(EASE_IN_SQUARE),
-  DALI_ALPHA_FUNCTION_ENTRY(EASE_OUT_SQUARE),
-  DALI_ALPHA_FUNCTION_ENTRY(EASE_IN),
-  DALI_ALPHA_FUNCTION_ENTRY(EASE_OUT),
-  DALI_ALPHA_FUNCTION_ENTRY(EASE_IN_OUT),
-  DALI_ALPHA_FUNCTION_ENTRY(EASE_IN_SINE),
-  DALI_ALPHA_FUNCTION_ENTRY(EASE_OUT_SINE),
-  DALI_ALPHA_FUNCTION_ENTRY(EASE_IN_OUT_SINE),
-  DALI_ALPHA_FUNCTION_ENTRY(BOUNCE),
-  DALI_ALPHA_FUNCTION_ENTRY(SIN),
-  DALI_ALPHA_FUNCTION_ENTRY(EASE_OUT_BACK),
-};
+std::unordered_map<std::string, AlphaFunction>& GetFunctions()
+{
+  static std::unordered_map<std::string, AlphaFunction> sFunctions{
+    DALI_ALPHA_FUNCTION_ENTRY(DEFAULT),
+    DALI_ALPHA_FUNCTION_ENTRY(LINEAR),
+    DALI_ALPHA_FUNCTION_ENTRY(REVERSE),
+    DALI_ALPHA_FUNCTION_ENTRY(EASE_IN_SQUARE),
+    DALI_ALPHA_FUNCTION_ENTRY(EASE_OUT_SQUARE),
+    DALI_ALPHA_FUNCTION_ENTRY(EASE_IN),
+    DALI_ALPHA_FUNCTION_ENTRY(EASE_OUT),
+    DALI_ALPHA_FUNCTION_ENTRY(EASE_IN_OUT),
+    DALI_ALPHA_FUNCTION_ENTRY(EASE_IN_SINE),
+    DALI_ALPHA_FUNCTION_ENTRY(EASE_OUT_SINE),
+    DALI_ALPHA_FUNCTION_ENTRY(EASE_IN_OUT_SINE),
+    DALI_ALPHA_FUNCTION_ENTRY(BOUNCE),
+    DALI_ALPHA_FUNCTION_ENTRY(SIN),
+    DALI_ALPHA_FUNCTION_ENTRY(EASE_OUT_BACK),
+  };
+  return sFunctions;
+}
 
 #undef DALI_ALPHA_FUNCTION_ENTRY
 
@@ -52,8 +56,8 @@ std::unordered_map<std::string, AlphaFunction> sFunctions{
 
 AlphaFunction GetAlphaFunction(const std::string& name, bool* found)
 {
-  auto iFind   = sFunctions.find(name);
-  bool success = iFind != sFunctions.end();
+  auto iFind   = GetFunctions().find(name);
+  bool success = iFind != GetFunctions().end();
   if(found)
   {
     *found = success;
@@ -63,7 +67,7 @@ AlphaFunction GetAlphaFunction(const std::string& name, bool* found)
 
 void RegisterAlphaFunction(const std::string& name, AlphaFunction alphaFn)
 {
-  DALI_ASSERT_ALWAYS(sFunctions.insert({name, alphaFn}).second &&
+  DALI_ASSERT_ALWAYS(GetFunctions().insert({name, alphaFn}).second &&
                      "Function with given key already exists.");
 }
 
index 31c5ed1..5d8d304 100644 (file)
@@ -45,6 +45,20 @@ AnimationDefinition::AnimationDefinition(AnimationDefinition&& other)
 {
 }
 
+AnimationDefinition& AnimationDefinition::operator=(AnimationDefinition&& other)
+{
+  AnimationDefinition tmp(std::move(other));
+  mName             = std::move(tmp.mName);
+  mDuration         = tmp.mDuration;
+  mLoopCount        = tmp.mLoopCount;
+  mDisconnectAction = tmp.mDisconnectAction;
+  mEndAction        = tmp.mEndAction;
+  mSpeedFactor      = tmp.mSpeedFactor;
+  mPlayRange        = tmp.mPlayRange;
+  mProperties.swap(tmp.mProperties);
+  return *this;
+}
+
 void AnimationDefinition::Animate(Animation& animation, AnimatedProperty::GetActor getActor)
 {
   DALI_ASSERT_ALWAYS(animation);
@@ -69,18 +83,98 @@ Animation AnimationDefinition::ReAnimate(AnimatedProperty::GetActor getActor)
   return animation;
 }
 
-AnimationDefinition& AnimationDefinition::operator=(AnimationDefinition&& other)
+void AnimationDefinition::SetName(const std::string& name)
 {
-  AnimationDefinition tmp(std::move(other));
-  mName             = std::move(tmp.mName);
-  mDuration         = tmp.mDuration;
-  mLoopCount        = tmp.mLoopCount;
-  mDisconnectAction = tmp.mDisconnectAction;
-  mEndAction        = tmp.mEndAction;
-  mSpeedFactor      = tmp.mSpeedFactor;
-  mPlayRange        = tmp.mPlayRange;
-  mProperties.swap(tmp.mProperties);
-  return *this;
+  mName = name;
+}
+
+const std::string& AnimationDefinition::GetName() const
+{
+  return mName;
+}
+
+void AnimationDefinition::SetDuration(float duration)
+{
+  mDuration = duration;
+}
+
+float AnimationDefinition::GetDuration() const
+{
+  return mDuration;
+}
+
+void AnimationDefinition::SetLoopCount(int32_t loopCount)
+{
+  mLoopCount = loopCount;
+}
+
+int AnimationDefinition::GetLoopCount() const
+{
+  return mLoopCount;
+}
+
+void AnimationDefinition::SetDisconnectAction(Animation::EndAction disconnectAction)
+{
+  mDisconnectAction = disconnectAction;
+}
+
+Animation::EndAction AnimationDefinition::GetDisconnectAction() const
+{
+  return mDisconnectAction;
+}
+
+void AnimationDefinition::SetEndAction(Animation::EndAction endAction)
+{
+  mEndAction = endAction;
+}
+
+Animation::EndAction AnimationDefinition::GetEndAction() const
+{
+  return mEndAction;
+}
+
+void AnimationDefinition::SetSpeedFactor(float speedFactor)
+{
+  mSpeedFactor = speedFactor;
+}
+
+float AnimationDefinition::GetSpeedFactor() const
+{
+  return mSpeedFactor;
+}
+
+void AnimationDefinition::SetPlayRange(const Vector2& playRange)
+{
+  mPlayRange = playRange;
+}
+
+Vector2 AnimationDefinition::GetPlayRange() const
+{
+  return mPlayRange;
+}
+
+void AnimationDefinition::ReserveSize(uint32_t size)
+{
+  mProperties.reserve(size);
+}
+
+uint32_t AnimationDefinition::GetPropertyCount()
+{
+  return mProperties.size();
+}
+
+void AnimationDefinition::SetProperty(uint32_t index, AnimatedProperty&& property)
+{
+  if(mProperties.size() <= index)
+  {
+    mProperties.resize(index + 1);
+  }
+  mProperties[index] = std::move(property);
+}
+
+const AnimatedProperty& AnimationDefinition::GetPropertyAt(uint32_t index)
+{
+  return mProperties[index];
 }
 
 } // namespace Dali::Scene3D::Loader
index 27fe2ac..ff72e3f 100644 (file)
@@ -49,6 +49,8 @@ public: // METHODS
 
   AnimationDefinition(AnimationDefinition&& other);
 
+  AnimationDefinition& operator=(AnimationDefinition&& other);
+
   /**
    * @brief Registers the properties against the given @a animation. @a getActor
    *  will be used to obtain the Actors for each AnimatedProperty.
@@ -61,13 +63,138 @@ public: // METHODS
    */
   Animation ReAnimate(AnimatedProperty::GetActor getActor);
 
-  AnimationDefinition& operator=(AnimationDefinition&& other);
+  /**
+   * @brief Set the name of the animation.
+   *
+   * @param[in] name The name of the animation.
+   */
+  void SetName(const std::string& name);
+
+  /**
+   * @brief Get the name of the animation.
+   *
+   * @return The name of the animation.
+   */
+  const std::string& GetName() const;
+
+  /**
+   * @brief Set the duration of the animation in seconds.
+   *
+   * @param[in] duration The duration of the animation in seconds.
+   */
+  void SetDuration(float duration);
+
+  /**
+   * @brief Get the duration of the animation in seconds.
+   *
+   * @return The duration of the animation in seconds.
+   */
+  float GetDuration() const;
+
+  /**
+   * @brief Set the number of times to loop the animation.
+   *
+   * @param[in] loopCount The number of times to loop the animation. Use -1 for infinite looping.
+   */
+  void SetLoopCount(int32_t loopCount);
+
+  /**
+   * @brief Get the number of times to loop the animation.
+   *
+   * @return The number of times to loop the animation. Use -1 for infinite looping.
+   */
+  int GetLoopCount() const;
+
+  /**
+   * @brief Set what should happen when an animation is disconnected from an object.
+   *
+   * @param[in] disconnectAction What should happen when an animation is disconnected from an object.
+   */
+  void SetDisconnectAction(Animation::EndAction disconnectAction);
+
+  /**
+   * @brief Get what should happen when an animation is disconnected from an object.
+   *
+   * @return What should happen when an animation is disconnected from an object.
+   */
+  Animation::EndAction GetDisconnectAction() const;
+
+  /**
+   * @brief Set what should happen when an animation reaches its end.
+   *
+   * @param[in] endAction What should happen when an animation reaches its end.
+   */
+  void SetEndAction(Animation::EndAction endAction);
+
+  /**
+   * @brief Get what should happen when an animation reaches its end.
+   *
+   * @return What should happen when an animation reaches its end.
+   */
+  Animation::EndAction GetEndAction() const;
+
+  /**
+   * @brief Set a speed factor for this animation. This can be used to speed up or slow down playback of this animation relative to other animations in a scene.
+   *
+   * @param[in] speedFactor The speed factor for this animation. 1.0 is normal speed, 2.0 is double speed, 0.5 is half speed, etc.
+   */
+  void SetSpeedFactor(float speedFactor);
+
+  /**
+   * @brief Get a speed factor for this animation. This can be used to speed up or slow down playback of this animation relative to other animations in a scene.
+   *
+   * @return The speed factor for this animation. 1.0 is normal speed, 2.0 is double speed, 0.5 is half speed, etc.
+   */
+  float GetSpeedFactor() const;
+
+  /**
+   * @brief Set a range within which to play this animation. This can be used to play only part of an animation or to play it backwards by setting playRange.y < playRange.x
+   *
+   * @param[in] playRange A range within which to play this animation. x = start time in seconds, y = end time in seconds
+   */
+  void SetPlayRange(const Vector2& playRange);
+
+  /**
+   * @brief Get a range within which to play this animation. This can be used to play only part of an animation or to play it backwards by setting playRange.y < playRange.x
+   *
+   * @return A range within which to play this animation. x = start time in seconds, y = end time in seconds
+   */
+  Vector2 GetPlayRange() const;
+
+  /**
+   * @brief Reserves Animated property vector's size
+   *
+   * @param[in] size The size to reserve property
+   */
+  void ReserveSize(uint32_t size);
+
+  /**
+   * @brief Retrieves the number of animated properties' count
+   *
+   * @return The count of animated properties.
+   */
+  uint32_t GetPropertyCount();
+
+  /**
+   * @brief Add a property that will be animated by this AnimationDefinition
+   *
+   * @param[in] index The index the property will be stored.
+   * @param[in] property The property that will be animated by this AnimationDefinition
+   */
+  void SetProperty(uint32_t index, AnimatedProperty&& property);
+
+  /**
+   * @brief Retrieves animated property at the index
+   *
+   * @param[in] index The index of property to be retrieved.
+   */
+  const AnimatedProperty& GetPropertyAt(uint32_t index);
 
-public: // DATA
+private: // DATA
   std::string mName;
 
   float                mDuration         = DEFAULT_DURATION_SECONDS;
-  int                  mLoopCount        = 1;
+  int32_t              mLoopCount        = 1;
   Animation::EndAction mDisconnectAction = Animation::BAKE_FINAL;
   Animation::EndAction mEndAction        = Animation::BAKE;
   float                mSpeedFactor      = 1.f;
@@ -84,4 +211,4 @@ struct AnimationGroupDefinition
 
 } // namespace Dali::Scene3D::Loader
 
-#endif //DALI_SCENE3D_LOADER_ANIMATION_DEFINITION_H
+#endif // DALI_SCENE3D_LOADER_ANIMATION_DEFINITION_H
index d238452..4cf288c 100644 (file)
 
 namespace Dali::Scene3D::Loader
 {
-const std::string BlendShapes::NUMBER_OF_BLEND_SHAPES("uNumberOfBlendShapes");
-const std::string BlendShapes::UNNORMALIZE_FACTOR("uBlendShapeUnnormalizeFactor");
-const std::string BlendShapes::COMPONENT_SIZE("uBlendShapeComponentSize");
+const char* BlendShapes::NUMBER_OF_BLEND_SHAPES("uNumberOfBlendShapes");
+const char* BlendShapes::UNNORMALIZE_FACTOR("uBlendShapeUnnormalizeFactor");
+const char* BlendShapes::COMPONENT_SIZE("uBlendShapeComponentSize");
 
-const std::string BlendShapes::COMPONENTS("blendShapeComponents");
+const char* BlendShapes::COMPONENTS("blendShapeComponents");
 
-const std::string BlendShapes::WEIGHTS_UNIFORM("uBlendShapeWeight");
+const char* BlendShapes::WEIGHTS_UNIFORM("uBlendShapeWeight");
 
-void BlendShapes::ConfigureProperties(const std::pair<MeshDefinition, MeshGeometry>& mesh, Shader shader, Actor actor)
+void BlendShapes::ConfigureProperties(const BlendShapeData& data, Shader shader)
 {
   unsigned int index = 0u;
 
   char        weightNameBuffer[32];
   char        unnormalizeFactorNameBuffer[64];
-  char* const pWeightName = weightNameBuffer + snprintf(weightNameBuffer, sizeof(weightNameBuffer), "%s", WEIGHTS_UNIFORM.c_str());
-  char* const pFactorName = unnormalizeFactorNameBuffer + snprintf(unnormalizeFactorNameBuffer, sizeof(unnormalizeFactorNameBuffer), "%s", UNNORMALIZE_FACTOR.c_str());
-  for(const auto& blendShape : mesh.first.mBlendShapes)
+  char* const pWeightName = weightNameBuffer + snprintf(weightNameBuffer, sizeof(weightNameBuffer), "%s", WEIGHTS_UNIFORM);
+  char* const pFactorName = unnormalizeFactorNameBuffer + snprintf(unnormalizeFactorNameBuffer, sizeof(unnormalizeFactorNameBuffer), "%s", UNNORMALIZE_FACTOR);
+  for(const auto& weight : data.weights)
   {
     snprintf(pWeightName, sizeof(weightNameBuffer) - (pWeightName - weightNameBuffer), "[%d]", index);
     std::string weightName{weightNameBuffer};
-    actor.RegisterProperty(weightName, blendShape.weight);
+    Dali::Actor actor = data.mActor.GetHandle();
+    if(actor)
+    {
+      actor.RegisterProperty(weightName, weight);
+    }
 
-    if(shader && mesh.first.mBlendShapeVersion == Version::VERSION_1_0)
+    if(shader && data.version == Version::VERSION_1_0)
     {
       snprintf(pFactorName, sizeof(unnormalizeFactorNameBuffer) - (pFactorName - unnormalizeFactorNameBuffer), "[%d]", index);
       std::string factorName{unnormalizeFactorNameBuffer};
-      shader.RegisterProperty(factorName, mesh.second.blendShapeUnnormalizeFactor[index]);
+      shader.RegisterProperty(factorName, data.unnormalizeFactors[index]);
     }
 
     ++index;
@@ -62,22 +66,16 @@ void BlendShapes::ConfigureProperties(const std::pair<MeshDefinition, MeshGeomet
 
   if(shader)
   {
-    if(Version::VERSION_2_0 == mesh.first.mBlendShapeVersion)
+    if(Version::VERSION_2_0 == data.version)
     {
-      shader.RegisterProperty(UNNORMALIZE_FACTOR, mesh.second.blendShapeUnnormalizeFactor[0u]);
+      shader.RegisterProperty(UNNORMALIZE_FACTOR, data.unnormalizeFactors[0u]);
     }
 
     shader.RegisterProperty(NUMBER_OF_BLEND_SHAPES, Property::Value(static_cast<float>(index)));
-    shader.RegisterProperty(COMPONENT_SIZE, Property::Value(static_cast<float>(mesh.second.blendShapeBufferOffset)));
+    shader.RegisterProperty(COMPONENT_SIZE, Property::Value(static_cast<float>(data.bufferOffset)));
 
     // Create a read only property to preserve the components of the blend shape.
-    int32_t components = 0x0;
-    for(auto& bs : mesh.first.mBlendShapes)
-    {
-      components |= (bs.deltas.IsDefined() * Component::POSITIONS) |
-                    (bs.normals.IsDefined() * Component::NORMALS) | (bs.tangents.IsDefined() * Component::TANGENTS);
-    }
-    shader.RegisterProperty(COMPONENTS, components, Property::AccessMode::READ_ONLY);
+    shader.RegisterProperty(COMPONENTS, data.components, Property::AccessMode::READ_ONLY);
   }
 }
 
index a3182cf..2b304d6 100644 (file)
@@ -19,6 +19,8 @@
 
 // EXTERNAL INCLUDES
 #include <dali/public-api/actors/actor.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <dali/public-api/object/weak-handle.h>
 #include <dali/public-api/rendering/shader.h>
 #include <string>
 
@@ -49,22 +51,32 @@ struct DALI_SCENE3D_API BlendShapes
     };
   };
 
+  struct BlendShapeData
+  {
+    std::vector<float>      weights;
+    std::vector<float>      unnormalizeFactors;
+    Version                 version{Scene3D::Loader::BlendShapes::Version::INVALID};
+    uint32_t                bufferOffset{0};
+    int32_t                 components{0x0};
+    Dali::WeakHandle<Actor> mActor;
+  };
+
   // shader properties - animatable (uniforms)
-  static const std::string NUMBER_OF_BLEND_SHAPES; ///< Integer number of blend shapes loaded.
-  static const std::string UNNORMALIZE_FACTOR;     ///< Scalar(s) for position components of blend shapes; Version 1.0: float array (1 per blend shape); Version 2.0: single float.
-  static const std::string COMPONENT_SIZE;         ///< Integer offset from one component (positions / normals / tangents) of a blend shape to the next.
+  static const char* NUMBER_OF_BLEND_SHAPES; ///< Integer number of blend shapes loaded.
+  static const char* UNNORMALIZE_FACTOR;     ///< Scalar(s) for position components of blend shapes; Version 1.0: float array (1 per blend shape); Version 2.0: single float.
+  static const char* COMPONENT_SIZE;         ///< Integer offset from one component (positions / normals / tangents) of a blend shape to the next.
 
   // shader properties - read-only (not available as uniforms)
-  static const std::string COMPONENTS; ///< Integer bitmask of the blend shape components that the shader uses; refer to the Components enum.
+  static const char* COMPONENTS; ///< Integer bitmask of the blend shape components that the shader uses; refer to the Components enum.
 
   // actor property (instance) - animatable (uniforms)
-  static const std::string WEIGHTS_UNIFORM; ///< The weight of each blend shape in a float array
+  static const char* WEIGHTS_UNIFORM; ///< The weight of each blend shape in a float array
 
   /**
    * @brief Registers properties based on the mesh definition (and geometry) and identified by the above string constants,
    *  on the given @a shader and @a actor.
    */
-  static void ConfigureProperties(const std::pair<MeshDefinition, MeshGeometry>& mesh, Shader shader, Actor actor);
+  static void ConfigureProperties(const BlendShapeData& data, Shader shader);
 
   BlendShapes() = delete;
 };
index 93a826d..70cb297 100644 (file)
@@ -287,8 +287,8 @@ AnimationDefinition GenerateAnimation(const std::string& animationName, std::sha
 {
   AnimationDefinition animationDefinition;
 
-  animationDefinition.mName     = animationName;
-  animationDefinition.mDuration = frameTime * (frameCount - 1);
+  animationDefinition.SetName(animationName);
+  animationDefinition.SetDuration(frameTime * (frameCount - 1));
   float keyFrameInterval        = (frameCount > 1u) ? 1.0f / static_cast<float>(frameCount - 1u) : Dali::Math::MACHINE_EPSILON_10;
 
   std::vector<std::shared_ptr<Joint>> jointList;
@@ -296,16 +296,16 @@ AnimationDefinition GenerateAnimation(const std::string& animationName, std::sha
 
   if(!jointList.empty())
   {
-    animationDefinition.mProperties.resize(jointList.size() * 2u); // translation and rotation
+    animationDefinition.ReserveSize(jointList.size() * 2u); // translation and rotation
     for(uint32_t i = 0; i < jointList.size(); ++i)
     {
-      AnimatedProperty& translationProperty = animationDefinition.mProperties[i * 2u];
-      translationProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.mDuration);
+      AnimatedProperty translationProperty;
+      translationProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.GetDuration());
       translationProperty.mNodeName         = jointList[i]->name;
       translationProperty.mPropertyName     = PROPERTY_NAME_POSITION.data();
 
-      AnimatedProperty& rotationProperty = animationDefinition.mProperties[i * 2u + 1];
-      rotationProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.mDuration);
+      AnimatedProperty rotationProperty;
+      rotationProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.GetDuration());
       rotationProperty.mNodeName         = jointList[i]->name;
       rotationProperty.mPropertyName     = PROPERTY_NAME_ORIENTATION.data();
 
@@ -316,6 +316,8 @@ AnimationDefinition GenerateAnimation(const std::string& animationName, std::sha
         translationProperty.mKeyFrames.Add(static_cast<float>(j) * keyFrameInterval, (jointList[i]->translations[j] * scale));
         rotationProperty.mKeyFrames.Add(static_cast<float>(j) * keyFrameInterval, jointList[i]->rotations[j]);
       }
+      animationDefinition.SetProperty(i * 2u, std::move(translationProperty));
+      animationDefinition.SetProperty(i * 2u + 1, std::move(rotationProperty));
     }
   }
 
index 3124a85..61e5744 100644 (file)
@@ -21,6 +21,7 @@
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/environment-variable.h>
 #include <dali/devel-api/adaptor-framework/image-loading.h>
+#include <dali/devel-api/threading/mutex.h>
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/public-api/loader/environment-map-loader.h>
@@ -35,20 +36,39 @@ std::string GetDaliImagePath()
 }
 
 static constexpr float DEFAULT_INTENSITY = 1.0f;
+
 } // unnamed namespace
 
 namespace Dali::Scene3D::Loader
 {
 namespace
 {
-const std::string PRE_COMPUTED_BRDF_TEXTURE_FILE_NAME = "brdfLUT.png";
+const char* PRE_COMPUTED_BRDF_TEXTURE_FILE_NAME = "brdfLUT.png";
+}
+PixelData EnvironmentDefinition::mBrdfPixelData;
+Texture   EnvironmentDefinition::mBrdfTexture;
+bool      EnvironmentDefinition::mIsBrdfLoaded = false;
+
+Dali::Texture EnvironmentDefinition::GetBrdfTexture()
+{
+  if(!mBrdfTexture)
+  {
+    if(!mIsBrdfLoaded)
+    {
+      LoadBrdfTexture();
+    }
+    mBrdfTexture = Texture::New(TextureType::TEXTURE_2D, mBrdfPixelData.GetPixelFormat(), mBrdfPixelData.GetWidth(), mBrdfPixelData.GetHeight());
+    mBrdfTexture.Upload(mBrdfPixelData);
+  }
+  return mBrdfTexture;
 }
 
 EnvironmentDefinition::RawData
-EnvironmentDefinition::LoadRaw(const std::string& environmentsPath) const
+EnvironmentDefinition::LoadRaw(const std::string& environmentsPath)
 {
   RawData raw;
-  auto    loadFn = [&environmentsPath](const std::string& path, EnvironmentMapData& environmentMapData) {
+  auto    loadFn = [&environmentsPath](const std::string& path, EnvironmentMapData& environmentMapData)
+  {
     if(path.empty())
     {
       environmentMapData.mPixelData.resize(6);
@@ -69,16 +89,12 @@ EnvironmentDefinition::LoadRaw(const std::string& environmentsPath) const
 
   if(mUseBrdfTexture)
   {
-    Devel::PixelBuffer pixelBuffer = LoadImageFromFile(GetDaliImagePath() + PRE_COMPUTED_BRDF_TEXTURE_FILE_NAME);
-    if(pixelBuffer)
-    {
-      raw.mBrdf = Devel::PixelBuffer::Convert(pixelBuffer);
-    }
+    LoadBrdfTexture();
   }
   return raw;
 }
 
-EnvironmentDefinition::Textures EnvironmentDefinition::Load(RawData&& raw) const
+EnvironmentDefinition::Textures EnvironmentDefinition::Load(RawData&& raw)
 {
   Textures textures;
 
@@ -95,10 +111,9 @@ EnvironmentDefinition::Textures EnvironmentDefinition::Load(RawData&& raw) const
     textures.mSpecularMipmapLevels = raw.mSpecular.GetMipmapLevels();
   }
 
-  if(raw.mBrdf)
+  if(mUseBrdfTexture)
   {
-    textures.mBrdf = Texture::New(TextureType::TEXTURE_2D, raw.mBrdf.GetPixelFormat(), raw.mBrdf.GetWidth(), raw.mBrdf.GetHeight());
-    textures.mBrdf.Upload(raw.mBrdf);
+    textures.mBrdf = GetBrdfTexture();
   }
   return textures;
 }
@@ -108,4 +123,21 @@ float EnvironmentDefinition::GetDefaultIntensity()
   return DEFAULT_INTENSITY;
 }
 
+void EnvironmentDefinition::LoadBrdfTexture()
+{
+  static Dali::Mutex mutex;
+  {
+    Mutex::ScopedLock lock(mutex);
+    if(!mIsBrdfLoaded)
+    {
+      Devel::PixelBuffer pixelBuffer = LoadImageFromFile(GetDaliImagePath() + PRE_COMPUTED_BRDF_TEXTURE_FILE_NAME);
+      if(pixelBuffer)
+      {
+        mBrdfPixelData = Devel::PixelBuffer::Convert(pixelBuffer);
+        mIsBrdfLoaded = true;
+      }
+    }
+  }
+}
+
 } // namespace Dali::Scene3D::Loader
\ No newline at end of file
index 577a31f..36d8ae6 100644 (file)
@@ -65,18 +65,20 @@ struct DALI_SCENE3D_API EnvironmentDefinition
   EnvironmentDefinition(EnvironmentDefinition&&) = default;
   EnvironmentDefinition& operator=(EnvironmentDefinition&&) = default;
 
+  static Dali::Texture GetBrdfTexture();
+
   /**
    * @brief Loads raw pixel data for the given diffuse and specular maps.
    * @note This can be done on any thread.
    */
-  RawData LoadRaw(const std::string& environmentsPath) const;
+  RawData LoadRaw(const std::string& environmentsPath);
 
   /**
    * @brief Creates DALi cubemap Textures from the pixel data in @a raw, then
    *  returns them in a Textures object.
    * @note This must only be called from the event thread.
    */
-  Textures Load(RawData&& raw) const;
+  Textures Load(RawData&& raw);
 
   /**
    * @brief Get default intensity value.
@@ -84,6 +86,9 @@ struct DALI_SCENE3D_API EnvironmentDefinition
    */
   static float GetDefaultIntensity();
 
+private:
+  static void LoadBrdfTexture();
+
 public: // DATA
   std::string              mDiffuseMapPath;
   std::string              mSpecularMapPath;
@@ -92,6 +97,11 @@ public: // DATA
   Vector3                  mYDirection      = Vector3::ONE;
   float                    mIblIntensity    = 1.0f;
   bool                     mUseBrdfTexture  = false;
+
+private:
+  static PixelData mBrdfPixelData;
+  static Texture   mBrdfTexture;
+  static bool      mIsBrdfLoaded;
 };
 
 } // namespace Dali::Scene3D::Loader
index 2414b1b..93920a2 100644 (file)
@@ -122,8 +122,8 @@ AnimationDefinition LoadFacialAnimation(const std::string& url)
   FACIAL_ANIMATION_READER.Read(rootObj, facialAnimation);\r
 \r
   AnimationDefinition animationDefinition;\r
-  animationDefinition.mName     = std::string(facialAnimation.mName.data());\r
-  animationDefinition.mDuration = MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[facialAnimation.mNumberOfFrames - 1u]);\r
+  animationDefinition.SetName(facialAnimation.mName.data());\r
+  animationDefinition.SetDuration(MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[facialAnimation.mNumberOfFrames - 1u]));\r
 \r
   // Calculate the number of animated properties.\r
   uint32_t numberOfAnimatedProperties = 0u;\r
@@ -131,46 +131,29 @@ AnimationDefinition LoadFacialAnimation(const std::string& url)
   {\r
     numberOfAnimatedProperties += blendShape.mNumberOfMorphTarget;\r
   }\r
-  animationDefinition.mProperties.resize(numberOfAnimatedProperties);\r
+  animationDefinition.ReserveSize(numberOfAnimatedProperties);\r
 \r
-  // Create the key frame instances.\r
-  for(auto& animatedProperty : animationDefinition.mProperties)\r
-  {\r
-    animatedProperty.mKeyFrames = Dali::KeyFrames::New();\r
-  }\r
-\r
-  // Set the property names\r
   uint32_t targets = 0u;\r
   for(const auto& blendShape : facialAnimation.mBlendShapes)\r
   {\r
     for(uint32_t morphTargetIndex = 0u; morphTargetIndex < blendShape.mNumberOfMorphTarget; ++morphTargetIndex)\r
     {\r
-      AnimatedProperty& animatedProperty = animationDefinition.mProperties[targets + morphTargetIndex];\r
-      animatedProperty.mTimePeriod       = Dali::TimePeriod(animationDefinition.mDuration);\r
-\r
+      AnimatedProperty animatedProperty;\r
+      animatedProperty.mTimePeriod = Dali::TimePeriod(animationDefinition.GetDuration());\r
       animatedProperty.mNodeName = blendShape.mNodeName;\r
-\r
       std::stringstream weightPropertyStream;\r
       weightPropertyStream << BlendShapes::WEIGHTS_UNIFORM << "[" << morphTargetIndex << "]";\r
       animatedProperty.mPropertyName = weightPropertyStream.str();\r
-    }\r
-    targets += blendShape.mNumberOfMorphTarget;\r
-  }\r
 \r
-  targets = 0u;\r
-  for(const auto& blendShape : facialAnimation.mBlendShapes)\r
-  {\r
-    for(uint32_t timeIndex = 0u; timeIndex < facialAnimation.mNumberOfFrames; ++timeIndex)\r
-    {\r
-      const float progress = MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[timeIndex]) / animationDefinition.mDuration;\r
-\r
-      for(uint32_t morphTargetIndex = 0u; morphTargetIndex < blendShape.mNumberOfMorphTarget; ++morphTargetIndex)\r
+      animatedProperty.mKeyFrames = Dali::KeyFrames::New();\r
+      for(uint32_t timeIndex = 0u; timeIndex < facialAnimation.mNumberOfFrames; ++timeIndex)\r
       {\r
-        AnimatedProperty& animatedProperty = animationDefinition.mProperties[targets + morphTargetIndex];\r
-\r
+        const float progress = MILLISECONDS_TO_SECONDS * static_cast<float>(facialAnimation.mTime[timeIndex]) / animationDefinition.GetDuration();\r
         animatedProperty.mKeyFrames.Add(progress, blendShape.mKeys[timeIndex][morphTargetIndex]);\r
       }\r
+      animationDefinition.SetProperty(targets + morphTargetIndex, std::move(animatedProperty));\r
     }\r
+\r
     targets += blendShape.mNumberOfMorphTarget;\r
   }\r
 \r
index ac6b73a..eb87ae1 100644 (file)
@@ -65,7 +65,11 @@ constexpr WrapMode::Type WRAP_MODES_TO_DALI[]{
   WrapMode::CLAMP_TO_EDGE,
   WrapMode::MIRRORED_REPEAT};
 
-const SamplerFlags::Type SINGLE_VALUE_SAMPLER = SamplerFlags::Encode(FilterMode::NEAREST, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
+const SamplerFlags::Type GetSingleValueSampler()
+{
+  static const SamplerFlags::Type SINGLE_VALUE_SAMPLER = SamplerFlags::Encode(FilterMode::NEAREST, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
+  return SINGLE_VALUE_SAMPLER;
+}
 
 static constexpr std::string_view EMBEDDED_DATA_PREFIX               = "data:";
 static constexpr std::string_view EMBEDDED_DATA_IMAGE_MEDIA_TYPE     = "image/";
@@ -105,6 +109,7 @@ Dali::PixelData LoadImageResource(const std::string& resourcePath,
   }
   else
   {
+    textureDefinition.mDirectoryPath = resourcePath;
     pixelData = SyncImageLoader::Load(resourcePath + textureDefinition.mImageUri, textureDefinition.mMinImageDimensions, fittingMode, textureDefinition.mSamplingMode, orientationCorrection);
   }
   return pixelData;
@@ -206,7 +211,7 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath)
     {
       const auto bufferSize = 4;
       uint8_t*   buffer     = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff, 0xff}; // normal of (0, 0, 1), roughness of 1
-      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
     }
   }
   else
@@ -240,7 +245,7 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath)
       buffer[0] = static_cast<uint8_t>(mColor.r * 255.f);
       buffer[1] = static_cast<uint8_t>(mColor.g * 255.f);
       buffer[2] = static_cast<uint8_t>(mColor.b * 255.f);
-      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, format, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, format, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
     }
 
     // If we have transparency, or an image based albedo map, we will have to continue with separate metallicRoughness + normal.
@@ -256,7 +261,7 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath)
       // glTF2 uses B & G, so we might as well just set all components to 1.0.
       const auto bufferSize = 4;
       uint8_t*   buffer     = new uint8_t[bufferSize]{0xff, 0xff, 0xff, 0xff};
-      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
     }
 
     if(checkStage(NORMAL))
@@ -270,13 +275,13 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath)
       {
         const auto bufferSize = 3;
         uint8_t*   buffer     = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff}; // normal of (0, 0, 1)
-        raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+        raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
       }
       else // single-value normal-roughness
       {
         const auto bufferSize = 4;
         uint8_t*   buffer     = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff, 0xff}; // normal of (0, 0, 1), roughness of 1.0
-        raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+        raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
       }
     }
   }
index b40a746..b8da495 100644 (file)
@@ -28,6 +28,7 @@
 #include <dali-scene3d/public-api/loader/environment-definition.h>
 #include <dali-scene3d/public-api/loader/index.h>
 #include <dali-scene3d/public-api/loader/utils.h>
+#include <dali-scene3d/public-api/model-components/material.h>
 
 namespace Dali::Scene3D::Loader
 {
@@ -111,6 +112,7 @@ struct DALI_SCENE3D_API SamplerFlags
 struct DALI_SCENE3D_API TextureDefinition
 {
   std::string          mImageUri; // When the texture is loaded from embedded resources, this URI is used as a data stream.
+  std::string          mDirectoryPath;
   SamplerFlags::Type   mSamplerFlags;
   ImageDimensions      mMinImageDimensions;
   SamplingMode::Type   mSamplingMode;
@@ -234,6 +236,7 @@ public: // DATA
   float   mNormalScale         = 1.f;
   float   mOcclusionStrength   = 1.f;
   Vector3 mEmissiveFactor      = Vector3::ZERO;
+  float   mIor                 = -1.0f;
   float   mDielectricSpecular  = 0.04f;
   float   mSpecularFactor      = 1.0f;
   Vector3 mSpecularColorFactor = Vector3::ONE;
@@ -244,10 +247,12 @@ public: // DATA
   bool mNeedNormalTexture            = true;
   bool mDoubleSided                  = false;
 
-  bool mIsOpaque = true;
-  bool mIsMask   = false;
+  Scene3D::Material::AlphaModeType mAlphaModeType = Scene3D::Material::AlphaModeType::OPAQUE;
+  bool                             mIsOpaque      = true;
+  bool                             mIsMask        = false;
 
   std::vector<TextureStage> mTextureStages;
+  Material                  mMaterial;
 };
 
 } // namespace Dali::Scene3D::Loader
index f34614b..58ae2c0 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef DALI_SCENE3D_LOADERER_MATRIX_STACK_H_
-#define DALI_SCENE3D_LOADERER_MATRIX_STACK_H_
+#ifndef DALI_SCENE3D_LOADER_MATRIX_STACK_H_
+#define DALI_SCENE3D_LOADER_MATRIX_STACK_H_
 /*
  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
@@ -48,4 +48,4 @@ private:
 
 } // namespace Dali::Scene3D::Loader
 
-#endif //DALI_SCENE3D_LOADERER_MATRIX_STACK_H_
+#endif //DALI_SCENE3D_LOADER_MATRIX_STACK_H_
index b977c65..4d7ef4d 100644 (file)
@@ -63,7 +63,7 @@ private:
   uint16_t (*mFunc)(uintptr_t&);
 };
 
-const std::string QUAD("quad");
+const char* QUAD("quad");
 
 ///@brief Reads a blob from the given stream @a source into @a target, which must have
 /// at least @a descriptor.length bytes.
@@ -879,6 +879,18 @@ MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector&
       raw.mAttribs.push_back({"aVertexColor", propertyType, static_cast<uint32_t>(bufferSize / propertySize), std::move(buffer)});
     }
   }
+  else
+  {
+    std::vector<uint8_t> buffer(raw.mAttribs[0].mNumElements * sizeof(Vector4));
+    auto                 colors = reinterpret_cast<Vector4*>(buffer.data());
+
+    for(uint32_t i = 0; i < raw.mAttribs[0].mNumElements; i++)
+    {
+      colors[i] = Vector4::ONE;
+    }
+
+    raw.mAttribs.push_back({"aVertexColor", Property::VECTOR4, raw.mAttribs[0].mNumElements, std::move(buffer)});
+  }
 
   if(IsSkinned())
   {
@@ -1028,4 +1040,14 @@ MeshGeometry MeshDefinition::Load(RawData&& raw) const
   return meshGeometry;
 }
 
+void MeshDefinition::RetrieveBlendShapeComponents(bool& hasPositions, bool& hasNormals, bool& hasTangents) const
+{
+  for(const auto& blendShape : mBlendShapes)
+  {
+    hasPositions = hasPositions || blendShape.deltas.IsDefined();
+    hasNormals   = hasNormals || blendShape.normals.IsDefined();
+    hasTangents  = hasTangents || blendShape.tangents.IsDefined();
+  }
+}
+
 } // namespace Dali::Scene3D::Loader
index 7289b40..eceb795 100644 (file)
@@ -269,6 +269,15 @@ struct DALI_SCENE3D_API MeshDefinition
    */
   MeshGeometry Load(RawData&& raw) const;
 
+  /**
+   * @brief Retrieves what Components information is in this mesh's BlendShape.
+   *
+   * @param[out] hasPositions True if the BlendShape has position components
+   * @param[out] hasNormals True if the BlendShape has normal components
+   * @param[out] hasTangents True if the BlendShape has tangent components
+   */
+  void RetrieveBlendShapeComponents(bool& hasPositions, bool& hasNormals, bool& hasTangents) const;
+
 public: // DATA
   std::shared_ptr<RawData> mRawData;
   uint32_t                 mFlags         = 0x0;
@@ -288,7 +297,8 @@ public: // DATA
   std::vector<BlendShape> mBlendShapes;
   BlendShapes::Version    mBlendShapeVersion = BlendShapes::Version::INVALID;
 
-  Index mSkeletonIdx = INVALID_INDEX;
+  Index          mSkeletonIdx = INVALID_INDEX;
+  ModelPrimitive mModelPrimitive;
 };
 
 } // namespace Dali::Scene3D::Loader
index 1ce30a7..fa2ac1d 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef DALI_SCENE3D_LOADERER_MESH_GEOMETRY_H
-#define DALI_SCENE3D_LOADERER_MESH_GEOMETRY_H
+#ifndef DALI_SCENE3D_LOADER_MESH_GEOMETRY_H
+#define DALI_SCENE3D_LOADER_MESH_GEOMETRY_H
 /*
  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/public-api/api.h>
+#include <dali-scene3d/public-api/model-components/model-primitive.h>
 
 namespace Dali::Scene3D::Loader
 {
 struct DALI_SCENE3D_API MeshGeometry
 {
-  Geometry      geometry;                    ///< The array of vertices.
-  Texture       blendShapeGeometry;          ///< The array of vertices of the different blend shapes encoded inside a texture with power of two dimensions.
-  Vector<float> blendShapeUnnormalizeFactor; ///< Factor used to unnormalize the geometry of the blend shape.
-  unsigned int  blendShapeBufferOffset{0};   ///< Offset used to calculate the start of each blend shape.
+  Geometry       geometry;                    ///< The array of vertices.
+  Texture        blendShapeGeometry;          ///< The array of vertices of the different blend shapes encoded inside a texture with power of two dimensions.
+  Vector<float>  blendShapeUnnormalizeFactor; ///< Factor used to unnormalize the geometry of the blend shape.
+  unsigned int   blendShapeBufferOffset{0};   ///< Offset used to calculate the start of each blend shape.
 };
 
 } // namespace Dali::Scene3D::Loader
 
-#endif // DALI_SCENE3D_LOADERER_MESH_GEOMETRY_H
+#endif // DALI_SCENE3D_LOADER_MESH_GEOMETRY_H
index 03d96f4..a8632e9 100644 (file)
 #include <dali-scene3d/public-api/loader/node-definition.h>
 
 // INTERNAL INCLUDES
+#include <dali-scene3d/internal/model-components/material-impl.h>
+#include <dali-scene3d/internal/model-components/model-primitive-impl.h>
 #include <dali-scene3d/public-api/loader/renderer-state.h>
 #include <dali-scene3d/public-api/loader/utils.h>
 
+#include <dali/integration-api/debug.h>
+
 namespace Dali
 {
 namespace
 {
-constexpr std::string_view IBL_INTENSITY_STRING("uIblIntensity");
-constexpr std::string_view IBL_Y_DIRECTION("uYDirection");
-constexpr std::string_view IBL_MAXLOD("uMaxLOD");
+static constexpr std::string_view IBL_INTENSITY_STRING("uIblIntensity");
+static constexpr std::string_view IBL_Y_DIRECTION("uYDirection");
+static constexpr std::string_view IBL_MAXLOD("uMaxLOD");
+
+static constexpr uint32_t MAX_NUMBER_OF_MATERIAL_TEXTURE = 7;
+static constexpr uint32_t SEMANTICS[MAX_NUMBER_OF_MATERIAL_TEXTURE] =
+  {
+    Scene3D::Loader::MaterialDefinition::ALBEDO,
+    Scene3D::Loader::MaterialDefinition::METALLIC | Scene3D::Loader::MaterialDefinition::ROUGHNESS,
+    Scene3D::Loader::MaterialDefinition::NORMAL,
+    Scene3D::Loader::MaterialDefinition::OCCLUSION,
+    Scene3D::Loader::MaterialDefinition::EMISSIVE,
+    Scene3D::Loader::MaterialDefinition::SPECULAR,
+    Scene3D::Loader::MaterialDefinition::SPECULAR_COLOR,
+};
+
+static constexpr Scene3D::Material::TextureType TEXTURE_TYPES[MAX_NUMBER_OF_MATERIAL_TEXTURE] =
+  {
+    Scene3D::Material::TextureType::BASE_COLOR,
+    Scene3D::Material::TextureType::METALLIC_ROUGHNESS,
+    Scene3D::Material::TextureType::NORMAL,
+    Scene3D::Material::TextureType::OCCLUSION,
+    Scene3D::Material::TextureType::EMISSIVE,
+    Scene3D::Material::TextureType::SPECULAR,
+    Scene3D::Material::TextureType::SPECULAR_COLOR,
+};
+
+Vector4 GetTextureFactor(Scene3D::Loader::MaterialDefinition& materialDefinition, uint32_t semantic)
+{
+  Vector4 factor = Vector4::ONE;
+  switch(semantic)
+  {
+    case Scene3D::Loader::MaterialDefinition::ALBEDO:
+      factor = materialDefinition.mBaseColorFactor;
+      break;
+    case Scene3D::Loader::MaterialDefinition::METALLIC | Scene3D::Loader::MaterialDefinition::ROUGHNESS:
+      factor = Vector4(materialDefinition.mMetallic, materialDefinition.mRoughness, 0.0f, 0.0f);
+      break;
+    case Scene3D::Loader::MaterialDefinition::NORMAL:
+      factor.x = materialDefinition.mNormalScale;
+      break;
+    case Scene3D::Loader::MaterialDefinition::OCCLUSION:
+      factor.x = materialDefinition.mOcclusionStrength;
+      break;
+    case Scene3D::Loader::MaterialDefinition::EMISSIVE:
+      factor = materialDefinition.mEmissiveFactor;
+      break;
+    case Scene3D::Loader::MaterialDefinition::SPECULAR:
+      factor.x = materialDefinition.mSpecularFactor;
+      break;
+    case Scene3D::Loader::MaterialDefinition::SPECULAR_COLOR:
+      factor = materialDefinition.mSpecularColorFactor;
+      break;
+    default:
+      break;
+  }
+  return factor;
+}
+
 } // namespace
 
 namespace Scene3D
@@ -50,7 +110,7 @@ void NodeDefinition::Renderable::ReflectResources(IResourceReflector& reflector)
   reflector.Reflect(ResourceType::Shader, mShaderIdx);
 }
 
-void NodeDefinition::Renderable::OnCreate(const NodeDefinition& node, CreateParams& params, Actor& actor) const
+void NodeDefinition::Renderable::OnCreate(const NodeDefinition& nodeDefinition, CreateParams& params, ModelNode& node) const
 {
   DALI_ASSERT_DEBUG(mShaderIdx != INVALID_INDEX);
   auto&  resources = params.mResources;
@@ -61,45 +121,45 @@ void NodeDefinition::Renderable::OnCreate(const NodeDefinition& node, CreatePara
 
   RendererState::Apply(resources.mShaders[mShaderIdx].first.mRendererState, renderer);
 
-  actor.AddRenderer(renderer);
+  node.AddRenderer(renderer);
 }
 
-const std::string NodeDefinition::ORIGINAL_MATRIX_PROPERTY_NAME = "originalMatrix";
+const char* NodeDefinition::ORIGINAL_MATRIX_PROPERTY_NAME = "originalMatrix";
 
-Actor NodeDefinition::CreateActor(CreateParams& params)
+ModelNode NodeDefinition::CreateModelNode(CreateParams& params)
 {
-  Actor actor = Actor::New();
-  mNodeId     = actor.GetProperty<int32_t>(Dali::Actor::Property::ID);
+  ModelNode node = ModelNode::New();
+  mNodeId        = node.GetProperty<int32_t>(Dali::Actor::Property::ID);
 
-  SetActorCentered(actor);
+  SetActorCentered(node);
 
-  actor.SetProperty(Actor::Property::NAME, mName);
-  actor.SetProperty(Actor::Property::POSITION, mPosition);
-  actor.SetProperty(Actor::Property::ORIENTATION, mOrientation);
-  actor.SetProperty(Actor::Property::SCALE, mScale);
-  actor.SetProperty(Actor::Property::SIZE, mSize);
-  actor.SetProperty(Actor::Property::VISIBLE, mIsVisible);
+  node.SetProperty(Actor::Property::NAME, mName);
+  node.SetProperty(Actor::Property::POSITION, mPosition);
+  node.SetProperty(Actor::Property::ORIENTATION, mOrientation);
+  node.SetProperty(Actor::Property::SCALE, mScale);
+  node.SetProperty(Actor::Property::SIZE, mSize);
+  node.SetProperty(Actor::Property::VISIBLE, mIsVisible);
 
-  actor.RegisterProperty(ORIGINAL_MATRIX_PROPERTY_NAME, GetLocalSpace(), Property::AccessMode::READ_ONLY);
+  node.RegisterProperty(ORIGINAL_MATRIX_PROPERTY_NAME, GetLocalSpace(), Property::AccessMode::READ_ONLY);
 
-  actor.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
+  node.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
 
   for(auto& renderable : mRenderables)
   {
-    renderable->OnCreate(*this, params, actor);
+    renderable->OnCreate(*this, params, node);
   }
 
   for(auto& e : mExtras)
   {
-    actor.RegisterProperty(e.mKey, e.mValue);
+    node.RegisterProperty(e.mKey, e.mValue);
   }
 
   for(auto& c : mConstraints)
   {
-    params.mConstrainables.push_back(ConstraintRequest{&c, actor});
+    params.mConstrainables.push_back(ConstraintRequest{&c, node});
   }
 
-  return actor;
+  return node;
 }
 
 Matrix NodeDefinition::GetLocalSpace() const
@@ -190,32 +250,19 @@ void ModelRenderable::ReflectResources(IResourceReflector& reflector)
   reflector.Reflect(ResourceType::Material, mMaterialIdx);
 }
 
-void ModelRenderable::OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams& params, Actor& actor) const
+void ModelRenderable::OnCreate(const NodeDefinition& nodeDefinition, NodeDefinition::CreateParams& params, ModelNode& node) const
 {
   DALI_ASSERT_DEBUG(mMeshIdx != INVALID_INDEX);
-  Renderable::OnCreate(node, params, actor);
+  Renderable::OnCreate(nodeDefinition, params, node);
 
   auto& resources = params.mResources;
   auto& mesh      = resources.mMeshes[mMeshIdx];
 
-  auto     renderer = actor.GetRendererAt(actor.GetRendererCount() - 1u);
+  auto     renderer = node.GetRendererAt(node.GetRendererCount() - 1u);
   Geometry geometry = mesh.second.geometry;
   renderer.SetGeometry(geometry);
 
-  auto shader = renderer.GetShader();
-
-  if(mesh.first.IsSkinned())
-  {
-    params.mSkinnables.push_back(SkinningShaderConfigurationRequest{mesh.first.mSkeletonIdx, shader});
-  }
-
-  if(mesh.first.HasBlendShapes())
-  {
-    params.mBlendshapeRequests.push_back(BlendshapeShaderConfigurationRequest{node.mName, mMeshIdx, shader});
-  }
-
   TextureSet textures = resources.mMaterials[mMaterialIdx].second;
-
   // Set the blend shape texture.
   if(mesh.second.blendShapeGeometry)
   {
@@ -232,27 +279,54 @@ void ModelRenderable::OnCreate(const NodeDefinition& node, NodeDefinition::Creat
 
     textures = newTextureSet;
   }
+  renderer.SetTextures(textures);
+
+  {
+    mesh.first.mModelPrimitive = ModelPrimitive::New();
+    auto primitive            = mesh.first.mModelPrimitive;
+    GetImplementation(primitive).SetRenderer(renderer);
+
+    Index    envIndex         = resources.mMaterials[mMaterialIdx].first.mEnvironmentIdx;
+    uint32_t specularMipmap = resources.mEnvironmentMaps[envIndex].second.mSpecularMipmapLevels;
+    GetImplementation(primitive).SetImageBasedLightTexture(resources.mEnvironmentMaps[envIndex].second.mDiffuse,
+                                                           resources.mEnvironmentMaps[envIndex].second.mSpecular,
+                                                           resources.mEnvironmentMaps[envIndex].first.mIblIntensity,
+                                                           specularMipmap);
+
+    bool hasPositions = false;
+    bool hasNormals   = false;
+    bool hasTangents  = false;
+    mesh.first.RetrieveBlendShapeComponents(hasPositions, hasNormals, hasTangents);
+    GetImplementation(primitive).SetBlendShapeOptions(hasPositions, hasNormals, hasTangents);
+    GetImplementation(primitive).SetBlendShapeGeometry(mesh.second.blendShapeGeometry);
+    GetImplementation(primitive).SetSkinned(mesh.first.IsSkinned());
+  }
+
+  auto shader = renderer.GetShader();
+  if(mesh.first.IsSkinned())
+  {
+    params.mSkinnables.push_back(SkinningShaderConfigurationRequest{mesh.first.mSkeletonIdx, shader, mesh.first.mModelPrimitive});
+  }
 
-  renderer.RegisterProperty("uHasVertexColor", static_cast<float>(mesh.first.mColors.IsDefined()));
+  if(mesh.first.HasBlendShapes())
+  {
+    params.mBlendshapeRequests.push_back(BlendshapeShaderConfigurationRequest{nodeDefinition.mName, mMeshIdx, shader, mesh.first.mModelPrimitive});
+  }
 
   auto& matDef = resources.mMaterials[mMaterialIdx].first;
-  actor.RegisterProperty("uColorFactor", matDef.mBaseColorFactor);
-  actor.RegisterProperty("uMetallicFactor", matDef.mMetallic);
-  actor.RegisterProperty("uRoughnessFactor", matDef.mRoughness);
-  actor.RegisterProperty("uDielectricSpecular", matDef.mDielectricSpecular);
-  actor.RegisterProperty("uSpecularFactor", matDef.mSpecularFactor);
-  actor.RegisterProperty("uSpecularColorFactor", matDef.mSpecularColorFactor);
-  actor.RegisterProperty("uNormalScale", matDef.mNormalScale);
-  actor.RegisterProperty("uEmissiveFactor", matDef.mEmissiveFactor);
+  renderer.RegisterProperty("uColorFactor", matDef.mBaseColorFactor);
+  renderer.RegisterProperty("uMetallicFactor", matDef.mMetallic);
+  renderer.RegisterProperty("uRoughnessFactor", matDef.mRoughness);
+  renderer.RegisterProperty("uDielectricSpecular", matDef.mDielectricSpecular);
+  renderer.RegisterProperty("uSpecularFactor", matDef.mSpecularFactor);
+  renderer.RegisterProperty("uSpecularColorFactor", matDef.mSpecularColorFactor);
+  renderer.RegisterProperty("uNormalScale", matDef.mNormalScale);
+  renderer.RegisterProperty("uEmissiveFactor", matDef.mEmissiveFactor);
   if(matDef.mFlags & MaterialDefinition::OCCLUSION)
   {
     renderer.RegisterProperty("uOcclusionStrength", matDef.mOcclusionStrength);
   }
 
-  Index envIdx = matDef.mEnvironmentIdx;
-  renderer.RegisterProperty(IBL_INTENSITY_STRING.data(), resources.mEnvironmentMaps[envIdx].first.mIblIntensity);
-  renderer.RegisterProperty(IBL_Y_DIRECTION.data(), resources.mEnvironmentMaps[envIdx].first.mYDirection);
-
   float opaque      = matDef.mIsOpaque ? 1.0f : 0.0f;
   float mask        = matDef.mIsMask ? 1.0f : 0.0f;
   float alphaCutoff = matDef.GetAlphaCutoff();
@@ -261,28 +335,58 @@ void ModelRenderable::OnCreate(const NodeDefinition& node, NodeDefinition::Creat
   renderer.RegisterProperty("uMask", mask);
   renderer.RegisterProperty("uAlphaThreshold", alphaCutoff);
 
-  renderer.SetTextures(textures);
+  Index    envIndex         = matDef.mEnvironmentIdx;
+  uint32_t specularMipmap = resources.mEnvironmentMaps[envIndex].second.mSpecularMipmapLevels;
+  renderer.RegisterProperty(IBL_MAXLOD.data(), static_cast<float>(specularMipmap));
+  renderer.RegisterProperty(IBL_INTENSITY_STRING.data(), resources.mEnvironmentMaps[envIndex].first.mIblIntensity);
+  renderer.RegisterProperty(IBL_Y_DIRECTION.data(), resources.mEnvironmentMaps[envIndex].first.mYDirection);
+
+  node.SetProperty(Actor::Property::COLOR, mColor);
+
+  {
+    matDef.mMaterial = Material::New();
+    auto material    = matDef.mMaterial;
+    uint32_t textureIndexOffset = (mesh.second.blendShapeGeometry) ? 1 : 0;
+    uint32_t textureIndex       = 0;
+    for(uint32_t i = 0; i < MAX_NUMBER_OF_MATERIAL_TEXTURE; ++i)
+    {
+      Internal::Material::TextureInformation textureInformation;
+      if(matDef.CheckTextures(SEMANTICS[i]))
+      {
+        textureInformation.mTexture = textures.GetTexture(textureIndex + textureIndexOffset);
+        textureInformation.mSampler = textures.GetSampler(textureIndex + textureIndexOffset);
+        textureInformation.mUrl     = matDef.mTextureStages[textureIndex].mTexture.mDirectoryPath + matDef.mTextureStages[textureIndex].mTexture.mImageUri;
+        textureIndex++;
+      }
+      textureInformation.mFactor = GetTextureFactor(matDef, SEMANTICS[i]);
+      GetImplementation(material).SetTextureInformation(TEXTURE_TYPES[i], std::move(textureInformation));
+    }
+    material.SetProperty(Scene3D::Material::Property::ALPHA_MODE, matDef.mAlphaModeType);
+    material.SetProperty(Scene3D::Material::Property::ALPHA_CUTOFF, matDef.GetAlphaCutoff());
+    material.SetProperty(Scene3D::Material::Property::DOUBLE_SIDED, matDef.mDoubleSided);
+    material.SetProperty(Scene3D::Material::Property::IOR, matDef.mIor);
+    GetImplementation(mesh.first.mModelPrimitive).SetMaterial(material, false);
+    GetImplementation(material).ResetFlag();
+  }
 
-  uint32_t specularMipmap = resources.mEnvironmentMaps[envIdx].second.mSpecularMipmapLevels;
-  actor.SetProperty(Actor::Property::COLOR, mColor);
-  actor.RegisterProperty(IBL_MAXLOD.data(), static_cast<float>(specularMipmap));
+  node.AddModelPrimitive(mesh.first.mModelPrimitive);
 }
 
-void ArcRenderable::OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams& params, Actor& actor) const
+void ArcRenderable::OnCreate(const NodeDefinition& nodeDefinition, NodeDefinition::CreateParams& params, ModelNode& node) const
 {
-  ModelRenderable::OnCreate(node, params, actor);
+  ModelRenderable::OnCreate(nodeDefinition, params, node);
 
-  actor.RegisterProperty("antiAliasing", mAntiAliasing ? 1 : 0);
-  actor.RegisterProperty("arcCaps", mArcCaps);
-  actor.RegisterProperty("radius", mRadius);
+  node.RegisterProperty("antiAliasing", mAntiAliasing ? 1 : 0);
+  node.RegisterProperty("arcCaps", mArcCaps);
+  node.RegisterProperty("radius", mRadius);
 
   const float startAngleRadians = mStartAngleDegrees * Math::PI_OVER_180;
   Vector2     startPolar{std::cos(startAngleRadians), std::sin(startAngleRadians)};
-  actor.RegisterProperty("startAngle", startPolar);
+  node.RegisterProperty("startAngle", startPolar);
 
   const float endAngleRadians = mEndAngleDegrees * Math::PI_OVER_180;
   Vector2     endPolar{std::cos(endAngleRadians), std::sin(endAngleRadians)};
-  actor.RegisterProperty("endAngle", endPolar);
+  node.RegisterProperty("endAngle", endPolar);
 }
 
 void ArcRenderable::GetEndVectorWithDiffAngle(float startAngle, float diffAngle, Vector2& endVector)
index fe853ae..9b73271 100644 (file)
@@ -30,6 +30,7 @@
 #include <dali-scene3d/public-api/loader/customization.h>
 #include <dali-scene3d/public-api/loader/matrix-stack.h>
 #include <dali-scene3d/public-api/loader/resource-bundle.h>
+#include <dali-scene3d/public-api/model-components/model-node.h>
 
 namespace Dali
 {
@@ -101,8 +102,9 @@ struct DALI_SCENE3D_API Transforms
  */
 struct DALI_SCENE3D_API SkinningShaderConfigurationRequest
 {
-  Index  mSkeletonIdx;
-  Shader mShader;
+  Index          mSkeletonIdx;
+  Shader         mShader;
+  ModelPrimitive mPrimitive;
 
   bool operator<(const SkinningShaderConfigurationRequest& other) const
   {
@@ -115,9 +117,10 @@ struct DALI_SCENE3D_API SkinningShaderConfigurationRequest
  */
 struct DALI_SCENE3D_API BlendshapeShaderConfigurationRequest
 {
-  std::string mNodeName;
-  Index       mMeshIdx;
-  Shader      mShader;
+  std::string    mNodeName;
+  Index          mMeshIdx;
+  Shader         mShader;
+  ModelPrimitive mPrimitive;
 
   bool operator<(const BlendshapeShaderConfigurationRequest& other) const
   {
@@ -147,8 +150,8 @@ public: // TYPES
   struct CreateParams
   {
   public: // input
-    const ResourceBundle& mResources;
-    Transforms&           mXforms;
+    ResourceBundle& mResources;
+    Transforms&     mXforms;
 
   public: // output
     std::vector<ConstraintRequest>                    mConstrainables;
@@ -167,7 +170,7 @@ public: // TYPES
     virtual bool GetExtents(const ResourceBundle& resources, Vector3& min, Vector3& max) const;
     virtual void RegisterResources(IResourceReceiver& receiver) const;
     virtual void ReflectResources(IResourceReflector& reflector);
-    virtual void OnCreate(const NodeDefinition& node, CreateParams& params, Actor& actor) const;
+    virtual void OnCreate(const NodeDefinition& nodeDefinition, CreateParams& params, ModelNode& node) const;
   };
 
   struct CustomizationDefinition
@@ -215,10 +218,10 @@ public: // TYPES
 
 public: // METHODS
   /**
-   * @brief Creates a DALi Actor from this definition only.
+   * @brief Creates a ModelNode from this definition only.
    * @note Not recursive.
    */
-  Actor CreateActor(CreateParams& params);
+  ModelNode CreateModelNode(CreateParams& params);
 
   /**
    * @brief Gets local space matrix of this node
@@ -257,7 +260,7 @@ public: // METHODS
   static std::string_view GetIblMaxLodUniformName();
 
 public: // DATA
-  static const std::string ORIGINAL_MATRIX_PROPERTY_NAME;
+  static const char* ORIGINAL_MATRIX_PROPERTY_NAME;
 
   std::string mName;
   uint32_t    mNodeId = INVALID_INDEX;
@@ -289,7 +292,7 @@ public: // METHODS
   bool GetExtents(const ResourceBundle& resources, Vector3& min, Vector3& max) const override;
   void RegisterResources(IResourceReceiver& receiver) const override;
   void ReflectResources(IResourceReflector& reflector) override;
-  void OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams& params, Actor& actor) const override;
+  void OnCreate(const NodeDefinition& nodeDefinition, NodeDefinition::CreateParams& params, ModelNode& node) const override;
 };
 
 /**
@@ -307,7 +310,7 @@ public: // DATA
 public: // METHODS
   static void GetEndVectorWithDiffAngle(float startAngle, float endAngle, Vector2& endVector);
 
-  void OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams& params, Actor& actor) const override;
+  void OnCreate(const NodeDefinition& nodeDefinition, NodeDefinition::CreateParams& params, ModelNode& node) const override;
 };
 
 } // namespace Loader
index 30b4d44..63b7d58 100644 (file)
@@ -28,112 +28,129 @@ namespace RendererState
 {
 namespace
 {
-std::map<std::string_view, Type> COMPARISONS{
-#define DECL_COMPARISON(x) {#x, Comparison::x}
-  DECL_COMPARISON(NEVER),
-  DECL_COMPARISON(ALWAYS),
-  DECL_COMPARISON(LESS),
-  DECL_COMPARISON(GREATER),
-  DECL_COMPARISON(EQUAL),
-  DECL_COMPARISON(NOT_EQUAL),
-  DECL_COMPARISON(LESS_EQUAL),
-  DECL_COMPARISON(GREATER_EQUAL),
-#undef DECL_COMPARISON
-};
+const std::map<std::string_view, Type>& GetComparisons()
+{
+  static const std::map<std::string_view, Type> COMPARISONS{
+  #define DECL_COMPARISON(x) {#x, Comparison::x}
+    DECL_COMPARISON(NEVER),
+    DECL_COMPARISON(ALWAYS),
+    DECL_COMPARISON(LESS),
+    DECL_COMPARISON(GREATER),
+    DECL_COMPARISON(EQUAL),
+    DECL_COMPARISON(NOT_EQUAL),
+    DECL_COMPARISON(LESS_EQUAL),
+    DECL_COMPARISON(GREATER_EQUAL),
+  #undef DECL_COMPARISON
+  };
+  return COMPARISONS;
+}
 
 Type InterpretComparison(const std::string_view& str)
 {
   Type value = 0x0;
-  auto iFind = COMPARISONS.find(str);
-  if(iFind != COMPARISONS.end())
+  auto iFind = GetComparisons().find(str);
+  if(iFind != GetComparisons().end())
   {
     value = iFind->second;
   }
   return value;
 }
 
-std::map<std::string_view, Type> BLEND_FACTORS{
-#define DECL_BLEND_FACTOR(x) {#x, Dali::Scene3D::Loader::BlendFactor::x}
-  DECL_BLEND_FACTOR(ZERO),
-  DECL_BLEND_FACTOR(ONE),
-  DECL_BLEND_FACTOR(SRC_COLOR),
-  DECL_BLEND_FACTOR(ONE_MINUS_SRC_COLOR),
-  DECL_BLEND_FACTOR(SRC_ALPHA),
-  DECL_BLEND_FACTOR(ONE_MINUS_SRC_ALPHA),
-  DECL_BLEND_FACTOR(DST_ALPHA),
-  DECL_BLEND_FACTOR(ONE_MINUS_DST_ALPHA),
-  DECL_BLEND_FACTOR(DST_COLOR),
-  DECL_BLEND_FACTOR(ONE_MINUS_DST_COLOR),
-  DECL_BLEND_FACTOR(SRC_ALPHA_SATURATE),
-  DECL_BLEND_FACTOR(CONSTANT_COLOR),
-  DECL_BLEND_FACTOR(ONE_MINUS_CONSTANT_COLOR),
-  DECL_BLEND_FACTOR(CONSTANT_ALPHA),
-  DECL_BLEND_FACTOR(ONE_MINUS_CONSTANT_ALPHA),
-#undef DECL_BLEND_FACTOR
-};
+const std::map<std::string_view, Type>& GetBlendFactors()
+{
+  static const std::map<std::string_view, Type> BLEND_FACTORS{
+  #define DECL_BLEND_FACTOR(x) {#x, Dali::Scene3D::Loader::BlendFactor::x}
+    DECL_BLEND_FACTOR(ZERO),
+    DECL_BLEND_FACTOR(ONE),
+    DECL_BLEND_FACTOR(SRC_COLOR),
+    DECL_BLEND_FACTOR(ONE_MINUS_SRC_COLOR),
+    DECL_BLEND_FACTOR(SRC_ALPHA),
+    DECL_BLEND_FACTOR(ONE_MINUS_SRC_ALPHA),
+    DECL_BLEND_FACTOR(DST_ALPHA),
+    DECL_BLEND_FACTOR(ONE_MINUS_DST_ALPHA),
+    DECL_BLEND_FACTOR(DST_COLOR),
+    DECL_BLEND_FACTOR(ONE_MINUS_DST_COLOR),
+    DECL_BLEND_FACTOR(SRC_ALPHA_SATURATE),
+    DECL_BLEND_FACTOR(CONSTANT_COLOR),
+    DECL_BLEND_FACTOR(ONE_MINUS_CONSTANT_COLOR),
+    DECL_BLEND_FACTOR(CONSTANT_ALPHA),
+    DECL_BLEND_FACTOR(ONE_MINUS_CONSTANT_ALPHA),
+  #undef DECL_BLEND_FACTOR
+  };
+  return BLEND_FACTORS;
+}
 
 Type InterpretBlendFactor(const std::string_view& str, uint8_t item)
 {
   Type value = 0x0;
-  auto iFind = BLEND_FACTORS.find(str);
-  if(iFind != BLEND_FACTORS.end())
+  auto iFind = GetBlendFactors().find(str);
+  if(iFind != GetBlendFactors().end())
   {
     value = iFind->second << (BLEND_FACTOR_BASE_SHIFT + BLEND_FACTOR_ITEM_BITS * item);
   }
   return value;
 }
 
-std::map<std::string_view, Type> BUFFER_MODES{
-#define DECL_BUFFER_MODE(x) {#x, BufferMode::x}
-  DECL_BUFFER_MODE(NONE),
-  DECL_BUFFER_MODE(AUTO),
-  DECL_BUFFER_MODE(COLOR),
-  DECL_BUFFER_MODE(STENCIL),
-  DECL_BUFFER_MODE(COLOR_STENCIL),
-};
+const std::map<std::string_view, Type>& GetBufferModes()
+{
+  static const std::map<std::string_view, Type> BUFFER_MODES{
+  #define DECL_BUFFER_MODE(x) {#x, BufferMode::x}
+    DECL_BUFFER_MODE(NONE),
+    DECL_BUFFER_MODE(AUTO),
+    DECL_BUFFER_MODE(COLOR),
+    DECL_BUFFER_MODE(STENCIL),
+    DECL_BUFFER_MODE(COLOR_STENCIL),
+  #undef DECL_BUFFER_MODE
+  };
+  return BUFFER_MODES;
+}
 
 Type InterpretBufferMode(const std::string_view& str)
 {
   Type value = 0x0;
-  auto iFind = BUFFER_MODES.find(str);
-  if(iFind != BUFFER_MODES.end())
+  auto iFind = GetBufferModes().find(str);
+  if(iFind != GetBufferModes().end())
   {
     value = iFind->second << BUFFER_MODE_SHIFT;
   }
   return value;
 }
 
-std::map<std::string_view, Type (*)(const std::string_view&)> RENDERER_STATE_PROCESSORS{
-  {"DEPTH_WRITE", [](const std::string_view&) -> Type { return DEPTH_WRITE; }},
-  {"DEPTH_TEST", [](const std::string_view&) -> Type { return DEPTH_TEST; }},
-  {"CULL_FRONT", [](const std::string_view&) -> Type { return CULL_FRONT; }},
-  {"CULL_BACK", [](const std::string_view&) -> Type { return CULL_BACK; }},
-  {"ALPHA_BLEND", [](const std::string_view&) -> Type { return ALPHA_BLEND; }},
-  {"DEPTH_FUNC", [](const std::string_view& arg) -> Type {
-     Type value = (arg[0] == ':') ? (InterpretComparison(std::string_view(arg.data() + 1, arg.size() - 1)) << DEPTH_FUNCTION_SHIFT) : 0x0;
-     return value;
-   }},
-  {"BLEND_SRC_RGB", [](const std::string_view& arg) -> Type {
-     Type value = (arg[0] == ':') ? InterpretBlendFactor(std::string_view(arg.data() + 1, arg.size() - 1), 0) : 0x0;
-     return value;
-   }},
-  {"BLEND_DST_RGB", [](const std::string_view& arg) -> Type {
-     Type value = (arg[0] == ':') ? InterpretBlendFactor(std::string_view(arg.data() + 1, arg.size() - 1), 1) : 0x0;
-     return value;
-   }},
-  {"BLEND_SRC_ALPHA", [](const std::string_view& arg) -> Type {
-     Type value = (arg[0] == ':') ? InterpretBlendFactor(std::string_view(arg.data() + 1, arg.size() - 1), 2) : 0x0;
-     return value;
-   }},
-  {"BLEND_DST_ALPHA", [](const std::string_view& arg) -> Type {
-     Type value = (arg[0] == ':') ? InterpretBlendFactor(std::string_view(arg.data() + 1, arg.size() - 1), 3) : 0x0;
-     return value;
-   }},
-  {"BUFFER_MODE", [](const std::string_view& arg) -> Type {
-     Type value = (arg[0] == ':') ? InterpretBufferMode(std::string_view(arg.data() + 1, arg.size() - 1)) : 0x0;
-     return value;
-   }},
-};
+const std::map<std::string_view, Type (*)(const std::string_view&)>& GetRendererStateProcessors()
+{
+  static const std::map<std::string_view, Type (*)(const std::string_view&)> RENDERER_STATE_PROCESSORS{
+    {"DEPTH_WRITE", [](const std::string_view&) -> Type { return DEPTH_WRITE; }},
+    {"DEPTH_TEST", [](const std::string_view&) -> Type { return DEPTH_TEST; }},
+    {"CULL_FRONT", [](const std::string_view&) -> Type { return CULL_FRONT; }},
+    {"CULL_BACK", [](const std::string_view&) -> Type { return CULL_BACK; }},
+    {"ALPHA_BLEND", [](const std::string_view&) -> Type { return ALPHA_BLEND; }},
+    {"DEPTH_FUNC", [](const std::string_view& arg) -> Type {
+       Type value = (arg[0] == ':') ? (InterpretComparison(std::string_view(arg.data() + 1, arg.size() - 1)) << DEPTH_FUNCTION_SHIFT) : 0x0;
+       return value;
+     }},
+    {"BLEND_SRC_RGB", [](const std::string_view& arg) -> Type {
+       Type value = (arg[0] == ':') ? InterpretBlendFactor(std::string_view(arg.data() + 1, arg.size() - 1), 0) : 0x0;
+       return value;
+     }},
+    {"BLEND_DST_RGB", [](const std::string_view& arg) -> Type {
+       Type value = (arg[0] == ':') ? InterpretBlendFactor(std::string_view(arg.data() + 1, arg.size() - 1), 1) : 0x0;
+       return value;
+     }},
+    {"BLEND_SRC_ALPHA", [](const std::string_view& arg) -> Type {
+       Type value = (arg[0] == ':') ? InterpretBlendFactor(std::string_view(arg.data() + 1, arg.size() - 1), 2) : 0x0;
+       return value;
+     }},
+    {"BLEND_DST_ALPHA", [](const std::string_view& arg) -> Type {
+       Type value = (arg[0] == ':') ? InterpretBlendFactor(std::string_view(arg.data() + 1, arg.size() - 1), 3) : 0x0;
+       return value;
+     }},
+    {"BUFFER_MODE", [](const std::string_view& arg) -> Type {
+       Type value = (arg[0] == ':') ? InterpretBufferMode(std::string_view(arg.data() + 1, arg.size() - 1)) : 0x0;
+       return value;
+     }},
+  };
+  return RENDERER_STATE_PROCESSORS;
+}
 
 } // namespace
 
@@ -150,8 +167,8 @@ Type Parse(const char* string, size_t length, StringCallback onError)
   {
     auto iNextToken = std::find(string, iEnd, '|');
     auto iColon     = std::find(string, iNextToken, ':');
-    auto i          = RENDERER_STATE_PROCESSORS.find(std::string_view(string, iColon - string));
-    if(i != RENDERER_STATE_PROCESSORS.end() && size_t(std::distance(string, iNextToken)) >= i->first.size())
+    auto i          = GetRendererStateProcessors().find(std::string_view(string, iColon - string));
+    if(i != GetRendererStateProcessors().end() && size_t(std::distance(string, iNextToken)) >= i->first.size())
     {
       value |= i->second(std::string_view(string + i->first.size(), iNextToken - iColon));
     }
index 65a6269..2379fa6 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef DALI_SCENE3D_LOADERERER_INTERPRET_RENDERER_STATE_H
-#define DALI_SCENE3D_LOADERERER_INTERPRET_RENDERER_STATE_H
+#ifndef DALI_SCENE3D_LOADERER_INTERPRET_RENDERER_STATE_H
+#define DALI_SCENE3D_LOADERER_INTERPRET_RENDERER_STATE_H
 /*
  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
@@ -43,4 +43,4 @@ DALI_SCENE3D_API Type Parse(const char* string, size_t length = 0, StringCallbac
 } // namespace RendererState
 } // namespace Dali::Scene3D::Loader
 
-#endif //DALI_SCENE3D_LOADERERER_INTERPRET_RENDERER_STATE_H
+#endif //DALI_SCENE3D_LOADERER_INTERPRET_RENDERER_STATE_H
index fe386ca..08dea60 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef DALI_SCENE3D_LOADERERERERER_RESOURCE_BUNDLE_H_
-#define DALI_SCENE3D_LOADERERERERER_RESOURCE_BUNDLE_H_
+#ifndef DALI_SCENE3D_LOADERERERER_RESOURCE_BUNDLE_H_
+#define DALI_SCENE3D_LOADERERERER_RESOURCE_BUNDLE_H_
 /*
  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
@@ -158,4 +158,4 @@ public: // DATA
 
 } // namespace Dali::Scene3D::Loader
 
-#endif //DALI_SCENE3D_LOADERERERERER_RESOURCE_BUNDLE_H_
+#endif //DALI_SCENE3D_LOADERERERER_RESOURCE_BUNDLE_H_
index 96420e5..8cc5385 100644 (file)
 
 // INTERNAL
 #include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-scene3d/internal/model-components/model-node-impl.h>
 #include <dali-scene3d/public-api/loader/blend-shape-details.h>
 #include <dali-scene3d/public-api/loader/skinning-details.h>
 #include <dali-scene3d/public-api/loader/utils.h>
 
-//#define DEBUG_SCENE_DEFINITION
-//#define DEBUG_JOINTS
+// #define DEBUG_SCENE_DEFINITION
+// #define DEBUG_JOINTS
 
 #if defined(DEBUG_SCENE_DEFINITION) || defined(DEBUG_JOINTS)
 #define DEBUG_ONLY(x) x
@@ -43,50 +44,53 @@ namespace Dali::Scene3D::Loader
 {
 namespace
 {
-const std::string JOINT_MATRIX{"jointMatrix"};
-
-const std::map<Property::Type, Constraint (*)(Actor&, Property::Index)> sConstraintFactory = {
-  {Property::Type::BOOLEAN,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<bool>(a, i, [](bool& current, const PropertyInputContainer& inputs) {
-       current = inputs[0]->GetBoolean();
-     });
-   }},
-  {Property::Type::INTEGER,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<int>(a, i, [](int& current, const PropertyInputContainer& inputs) {
-       current = inputs[0]->GetInteger();
-     });
-   }},
-  {Property::Type::FLOAT,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<float>(a, i, EqualToConstraint());
-   }},
-  {Property::Type::VECTOR2,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<Vector2>(a, i, EqualToConstraint());
-   }},
-  {Property::Type::VECTOR3,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<Vector3>(a, i, EqualToConstraint());
-   }},
-  {Property::Type::VECTOR4,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<Vector4>(a, i, EqualToConstraint());
-   }},
-  {Property::Type::MATRIX,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<Matrix>(a, i, EqualToConstraint());
-   }},
-  {Property::Type::MATRIX3,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<Matrix3>(a, i, EqualToConstraint());
-   }},
-  {Property::Type::ROTATION,
-   [](Actor& a, Property::Index i) {
-     return Constraint::New<Quaternion>(a, i, EqualToConstraint());
-   }},
-};
+
+const std::map<Property::Type, Constraint (*)(Actor&, Property::Index)>& GetConstraintFactory()
+{
+  static const std::map<Property::Type, Constraint (*)(Actor&, Property::Index)> sConstraintFactory = {
+    {Property::Type::BOOLEAN,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<bool>(a, i, [](bool& current, const PropertyInputContainer& inputs) {
+         current = inputs[0]->GetBoolean();
+       });
+     }},
+    {Property::Type::INTEGER,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<int>(a, i, [](int& current, const PropertyInputContainer& inputs) {
+         current = inputs[0]->GetInteger();
+       });
+     }},
+    {Property::Type::FLOAT,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<float>(a, i, EqualToConstraint());
+     }},
+    {Property::Type::VECTOR2,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<Vector2>(a, i, EqualToConstraint());
+     }},
+    {Property::Type::VECTOR3,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<Vector3>(a, i, EqualToConstraint());
+     }},
+    {Property::Type::VECTOR4,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<Vector4>(a, i, EqualToConstraint());
+     }},
+    {Property::Type::MATRIX,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<Matrix>(a, i, EqualToConstraint());
+     }},
+    {Property::Type::MATRIX3,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<Matrix3>(a, i, EqualToConstraint());
+     }},
+    {Property::Type::ROTATION,
+     [](Actor& a, Property::Index i) {
+       return Constraint::New<Quaternion>(a, i, EqualToConstraint());
+     }},
+  };
+  return sConstraintFactory;
+}
 
 struct ResourceReflector : IResourceReflector
 {
@@ -174,7 +178,7 @@ public:
   {
     mCreationContext.mXforms.modelStack.Push(n.GetLocalSpace());
 
-    Actor a = n.CreateActor(mCreationContext);
+    ModelNode a = n.CreateModelNode(mCreationContext);
     if(!mActorStack.empty())
     {
       mActorStack.back().Add(a);
@@ -192,15 +196,15 @@ public:
     mCreationContext.mXforms.modelStack.Pop();
   }
 
-  Actor GetRoot() const
+  ModelNode GetRoot() const
   {
     return mRoot;
   }
 
 private:
   NodeDefinition::CreateParams& mCreationContext;
-  std::vector<Actor>            mActorStack;
-  Actor                         mRoot;
+  std::vector<ModelNode>            mActorStack;
+  ModelNode                         mRoot;
 };
 
 bool IsAncestor(const SceneDefinition& scene, Index ancestor, Index node, Index rootHint = INVALID_INDEX)
@@ -240,10 +244,10 @@ Property::Index ConfigureJointMatrix(Actor actor, Actor ancestor, Property::Inde
     propJointMatrix = ConfigureJointMatrix(parent, ancestor, propJointMatrix);
   }
 
-  auto myPropJointMatrix = actor.GetPropertyIndex(JOINT_MATRIX);
+  auto myPropJointMatrix = actor.GetPropertyIndex(Skinning::JOINT_MATRIX);
   if(myPropJointMatrix == Property::INVALID_INDEX)
   {
-    myPropJointMatrix     = actor.RegisterProperty(JOINT_MATRIX, Matrix{false});
+    myPropJointMatrix     = actor.RegisterProperty(Skinning::JOINT_MATRIX, Matrix{false});
     Constraint constraint = Constraint::New<Matrix>(actor, propJointMatrix, [](Matrix& output, const PropertyInputContainer& inputs) {
       Matrix jointMatrix{false};
       jointMatrix.SetTransformComponents(Vector3::ONE, inputs[0]->GetQuaternion(), inputs[1]->GetVector3());
@@ -299,23 +303,10 @@ void SortAndDeduplicateSkinningRequests(std::vector<SkinningShaderConfigurationR
                  requests.end());
 }
 
-void ConfigureBoneMatrix(const Matrix& ibm, Actor joint, Shader& shader, Index& boneIdx)
+void ConfigureBoneMatrix(const Matrix& ibm, ModelNode joint, ModelPrimitive primitive, Index& boneIdx)
 {
   // Register bone transform on shader.
-  char propertyNameBuffer[32];
-  snprintf(propertyNameBuffer, sizeof(propertyNameBuffer), "%s[%d]", Skinning::BONE_UNIFORM_NAME.c_str(), boneIdx);
-  DALI_ASSERT_DEBUG(shader.GetPropertyIndex(propertyNameBuffer) == Property::INVALID_INDEX);
-  auto propBoneXform = shader.RegisterProperty(propertyNameBuffer, Matrix{false});
-
-  // Constrain bone matrix to joint transform.
-  Constraint constraint = Constraint::New<Matrix>(shader, propBoneXform, [ibm](Matrix& output, const PropertyInputContainer& inputs) {
-    Matrix::Multiply(output, ibm, inputs[0]->GetMatrix());
-  });
-
-  auto propJointMatrix = joint.GetPropertyIndex(JOINT_MATRIX);
-  constraint.AddSource(Source{joint, propJointMatrix});
-  constraint.Apply();
-
+  Internal::GetImplementation(joint).SetBoneMatrix(ibm, primitive, boneIdx);
   ++boneIdx;
 }
 
@@ -471,7 +462,7 @@ void SceneDefinition::CountResourceRefs(Index iNode, const Customization::Choice
   Visit(iNode, choices, refCounterVisitor);
 }
 
-Actor SceneDefinition::CreateNodes(Index iNode, const Customization::Choices& choices, NodeDefinition::CreateParams& params)
+ModelNode SceneDefinition::CreateNodes(Index iNode, const Customization::Choices& choices, NodeDefinition::CreateParams& params)
 {
   ActorCreatorVisitor actorCreatorVisitor(params);
 
@@ -777,8 +768,8 @@ void SceneDefinition::ApplyConstraints(Actor&                           root,
     if(iTarget != Property::INVALID_INDEX)
     {
       auto propertyType = cr.mTarget.GetPropertyType(iTarget);
-      auto iFind        = sConstraintFactory.find(propertyType);
-      if(iFind == sConstraintFactory.end())
+      auto iFind        = GetConstraintFactory().find(propertyType);
+      if(iFind == GetConstraintFactory().end())
       {
         onError(FormatString("node '%s': Property '%s' has unsupported type '%s'; ignored.",
                              sourceName,
@@ -965,8 +956,8 @@ void SceneDefinition::ConfigureSkeletonJoints(uint32_t iRoot, const SkeletonDefi
     auto rootJoint = root.FindChildByName(node->mName);
     DALI_ASSERT_ALWAYS(!!rootJoint);
 
-    DALI_ASSERT_DEBUG(rootJoint.GetPropertyIndex(JOINT_MATRIX) == Property::INVALID_INDEX);
-    auto       propJointMatrix = rootJoint.RegisterProperty(JOINT_MATRIX, Matrix{false});
+    DALI_ASSERT_DEBUG(rootJoint.GetPropertyIndex(Skinning::JOINT_MATRIX) == Property::INVALID_INDEX);
+    auto       propJointMatrix = rootJoint.RegisterProperty(Skinning::JOINT_MATRIX, Matrix{false});
     Constraint constraint      = Constraint::New<Matrix>(rootJoint, propJointMatrix, [](Matrix& output, const PropertyInputContainer& inputs) {
       output.SetTransformComponents(Vector3::ONE, inputs[0]->GetQuaternion(), inputs[1]->GetVector3());
     });
@@ -1055,8 +1046,12 @@ void SceneDefinition::ConfigureSkinningShaders(const ResourceBundle&
     for(auto& joint : skeleton.mJoints)
     {
       auto  node  = GetNode(joint.mNodeIdx);
-      Actor actor = rootActor.FindChildByName(node->mName);
-      ConfigureBoneMatrix(joint.mInverseBindMatrix, actor, request.mShader, boneIdx);
+      ModelNode modelNode = ModelNode::DownCast(rootActor.FindChildByName(node->mName));
+      if(!modelNode)
+      {
+        continue;
+      }
+      ConfigureBoneMatrix(joint.mInverseBindMatrix, modelNode, request.mPrimitive, boneIdx);
     }
   }
 }
@@ -1111,9 +1106,27 @@ bool SceneDefinition::ConfigureBlendshapeShaders(const ResourceBundle&
       if(mesh.first.HasBlendShapes())
       {
         Actor actor = rootActor.FindChildByName(node->mName);
-
-        // Sets the property to be animated.
-        BlendShapes::ConfigureProperties(mesh, i.mShader, actor);
+        Scene3D::ModelNode node = Scene3D::ModelNode::DownCast(actor);
+        if(!node)
+        {
+          continue;
+        }
+        BlendShapes::BlendShapeData data;
+        data.components = 0x0;
+        for(auto&& blendShape : mesh.first.mBlendShapes)
+        {
+          data.weights.push_back(blendShape.weight);
+          data.components |= (blendShape.deltas.IsDefined() * BlendShapes::Component::POSITIONS) |
+                             (blendShape.normals.IsDefined() * BlendShapes::Component::NORMALS) | (blendShape.tangents.IsDefined() * BlendShapes::Component::TANGENTS);
+        }
+        for(auto&& factor : mesh.second.blendShapeUnnormalizeFactor)
+        {
+          data.unnormalizeFactors.push_back(factor);
+        }
+        data.version = mesh.first.mBlendShapeVersion;
+        data.bufferOffset = mesh.second.blendShapeBufferOffset;
+        data.mActor = actor;
+        Internal::GetImplementation(node).SetBlendShapeData(data, i.mPrimitive);
       }
     }
   }
index d1ec3f1..06d24db 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef DALI_SCENE3D_LOADERER_SCENE_DEFINITION_H_
-#define DALI_SCENE3D_LOADERER_SCENE_DEFINITION_H_
+#ifndef DALI_SCENE3D_LOADER_SCENE_DEFINITION_H_
+#define DALI_SCENE3D_LOADER_SCENE_DEFINITION_H_
 /*
  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
@@ -105,11 +105,11 @@ public: // METHODS
 
   /*
    * @brief Given a bundle of @a resources that are loaded, and customization
-   *  @a choices, this method traverses the scene, creating the actors and renderers
+   *  @a choices, this method traverses the scene, creating the ModelNodes and renderers
    *  from node definitions.
-   * @return Handle to the root actor.
+   * @return Handle to the root node.
    */
-  Actor CreateNodes(Index iNode, const Customization::Choices& choices, NodeDefinition::CreateParams& params);
+  ModelNode CreateNodes(Index iNode, const Customization::Choices& choices, NodeDefinition::CreateParams& params);
 
   /*
    * @brief Creates / update a registry of mappings from customization tags to
@@ -272,4 +272,4 @@ private:                                               // DATA
 
 } // namespace Dali::Scene3D::Loader
 
-#endif //DALI_SCENE3D_LOADERER_SCENE_DEFINITION_H_
+#endif //DALI_SCENE3D_LOADER_SCENE_DEFINITION_H_
index a473af1..0d1e346 100644 (file)
@@ -60,16 +60,6 @@ struct ResourceReceiver : IResourceReceiver
   }
 };
 
-void RetrieveBlendShapeComponents(const std::vector<MeshDefinition::BlendShape>& blendShapes, bool& hasPositions, bool& hasNormals, bool& hasTangents)
-{
-  for(const auto& blendShape : blendShapes)
-  {
-    hasPositions = hasPositions || blendShape.deltas.IsDefined();
-    hasNormals   = hasNormals || blendShape.normals.IsDefined();
-    hasTangents  = hasTangents || blendShape.tangents.IsDefined();
-  }
-}
-
 uint64_t HashNode(const MaterialDefinition& materialDef, const MeshDefinition& meshDef)
 {
   Hash hash;
@@ -161,7 +151,7 @@ uint64_t HashNode(const MaterialDefinition& materialDef, const MeshDefinition& m
     bool hasPositions = false;
     bool hasNormals   = false;
     bool hasTangents  = false;
-    RetrieveBlendShapeComponents(meshDef.mBlendShapes, hasPositions, hasNormals, hasTangents);
+    meshDef.RetrieveBlendShapeComponents(hasPositions, hasNormals, hasTangents);
     if(hasPositions)
     {
       hash.Add("MORPHPOS");
@@ -335,7 +325,7 @@ Index ShaderDefinitionFactory::ProduceShader(NodeDefinition::Renderable& rendera
       bool hasPositions = false;
       bool hasNormals   = false;
       bool hasTangents  = false;
-      RetrieveBlendShapeComponents(meshDef.mBlendShapes, hasPositions, hasNormals, hasTangents);
+      meshDef.RetrieveBlendShapeComponents(hasPositions, hasNormals, hasTangents);
 
       if(hasPositions)
       {
index 25544cd..2796340 100644 (file)
@@ -26,9 +26,8 @@ namespace Dali::Scene3D::Loader
 {
 namespace
 {
-const std::string SHADER_HINT_OUTPUT_IS_TRANSPARENT("OUTPUT_IS_TRANSPARENT"); ///< Might generate transparent alpha from opaque inputs.
-const std::string SHADER_HINT_MODIFIES_GEOMETRY("MODIFIES_GEOMETRY");         ///< Might change position of vertices, this option disables any culling optimizations.
-
+const char* SHADER_HINT_OUTPUT_IS_TRANSPARENT("OUTPUT_IS_TRANSPARENT"); ///< Might generate transparent alpha from opaque inputs.
+const char* SHADER_HINT_MODIFIES_GEOMETRY("MODIFIES_GEOMETRY");         ///< Might change position of vertices, this option disables any culling optimizations.
 } // namespace
 
 ShaderDefinition::ShaderDefinition(const ShaderDefinition& other)
index 7161a34..0fca023 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef DALI_SCENE3D_LOADERER_SKELETON_H
-#define DALI_SCENE3D_LOADERER_SKELETON_H
+#ifndef DALI_SCENE3D_LOADER_SKELETON_H
+#define DALI_SCENE3D_LOADER_SKELETON_H
 /*
  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
@@ -48,4 +48,4 @@ struct DALI_SCENE3D_API SkeletonDefinition
 
 } // namespace Dali::Scene3D::Loader
 
-#endif //DALI_SCENE3D_LOADERER_SKELETON_H
+#endif //DALI_SCENE3D_LOADER_SKELETON_H
index 941bf57..fc18d40 100644 (file)
@@ -27,5 +27,6 @@ namespace Dali::Scene3D::Loader
 {
 const unsigned int Skinning::MAX_JOINTS = 64;
 
-const std::string Skinning::BONE_UNIFORM_NAME = "uBone";
+const char* Skinning::BONE_UNIFORM_NAME = "uBone";
+const char* Skinning::JOINT_MATRIX      = "jointMatrix";
 } // namespace Dali::Scene3D::Loader
index ab04423..ca0bdc2 100644 (file)
 
 // EXTERNAL INCLUDES
 #include <dali/public-api/rendering/shader.h>
+#include <dali/public-api/animation/constraint.h>
 #include <string>
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/public-api/api.h>
+#include <dali-scene3d/public-api/loader/index.h>
+#include <dali-scene3d/public-api/model-components/model-primitive.h>
 
 namespace Dali::Scene3D::Loader
 {
 struct DALI_SCENE3D_API Skinning
 {
+public:
+  struct BoneData
+  {
+    Dali::Scene3D::ModelPrimitive primitive;
+    Dali::Constraint              constraint;
+    Scene3D::Loader::Index        boneIndex;
+    std::string                   propertyName;
+    Matrix                        inverseMatrix;
+  };
+
   /*
    * @brief Upper limit on the number of joints supported.
    */
@@ -36,7 +49,8 @@ struct DALI_SCENE3D_API Skinning
   /*
    * @brief Name of bone matrix uniform (array).
    */
-  static const std::string BONE_UNIFORM_NAME;
+  static const char* BONE_UNIFORM_NAME;
+  static const char* JOINT_MATRIX;
 
   Skinning() = delete;
 };
diff --git a/dali-scene3d/public-api/model-components/material.cpp b/dali-scene3d/public-api/model-components/material.cpp
new file mode 100644 (file)
index 0000000..9b2fb48
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2023 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/public-api/model-components/material.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/rendering/sampler.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/model-components/material-impl.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+Material Material::New()
+{
+  Internal::MaterialPtr internal = Internal::Material::New();
+
+  return Material(internal.Get());
+}
+
+Material::Material()
+{
+}
+
+Material::Material(const Material& mateiral) = default;
+
+Material::Material(Material&& rhs) = default;
+
+Material::~Material()
+{
+}
+
+Material& Material::operator=(const Material& handle) = default;
+
+Material& Material::operator=(Material&& rhs) = default;
+
+Material Material::DownCast(BaseHandle handle)
+{
+  return Material(dynamic_cast<Dali::Scene3D::Internal::Material*>(handle.GetObjectPtr()));
+}
+
+Material::Material(Dali::Scene3D::Internal::Material* internal)
+: BaseHandle(internal)
+{
+}
+
+// Public Method
+
+void Material::SetProperty(Dali::Property::Index index, Dali::Property::Value propertyValue)
+{
+  GetImplementation(*this).SetProperty(index, propertyValue);
+}
+
+Dali::Property::Value Material::GetProperty(Dali::Property::Index index) const
+{
+  return GetImplementation(*this).GetProperty(index);
+}
+
+void Material::SetTexture(Scene3D::Material::TextureType index, Dali::Texture texture)
+{
+  GetImplementation(*this).SetTexture(index, texture);
+}
+
+Dali::Texture Material::GetTexture(Scene3D::Material::TextureType index)
+{
+  return GetImplementation(*this).GetTexture(index);
+}
+
+void Material::SetSampler(Scene3D::Material::TextureType index, Dali::Sampler sampler)
+{
+  GetImplementation(*this).SetSampler(index, sampler);
+}
+
+Dali::Sampler Material::GetSampler(Scene3D::Material::TextureType index)
+{
+  return GetImplementation(*this).GetSampler(index);
+}
+
+} // namespace Scene3D
+
+} // namespace Dali
diff --git a/dali-scene3d/public-api/model-components/material.h b/dali-scene3d/public-api/model-components/material.h
new file mode 100644 (file)
index 0000000..c48edbf
--- /dev/null
@@ -0,0 +1,473 @@
+#ifndef DALI_SCENE3D_MODEL_COMPONENTS_MATERIAL_H
+#define DALI_SCENE3D_MODEL_COMPONENTS_MATERIAL_H
+
+/*
+ * Copyright (c) 2023 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/dali-common.h>
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/object/property-index-ranges.h>
+#include <dali/public-api/object/property-value.h>
+#include <dali/public-api/object/property.h>
+#include <dali/public-api/rendering/sampler.h>
+#include <dali/public-api/rendering/texture.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/api.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+// Forward declarations.
+namespace Internal
+{
+class Material;
+} // namespace Internal
+
+/**
+ * @addtogroup dali_scene3d_model_components_material
+ * @{
+ */
+
+/**
+ * @brief Class for setting Material properties of 3D models
+ *
+ * @SINCE_2_2.99
+ *
+ * @note This Material class is for setting Material properties of 3D models.
+ * This Material supports properties and textures for PBR.
+ * Also, Material can be shared with multiple ModelPrimitives and if the value is modified,
+ * the rendering results of all ModelPrimitives using this Material will be changed.
+ *
+ * @code
+ * Material material = Material::New();
+ * ModelPrimitive modelPrimitive = ModelPrimitive::New();
+ * modelPrimitive.SetMaterial(material);
+ * material.SetProperty(PropertyIndex, PropertyValue);
+ * @endcode
+ */
+class DALI_SCENE3D_API Material : public Dali::BaseHandle
+{
+public:
+  /**
+   * @brief Enumeration for the start and end property ranges for material.
+   * @SINCE_2_2.99
+   */
+  enum PropertyRange
+  {
+    PROPERTY_START_INDEX          = PROPERTY_REGISTRATION_START_INDEX,    ///< Start index is used by the property registration macro. @SINCE_2_2.99
+    MATERIAL_PROPERTY_START_INDEX = PROPERTY_START_INDEX,                 ///< Start index of Control properties. @SINCE_2_2.99
+    MATERIAL_PROPERTY_END_INDEX   = MATERIAL_PROPERTY_START_INDEX + 1000, ///< Reserving 1000 property indices. @SINCE_2_2.99
+
+    ANIMATABLE_PROPERTY_START_INDEX = ANIMATABLE_PROPERTY_REGISTRATION_START_INDEX,        ///< @SINCE_2_2.99
+    ANIMATABLE_PROPERTY_END_INDEX   = ANIMATABLE_PROPERTY_REGISTRATION_START_INDEX + 1000, ///< Reserve animatable property indices, @SINCE_2_2.99
+  };
+
+  /**
+   * @brief Enumeration for the instance of properties belonging to the Material class.
+   * @SINCE_2_2.99
+   */
+  struct Property
+  {
+    enum
+    {
+      /**
+       * @brief Name of material.
+       * @details type Property::STRING.
+       * @SINCE_2_2.99
+       */
+      NAME = MATERIAL_PROPERTY_START_INDEX,
+
+      /**
+       * @brief Property for the URL of the base color texture.
+       * @details Type Property::STRING.
+       * @SINCE_2_2.99
+       */
+      BASE_COLOR_URL,
+
+      /**
+       * @brief Property for the base color factor of the material surface.
+       * @details Type Property::VECTOR4.
+       * @SINCE_2_2.99
+       */
+      BASE_COLOR_FACTOR,
+
+      /**
+       * @brief Property for the URL of the metallic-roughness texture.
+       * @details Type Property::STRING.
+       * @SINCE_2_2.99
+       */
+      METALLIC_ROUGHNESS_URL,
+
+      /**
+       * @brief Property for the metallic factor of the material surface.
+       * @details Type Property::FLOAT.
+       * @SINCE_2_2.99
+       */
+      METALLIC_FACTOR,
+
+      /**
+       * @brief Property for the roughness factor of the material surface.
+       * @details Type Property::FLOAT.
+       * @SINCE_2_2.99
+       */
+      ROUGHNESS_FACTOR,
+
+      /**
+       * @brief Property for the URL of the normal texture.
+       * @details Type Property::STRING.
+       * @SINCE_2_2.99
+       */
+      NORMAL_URL,
+
+      /**
+       * @brief Property for the scale factor applied to normal vectors.
+       * @details Type Property::FLOAT.
+       * @SINCE_2_2.99
+       */
+      NORMAL_SCALE,
+
+      /**
+       * @brief Property for the URL of the occlusion texture.
+       * @details Type Property::STRING.
+       * @SINCE_2_2.99
+       */
+      OCCLUSION_URL,
+
+      /**
+       * @brief Property for the occlusion strength of the material surface.
+       * @details Type Property::FLOAT.
+       * @SINCE_2_2.99
+       */
+      OCCLUSION_STRENGTH,
+
+      /**
+       * @brief Property for the URL of the emissive texture.
+       * @details Type Property::STRING.
+       * @SINCE_2_2.99
+       */
+      EMISSIVE_URL,
+
+      /**
+       * @brief Emissive factor Property.
+       * @details type Property::VECTOR3.
+       * @SINCE_2_2.99
+       */
+      EMISSIVE_FACTOR,
+
+      /**
+       * @brief Alpha mode Property.
+       * @details type Property::INTEGER.
+       * @SINCE_2_2.99
+       */
+      ALPHA_MODE,
+
+      /**
+       * @brief Alpha cutoff Property.
+       * @details type Property::FLOAT.
+       * @SINCE_2_2.99
+       */
+      ALPHA_CUTOFF,
+
+      /**
+       * @brief Double sided Property.
+       * @details type Property::BOOLEAN.
+       * @SINCE_2_2.99
+       */
+      DOUBLE_SIDED,
+
+      /**
+       *@brief Index of refraction (IOR) of the material surface
+       *@details type Property::FLOAT
+       * @SINCE_2_2.99
+       */
+      IOR,
+
+      /**
+       * @brief Property for the URL of the specular texture.
+       * @details Type Property::STRING.
+       * @SINCE_2_2.99
+       */
+      SPECULAR_URL,
+
+      /**
+       *@brief Property for the specular factor of the material surface.
+       *@details Type Property::FLOAT.
+       * @SINCE_2_2.99
+       */
+      SPECULAR_FACTOR,
+
+      /**
+       * @brief Property for the URL of the specular color texture.
+       * @details Type Property::STRING.
+       * @SINCE_2_2.99
+       */
+      SPECULAR_COLOR_URL,
+
+      /**
+       *@brief Property for the specular color factor of the material surface.
+       *@details Type Property::VECTOR3.
+       * @SINCE_2_2.99
+       */
+      SPECULAR_COLOR_FACTOR,
+    };
+  };
+
+  enum TextureType
+  {
+    /**
+     * @brief Base Color Texture Property.
+     * @SINCE_2_2.99
+     * @note This texture represents the base color of the material. In most cases, this will be the diffuse color of the material.
+     */
+    BASE_COLOR,
+
+    /**
+     * @brief Metallic Roughness Texture Property.
+     * @SINCE_2_2.99
+     * @note This texture represents the metallicness and roughness of the material. This texture can be used to make the material look more metallic or rough.
+     */
+    METALLIC_ROUGHNESS,
+
+    /**
+     * @brief Normal Texture Property.
+     * @SINCE_2_2.99
+     * @note This texture represents the surface of the material. This texture can be used to make the surface of the material look smooth or rough.
+     */
+    NORMAL,
+
+    /**
+     * @brief Occlusion Texture Property.
+     * @SINCE_2_2.99
+     * @note This texture represents the depth of the material. This texture can be used to make the material look more three-dimensional.
+     */
+    OCCLUSION,
+
+    /**
+     * @brief Emissive Texture Property.
+     * @SINCE_2_2.99
+     * @note This texture makes the material look like it's emitting light. This texture can be used to make the material look like it's glowing.
+     */
+    EMISSIVE,
+
+    /**
+     * @brief Specular Texture Property.
+     * @SINCE_2_2.99
+     * @note This texture represents the specular reflection of the material. This texture can be used to make the material look more reflective.
+     */
+    SPECULAR,
+
+    /**
+     * @brief Specular Color Texture Property.
+     * @SINCE_2_2.99
+     * @note This texture represents the color of the specular reflection of the material. This texture can be used to set the color of the specular reflection of the material.
+     */
+    SPECULAR_COLOR,
+  };
+
+  enum AlphaModeType
+  {
+    /**
+     * @brief This indicates that the material is fully opaque and that the alpha value should be ignored.
+     * @SINCE_2_2.99
+     */
+    OPAQUE,
+
+    /**
+     * @brief This indicates that the material is either fully opaque or fully transparent depending on the alpha value. The alpha value is used to mask out areas of the material that should be transparent.
+     * @SINCE_2_2.99
+     */
+    MASK,
+
+    /**
+     * @brief This indicates that the material is transparent and that the alpha value should be used to blend the material with the background.
+     * @SINCE_2_2.99
+     */
+    BLEND,
+  };
+
+public: // Creation & Destruction
+  /**
+   * @brief Create an initialized Material.
+   *
+   * @SINCE_2_2.99
+   * @return A handle to a newly allocated Dali resource
+   */
+  static Material New();
+
+  /**
+   * @brief Creates an uninitialized Material.
+   *
+   * Only derived versions can be instantiated. Calling member
+   * functions with an uninitialized Dali::Object is not allowed.
+   *
+   * @SINCE_2_2.99
+   */
+  Material();
+
+  /**
+   * @brief Destructor.
+   *
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   *
+   * @SINCE_2_2.99
+   */
+  ~Material();
+
+  /**
+   * @brief Copy constructor.
+   *
+   * @SINCE_2_2.99
+   * @param[in] material Handle to an object
+   */
+  Material(const Material& material);
+
+  /**
+   * @brief Move constructor
+   *
+   * @SINCE_2_2.99
+   * @param[in] rhs A reference to the moved handle
+   */
+  Material(Material&& rhs);
+
+  /**
+   * @brief Assignment operator.
+   *
+   * @SINCE_2_2.99
+   * @param[in] material Handle to an object
+   * @return reference to this
+   */
+  Material& operator=(const Material& material);
+
+  /**
+   * @brief Move assignment
+   *
+   * @SINCE_2_2.99
+   * @param[in] rhs A reference to the moved handle
+   * @return A reference to this
+   */
+  Material& operator=(Material&& rhs);
+
+  /**
+   * @brief Downcasts an Object handle to Material.
+   *
+   * If handle points to a Material, the downcast produces valid handle.
+   * If not, the returned handle is left uninitialized.
+   *
+   * @SINCE_2_2.99
+   * @param[in] handle Handle to an object
+   * @return Handle to a Material or an uninitialized handle
+   */
+  static Material DownCast(BaseHandle handle);
+
+public: // Public Method
+  /**
+   * @brief Sets the value of an existing property.
+   * @note BaseHandle is not subclass of Handle. So this API is not use Handle.SetProperty
+   *
+   * @SINCE_2_2.99
+   * @param[in] index The index of the property
+   * @param[in] propertyValue The new value of the property
+   */
+  void SetProperty(Dali::Property::Index index, Dali::Property::Value propertyValue);
+
+  /**
+   * @brief Retrieves a property value.
+   * @note BaseHandle is not subclass of Handle. So this API is not use Handle.SetProperty
+   *
+   * @SINCE_2_2.99
+   * @param[in] index The index of the property
+   * @return The property value
+   * @note This returns the value set by SetProperty() or the animation target value if it is being animated.
+   */
+  Dali::Property::Value GetProperty(Dali::Property::Index index) const;
+
+  /**
+   * @brief Convenience function for obtaining a property of a known type.
+   *
+   * @SINCE_2_2.99
+   * @param[in] index The index of the property
+   * @return The property value
+   * @pre The property types match i.e. PropertyTypes::Get<T>() is equal to GetPropertyType(index).
+   * @see GetProperty()
+   */
+  template<typename T>
+  T GetProperty(Dali::Property::Index index) const
+  {
+    Dali::Property::Value value = GetProperty(index);
+
+    return T(value.Get<T>());
+  }
+
+  /**
+   * @brief Sets the texture for a given texture type.
+   *
+   * @SINCE_2_2.99
+   * @param[in] index The texture type index
+   * @param[in] texture The texture to set.
+   */
+  void SetTexture(Scene3D::Material::TextureType index, Dali::Texture texture);
+
+  /**
+   * @brief Gets the texture for a given texture type.
+   *
+   * @SINCE_2_2.99
+   * @param[in] index The texture type index
+   * @return The texture at the given index.
+   */
+  Dali::Texture GetTexture(Scene3D::Material::TextureType index);
+
+  /**
+   * @brief Sets the sampler for a given texture type.
+   *
+   * @SINCE_2_2.99
+   * @param[in] index The texture type index
+   * @param[in] sampler The sampler to use for this texture type
+   */
+  void SetSampler(Scene3D::Material::TextureType index, Dali::Sampler sampler);
+
+  /**
+   * @brief Gets the sampler for a given texture type.
+   *
+   * @SINCE_2_2.99
+   * @param[in] index The texture type index
+   * @return The sampler used for this texture type
+   */
+  Dali::Sampler GetSampler(Scene3D::Material::TextureType index);
+
+public: // Not intended for application developers
+  /// @cond internal
+  /**
+   * @brief Creates a handle using the Scene3D::Internal implementation.
+   *
+   * @param[in] implementation The Material implementation
+   */
+  DALI_INTERNAL Material(Dali::Scene3D::Internal::Material* implementation);
+  /// @endcond
+};
+
+/**
+ * @}
+ */
+
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_MODEL_COMPONENTS_MATERIAL_H
diff --git a/dali-scene3d/public-api/model-components/model-node.cpp b/dali-scene3d/public-api/model-components/model-node.cpp
new file mode 100644 (file)
index 0000000..152dafc
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2023 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/public-api/model-components/model-node.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/actors/custom-actor.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/model-components/model-node-impl.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+ModelNode ModelNode::New()
+{
+  return Scene3D::Internal::ModelNode::New();
+}
+
+ModelNode::ModelNode()
+{
+}
+
+ModelNode::ModelNode(const ModelNode& modelNode) = default;
+
+ModelNode::ModelNode(ModelNode&& rhs) = default;
+
+ModelNode::~ModelNode()
+{
+}
+
+ModelNode& ModelNode::operator=(const ModelNode& handle) = default;
+
+ModelNode& ModelNode::operator=(ModelNode&& rhs) = default;
+
+ModelNode ModelNode::DownCast(BaseHandle handle)
+{
+  ModelNode result;
+
+  CustomActor custom = Dali::CustomActor::DownCast(handle);
+  if(custom)
+  {
+    CustomActorImpl& customImpl = custom.GetImplementation();
+
+    Internal::ModelNode* impl = dynamic_cast<Internal::ModelNode*>(&customImpl);
+
+    if(impl)
+    {
+      result = ModelNode(customImpl.GetOwner());
+    }
+  }
+
+  return result;
+}
+
+ModelNode::ModelNode(Internal::ModelNode& implementation)
+: CustomActor(implementation)
+{
+}
+
+ModelNode::ModelNode(Dali::Internal::CustomActor* internal)
+: CustomActor(internal)
+{
+  // Can have a NULL pointer so we only need to check if the internal implementation is our class
+  // when there is a value.
+  if(internal)
+  {
+    DALI_ASSERT_DEBUG(dynamic_cast<Internal::ModelNode*>(&CustomActor(internal).GetImplementation()));
+  }
+}
+
+// Public Method
+
+uint32_t ModelNode::GetModelPrimitiveCount() const
+{
+  return Internal::GetImplementation(*this).GetModelPrimitiveCount();
+}
+
+void ModelNode::AddModelPrimitive(ModelPrimitive modelPrimitive)
+{
+  Internal::GetImplementation(*this).AddModelPrimitive(modelPrimitive);
+}
+
+void ModelNode::RemoveModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive)
+{
+  Internal::GetImplementation(*this).RemoveModelPrimitive(modelPrimitive);
+}
+
+void ModelNode::RemoveModelPrimitive(uint32_t index)
+{
+  Internal::GetImplementation(*this).RemoveModelPrimitive(index);
+}
+
+ModelPrimitive ModelNode::GetModelPrimitive(uint32_t index) const
+{
+  return Internal::GetImplementation(*this).GetModelPrimitive(index);
+}
+
+ModelNode ModelNode::FindChildModelNodeByName(std::string_view nodeName)
+{
+  return Internal::GetImplementation(*this).FindChildModelNodeByName(nodeName);
+}
+
+} // namespace Scene3D
+
+} // namespace Dali
diff --git a/dali-scene3d/public-api/model-components/model-node.h b/dali-scene3d/public-api/model-components/model-node.h
new file mode 100644 (file)
index 0000000..b48a158
--- /dev/null
@@ -0,0 +1,214 @@
+#ifndef DALI_SCENE3D_MODEL_COMPONENTS_MODEL_NODE_H
+#define DALI_SCENE3D_MODEL_COMPONENTS_MODEL_NODE_H
+
+/*
+ * Copyright (c) 2023 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/actors/custom-actor.h>
+#include <dali/public-api/common/dali-common.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/api.h>
+#include <dali-scene3d/public-api/model-components/model-primitive.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+// Forward declarations.
+
+namespace Internal
+{
+class ModelNode;
+}
+/**
+ * @addtogroup dali_scene3d_model_components_model_node
+ * @{
+ */
+
+/**
+ * @brief ModelNode is a class for representing the Node of Model in Scene3D.
+ * ModelNode contains multiple ModelPrimitives and allows easy access and modification of Material information that ModelPrimitive has.
+ * If a 3D format file is loaded by Model, ModelNode is created internally to construct the model.
+ * In addition, you can create a Custom ModelNode using ModelPrimitive and Material directly and add it to Model.
+ *
+ * @SINCE_2_2.99
+ *
+ * @code
+ * ModelNode modelNode = ModelNode::New();
+ * ModelPrimitive modelPrimitive = ModelPrimitive::New();
+ * modelNode.AddModelPrimitive(modelPrimitive);
+ *
+ * Material material = Material::New();
+ * modelPrimitive.SetMaterial(material);
+ * material.SetProperty(PropertyIndex, PropertyValue);
+ * @endcode
+ */
+class DALI_SCENE3D_API ModelNode : public Dali::CustomActor
+{
+public:
+  /**
+   * @brief Create an initialized ModelNode.
+   *
+   * @SINCE_2_2.99
+   * @return A handle to a newly allocated Dali resource
+   */
+  static ModelNode New();
+
+  /**
+   * @brief Creates an uninitialized ModelNode.
+   *
+   * Only derived versions can be instantiated. Calling member
+   * functions with an uninitialized Dali::Object is not allowed.
+   *
+   * @SINCE_2_2.99
+   */
+  ModelNode();
+
+  /**
+   * @brief Destructor.
+   *
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   *
+   * @SINCE_2_2.99
+   */
+  ~ModelNode();
+
+  /**
+   * @brief Copy constructor.
+   *
+   * @SINCE_2_2.99
+   * @param[in] modelNode Handle to an object
+   */
+  ModelNode(const ModelNode& modelNode);
+
+  /**
+   * @brief Move constructor
+   *
+   * @SINCE_2_2.99
+   * @param[in] rhs A reference to the moved handle
+   */
+  ModelNode(ModelNode&& rhs);
+
+  /**
+   * @brief Assignment operator.
+   *
+   * @SINCE_2_2.99
+   * @param[in] modelNode Handle to an object
+   * @return reference to this
+   */
+  ModelNode& operator=(const ModelNode& modelNode);
+
+  /**
+   * @brief Move assignment
+   *
+   * @SINCE_2_2.99
+   * @param[in] rhs A reference to the moved handle
+   * @return A reference to this
+   */
+  ModelNode& operator=(ModelNode&& rhs);
+
+  /**
+   * @brief Downcasts an Object handle to ModelNode.
+   *
+   * If handle points to a ModelNode, the downcast produces valid handle.
+   * If not, the returned handle is left uninitialized.
+   *
+   * @SINCE_2_2.99
+   * @param[in] handle Handle to an object
+   * @return Handle to a ModelNode or an uninitialized handle
+   */
+  static ModelNode DownCast(BaseHandle handle);
+
+public: // Public Method
+
+  /**
+   * @brief Gets the number of ModelPrimitives this node has.
+   *
+   * @SINCE_2_2.99
+   * @return The number of ModelPrimitives this node has.
+   */
+  uint32_t GetModelPrimitiveCount() const;
+
+  /**
+   * @brief Appends a ModelPrimitive to this node.
+   *
+   * @SINCE_2_2.99
+   * @param[in] modelPrimitive The ModelPrimitive to append.
+   */
+  void AddModelPrimitive(ModelPrimitive modelPrimitive);
+
+  /**
+   * @brief Removes a ModelPrimitive from this node.
+   *
+   * @SINCE_2_2.99
+   * @param[in] modelPrimitive The ModelPrimitive to remove.
+   */
+  void RemoveModelPrimitive(ModelPrimitive modelPrimitive);
+
+  /**
+   * @brief Removes a ModelPrimitive from this node by index.
+   *
+   * @SINCE_2_2.99
+   * @param[in] index The index of the ModelPrimitive to remove.
+   */
+  void RemoveModelPrimitive(uint32_t index);
+
+  /**
+   * @brief Gets a ModelPrimitive by index.
+   *
+   * @SINCE_2_2.99
+   * @param[in] index The index of the ModelPrimitive to get.
+   * @return The ModelPrimitive at the given index, or an empty handle if the index is out of range.
+   */
+  ModelPrimitive GetModelPrimitive(uint32_t index) const;
+
+  /**
+   * @brief Returns a child ModelNode object with a name that matches nodeName.
+   *
+   * @param[in] nodeName The name of the child ModelNode object you want to find.
+   * @return Returns a child ModelNode object with a name that matches nodeName. If there is no corresponding child ModelNode object, it returns an empty ModelNode object.
+   */
+  ModelNode FindChildModelNodeByName(std::string_view nodeName);
+
+public: // Not intended for application developers
+  /// @cond internal
+  /**
+   * @brief Creates a handle using the Scene3D::Internal implementation.
+   *
+   * @param[in] implementation The ModelNodel implementation
+   */
+  DALI_INTERNAL ModelNode(Internal::ModelNode& implementation);
+
+  /**
+   * @brief Allows the creation of this Control from an Internal::CustomActor pointer.
+   *
+   * @param[in] internal A pointer to the internal CustomActor
+   */
+  DALI_INTERNAL ModelNode(Dali::Internal::CustomActor* internal);
+  /// @endcond
+};
+
+/**
+ * @}
+ */
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_MODEL_COMPONENTS_MODEL_NODE_H
diff --git a/dali-scene3d/public-api/model-components/model-primitive.cpp b/dali-scene3d/public-api/model-components/model-primitive.cpp
new file mode 100644 (file)
index 0000000..b8d5400
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2023 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/public-api/model-components/model-primitive.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/model-components/model-primitive-impl.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+ModelPrimitive ModelPrimitive::New()
+{
+  Internal::ModelPrimitivePtr internal = Internal::ModelPrimitive::New();
+
+  return ModelPrimitive(internal.Get());
+}
+
+ModelPrimitive::ModelPrimitive()
+{
+}
+
+ModelPrimitive::ModelPrimitive(const ModelPrimitive& modelPrimitive) = default;
+
+ModelPrimitive::ModelPrimitive(ModelPrimitive&& rhs) = default;
+
+ModelPrimitive::~ModelPrimitive()
+{
+}
+
+ModelPrimitive& ModelPrimitive::operator=(const ModelPrimitive& handle) = default;
+
+ModelPrimitive& ModelPrimitive::operator=(ModelPrimitive&& rhs) = default;
+
+ModelPrimitive ModelPrimitive::DownCast(BaseHandle handle)
+{
+  return ModelPrimitive(dynamic_cast<Dali::Scene3D::Internal::ModelPrimitive*>(handle.GetObjectPtr()));
+}
+
+ModelPrimitive::ModelPrimitive(Dali::Scene3D::Internal::ModelPrimitive* internal)
+: BaseHandle(internal)
+{
+}
+
+// Public Method
+void ModelPrimitive::SetGeometry(Dali::Geometry geometry)
+{
+  GetImplementation(*this).SetGeometry(geometry);
+}
+
+Dali::Geometry ModelPrimitive::GetGeometry() const
+{
+  return GetImplementation(*this).GetGeometry();
+}
+
+void ModelPrimitive::SetMaterial(Dali::Scene3D::Material material)
+{
+  GetImplementation(*this).SetMaterial(material);
+}
+
+Dali::Scene3D::Material ModelPrimitive::GetMaterial() const
+{
+  return GetImplementation(*this).GetMaterial();
+}
+
+} // namespace Scene3D
+
+} // namespace Dali
diff --git a/dali-scene3d/public-api/model-components/model-primitive.h b/dali-scene3d/public-api/model-components/model-primitive.h
new file mode 100644 (file)
index 0000000..899996c
--- /dev/null
@@ -0,0 +1,184 @@
+#ifndef DALI_SCENE3D_MODEL_COMPONENTS_MODEL_PRIMITIVE_H
+#define DALI_SCENE3D_MODEL_COMPONENTS_MODEL_PRIMITIVE_H
+
+/*
+ * Copyright (c) 2023 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/dali-common.h>
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/rendering/renderer.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/api.h>
+#include <dali-scene3d/public-api/model-components/material.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+//Forward declarations.
+namespace Internal
+{
+class ModelPrimitive;
+} // namespace Internal
+
+/**
+ * @addtogroup dali_scene3d_model_components_model_primitive
+ * @{
+ */
+
+/**
+ * @brief This ModelPrimitive class is required to draw the mesh geometry defined by the user.
+ * Users can set Dali::Geometry and Material to ModelPrimitive using SetGeometry and SetMaterial methods respectively.
+ * When ModelPrimitive added to ModelNode using ModelNode::AddModelPrimitive() method, the Geometry is rendered on the screen according to the Material settings.
+ *
+ * @SINCE_2_2.99
+ *
+ * If you load resources from 3D format files such as glTF using Model class, ModelPrimitive is also created internally.
+ * In this case, blendShape morphing or skeletal animation defined in the format can be used.
+ * However, for the custom ModelPrimitive that is created by user, blendShape morphing or skeletal animation is not supported.
+ */
+class DALI_SCENE3D_API ModelPrimitive : public Dali::BaseHandle
+{
+public: // Creation & Destruction
+  /**
+   * @brief Create an initialized ModelPrimitive.
+   *
+   * @SINCE_2_2.99
+   * @return A handle to a newly allocated Dali resource
+   */
+  static ModelPrimitive New();
+
+  /**
+   * @brief Creates an uninitialized ModelPrimitive.
+   *
+   * Only derived versions can be instantiated. Calling member
+   * functions with an uninitialized Dali::Object is not allowed.
+   *
+   * @SINCE_2_2.99
+   */
+  ModelPrimitive();
+
+  /**
+   * @brief Destructor.
+   *
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   *
+   * @SINCE_2_2.99
+   */
+  ~ModelPrimitive();
+
+  /**
+   * @brief Copy constructor.
+   *
+   * @SINCE_2_2.99
+   * @param[in] modelPrimtive Handle to an object
+   */
+  ModelPrimitive(const ModelPrimitive& modelPrimtive);
+
+  /**
+   * @brief Move constructor
+   *
+   * @SINCE_2_2.99
+   * @param[in] rhs A reference to the moved handle
+   */
+  ModelPrimitive(ModelPrimitive&& rhs);
+
+  /**
+   * @brief Assignment operator.
+   *
+   * @SINCE_2_2.99
+   * @param[in] modelPrimitive Handle to an object
+   * @return reference to this
+   */
+  ModelPrimitive& operator=(const ModelPrimitive& modelPrimitive);
+
+  /**
+   * @brief Move assignment
+   *
+   * @SINCE_2_2.99
+   * @param[in] rhs A reference to the moved handle
+   * @return A reference to this
+   */
+  ModelPrimitive& operator=(ModelPrimitive&& rhs);
+
+  /**
+   * @brief Downcasts an Object handle to ModelPrimitive.
+   *
+   * If handle points to a ModelPrimitive, the downcast produces valid handle.
+   * If not, the returned handle is left uninitialized.
+   *
+   * @SINCE_2_2.99
+   * @param[in] handle Handle to an object
+   * @return Handle to a ModelPrimitive or an uninitialized handle
+   */
+  static ModelPrimitive DownCast(BaseHandle handle);
+
+public: // Public Method
+  /**
+   * @brief Set the geometry for this renderer.
+   *
+   * @SINCE_2_2.99
+   * @param[in] geometry The geometry to set.
+   */
+  void SetGeometry(Dali::Geometry geometry);
+
+  /**
+   * @brief Get the geometry for this renderer.
+   *
+   * @SINCE_2_2.99
+   * @return The geometry for this renderer.
+   */
+  Dali::Geometry GetGeometry() const;
+
+  /**
+   * @brief Sets the material for this primitive.
+   *
+   * @SINCE_2_2.99
+   * @param[in] material The material
+   */
+  void SetMaterial(Material material);
+
+  /**
+   * @brief Retrieves a material.
+   *
+   * @SINCE_2_2.99
+   * @return The material handle
+   */
+  Material GetMaterial() const;
+
+public: // Not intended for application developers
+  /// @cond internal
+  /**
+   * @brief Creates a handle using the Scene3D::Internal implementation.
+   *
+   * @param[in] implementation The ModelPrimitive implementation
+   */
+  DALI_INTERNAL ModelPrimitive(Dali::Scene3D::Internal::ModelPrimitive* implementation);
+  /// @endcond
+};
+
+/**
+ * @}
+ */
+
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_MODEL_COMPONENTS_MODEL_PRIMITIVE_H
index cff4718..ace107e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -26,18 +26,18 @@ namespace Toolkit
 {
 namespace TextureManager
 {
-std::string AddTexture(Texture& texture)
+std::string AddTexture(Texture& texture, bool preMultiplied)
 {
   TextureSet set = TextureSet::New();
   set.SetTexture(0u, texture);
-  return AddTexture(set);
+  return AddTexture(set, preMultiplied);
 }
 
-std::string AddTexture(TextureSet& textureSet)
+std::string AddTexture(TextureSet& textureSet, bool preMultiplied)
 {
   auto  visualFactory = Toolkit::VisualFactory::Get();
   auto& textureMgr    = GetImplementation(visualFactory).GetTextureManager();
-  return textureMgr.AddExternalTexture(textureSet);
+  return textureMgr.AddExternalTexture(textureSet, preMultiplied);
 }
 
 TextureSet RemoveTexture(const std::string& textureUrl)
index f18ed14..ff61916 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_DEVEL_API_TEXTURE_MANAGER_H
 
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -39,9 +39,10 @@ namespace TextureManager
  * @note this method does not check for duplicates,
  *       if same Texture is added multiple times, a different URL is returned each time
  * @param[in] texture the Texture to add
+ * @param[in] preMultiplied Whether this external texture preMultiplied or not. Default as false.
  * @return the Url string representing this texture
  */
-DALI_TOOLKIT_API std::string AddTexture(Texture& texture);
+DALI_TOOLKIT_API std::string AddTexture(Texture& texture, bool preMultiplied = false);
 
 /**
  * @brief Add a TextureSet to texture manager
@@ -49,9 +50,10 @@ DALI_TOOLKIT_API std::string AddTexture(Texture& texture);
  * @note this method does not check for duplicates,
  *       if same TextureSet is added multiple times, a different URL is returned each time
  * @param[in] textureSet the TextureSet to add
+ * @param[in] preMultiplied Whether this external texture preMultiplied or not. Default as false.
  * @return the Url string representing this texture
  */
-DALI_TOOLKIT_API std::string AddTexture(TextureSet& textureSet);
+DALI_TOOLKIT_API std::string AddTexture(TextureSet& textureSet, bool preMultiplied = false);
 
 /**
  * @brief Removes a TextureSet from toolkit
index 9ddab28..28ecd9f 100644 (file)
@@ -70,23 +70,22 @@ namespace
 {
 #define TOKEN_STRING(x) #x
 
-const std::string KEYNAME_ACTORS           = "actors";
-const std::string KEYNAME_ENTRY_TRANSITION = "entryTransition";
-const std::string KEYNAME_EXIT_TRANSITION  = "exitTransition";
-const std::string KEYNAME_INCLUDES         = "includes";
-const std::string KEYNAME_INHERIT          = "inherit";
-const std::string KEYNAME_MAPPINGS         = "mappings";
-const std::string KEYNAME_NAME             = "name";
-const std::string KEYNAME_SIGNALS          = "signals";
-const std::string KEYNAME_STATES           = "states";
-const std::string KEYNAME_STYLES           = "styles";
-const std::string KEYNAME_TEMPLATES        = "templates";
-const std::string KEYNAME_TRANSITIONS      = "transitions";
-const std::string KEYNAME_TYPE             = "type";
-const std::string KEYNAME_VISUALS          = "visuals";
-
-const std::string PROPERTIES            = "properties";
-const std::string ANIMATABLE_PROPERTIES = "animatableProperties";
+const char* KEYNAME_ACTORS           = "actors";
+const char* KEYNAME_ENTRY_TRANSITION = "entryTransition";
+const char* KEYNAME_EXIT_TRANSITION  = "exitTransition";
+const char* KEYNAME_INCLUDES         = "includes";
+const char* KEYNAME_INHERIT          = "inherit";
+const char* KEYNAME_MAPPINGS         = "mappings";
+const char* KEYNAME_SIGNALS          = "signals";
+const char* KEYNAME_STATES           = "states";
+const char* KEYNAME_STYLES           = "styles";
+const char* KEYNAME_TEMPLATES        = "templates";
+const char* KEYNAME_TRANSITIONS      = "transitions";
+const char* KEYNAME_TYPE             = "type";
+const char* KEYNAME_VISUALS          = "visuals";
+
+const char* PROPERTIES            = "properties";
+const char* ANIMATABLE_PROPERTIES = "animatableProperties";
 
 typedef std::vector<const TreeNode*> TreeNodeList;
 
index 84d44b9..79408a2 100644 (file)
 
 namespace
 {
-const std::string READING_INFO_TYPE_NAME           = "name";
-const std::string READING_INFO_TYPE_ROLE           = "role";
-const std::string READING_INFO_TYPE_DESCRIPTION    = "description";
-const std::string READING_INFO_TYPE_STATE          = "state";
-const std::string READING_INFO_TYPE_ATTRIBUTE_NAME = "reading_info_type";
-const std::string READING_INFO_TYPE_SEPARATOR      = "|";
+const char* READING_INFO_TYPE_NAME           = "name";
+const char* READING_INFO_TYPE_ROLE           = "role";
+const char* READING_INFO_TYPE_DESCRIPTION    = "description";
+const char* READING_INFO_TYPE_STATE          = "state";
+const char* READING_INFO_TYPE_ATTRIBUTE_NAME = "reading_info_type";
+const char* READING_INFO_TYPE_SEPARATOR      = "|";
 } // namespace
 
 namespace Dali
index 6231b64..7b3679a 100644 (file)
@@ -35,7 +35,7 @@ namespace
 const char* const IMAGE_BRDF_FILE_NAME = "brdfLUT.png";
 
 // glTF file extension
-const std::string GLTF_EXT(".gltf");
+const char* GLTF_EXT(".gltf");
 
 /**
  * cube map face index
index 96dc6af..f34dac7 100644 (file)
@@ -70,25 +70,25 @@ const unsigned int DEFAULT_RENDERING_BACKEND = Dali::Toolkit::DevelText::DEFAULT
  * The alignment depends on the alignment value of the text label (Use Text::VerticalAlignment enumerations).
  */
 const float VERTICAL_ALIGNMENT_TABLE[Text::VerticalAlignment::BOTTOM + 1] =
-  {
+{
     0.0f, // VerticalAlignment::TOP
     0.5f, // VerticalAlignment::CENTER
     1.0f  // VerticalAlignment::BOTTOM
 };
 
-const std::string TEXT_FIT_ENABLE_KEY("enable");
-const std::string TEXT_FIT_MIN_SIZE_KEY("minSize");
-const std::string TEXT_FIT_MAX_SIZE_KEY("maxSize");
-const std::string TEXT_FIT_STEP_SIZE_KEY("stepSize");
-const std::string TEXT_FIT_FONT_SIZE_KEY("fontSize");
-const std::string TEXT_FIT_FONT_SIZE_TYPE_KEY("fontSizeType");
+const char* TEXT_FIT_ENABLE_KEY("enable");
+const char* TEXT_FIT_MIN_SIZE_KEY("minSize");
+const char* TEXT_FIT_MAX_SIZE_KEY("maxSize");
+const char* TEXT_FIT_STEP_SIZE_KEY("stepSize");
+const char* TEXT_FIT_FONT_SIZE_KEY("fontSize");
+const char* TEXT_FIT_FONT_SIZE_TYPE_KEY("fontSizeType");
 
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
 #endif
 
 const Scripting::StringEnum AUTO_SCROLL_STOP_MODE_TABLE[] =
-  {
+{
     {"IMMEDIATE", Toolkit::TextLabel::AutoScrollStopMode::IMMEDIATE},
     {"FINISH_LOOP", Toolkit::TextLabel::AutoScrollStopMode::FINISH_LOOP},
 };
index 862a69c..50d7d3d 100644 (file)
@@ -57,7 +57,7 @@ namespace
 #define GET_LOCALE_TEXT(string) dgettext("dali-toolkit", string)
 #endif
 
-const std::string   TEXT_SELECTION_POPUP_BUTTON_STYLE_NAME("TextSelectionPopupButton");
+const char*   TEXT_SELECTION_POPUP_BUTTON_STYLE_NAME("TextSelectionPopupButton");
 const Dali::Vector4 DEFAULT_OPTION_PRESSED_COLOR(Dali::Vector4(0.24f, 0.72f, 0.8f, 1.0f));
 const float         DEFAULT_OPTION_PRESSED_CORNER_RADIUS = 0.0f;
 const Dali::Vector4 DEFAULT_LABEL_PADDING(Dali::Vector4(24.0f, 24.0f, 14.0f, 14.0f));
@@ -93,8 +93,8 @@ const char* const OPTION_COPY("optionCopy");                 // "Copy" popup opt
 const char* const OPTION_PASTE("optionPaste");               // "Paste" popup option.
 const char* const OPTION_CLIPBOARD("optionClipboard");       // "Clipboard" popup option.
 
-const std::string IDS_LTR("IDS_LTR");
-const std::string RTL_DIRECTION("RTL");
+const char* IDS_LTR("IDS_LTR");
+const char* RTL_DIRECTION("RTL");
 
 BaseHandle Create()
 {
@@ -726,10 +726,10 @@ void TextSelectionPopup::AddPopupOptionsToToolbar(bool showIcons, bool showCapti
   // Whether to mirror the list of buttons (for right to left languages)
   bool mirror = false;
 #if defined(__GLIBC__)
-  char* idsLtr = GET_LOCALE_TEXT(IDS_LTR.c_str());
+  char* idsLtr = GET_LOCALE_TEXT(IDS_LTR);
   if(NULL != idsLtr)
   {
-    mirror = (0 == strcmp(idsLtr, RTL_DIRECTION.c_str()));
+    mirror = (0 == strcmp(idsLtr, RTL_DIRECTION));
 
     if(mirror)
     {
index fae9fc1..31dc6fc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -30,9 +30,9 @@ namespace Toolkit
 {
 namespace Internal
 {
-ImageUrl::ImageUrl(Texture& texture)
+ImageUrl::ImageUrl(Texture& texture, bool preMultiplied)
 {
-  mUrl = Dali::Toolkit::TextureManager::AddTexture(texture);
+  mUrl = Dali::Toolkit::TextureManager::AddTexture(texture, preMultiplied);
 }
 
 ImageUrl::ImageUrl(const EncodedImageBuffer& encodedImageBuffer)
@@ -66,9 +66,9 @@ ImageUrl::~ImageUrl()
   }
 }
 
-ImageUrlPtr ImageUrl::New(Texture& texture)
+ImageUrlPtr ImageUrl::New(Texture& texture, bool preMultiplied)
 {
-  ImageUrlPtr imageUrlPtr = new ImageUrl(texture);
+  ImageUrlPtr imageUrlPtr = new ImageUrl(texture, preMultiplied);
   return imageUrlPtr;
 }
 
index 99616bf..f7ae8ef 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_IMAGE_URL_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -41,7 +41,7 @@ public:
   /**
    * @brief Constructor.
    */
-  ImageUrl(Texture& texture);
+  ImageUrl(Texture& texture, bool preMultiplied);
 
   /**
    * @brief Constructor.
@@ -51,7 +51,7 @@ public:
   /**
    * @copydoc Dali::Toolkit::ImageUrl::New
    */
-  static ImageUrlPtr New(Texture& texture);
+  static ImageUrlPtr New(Texture& texture, bool preMultiplied);
 
   /**
    * @copydoc Dali::Toolkit::ImageUrl::New
index 076b4dd..f9dc67d 100644 (file)
@@ -39,11 +39,11 @@ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT
 
 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
 
-const std::string KEY_C_NAME      = "c";
-const std::string KEY_V_NAME      = "v";
-const std::string KEY_X_NAME      = "x";
-const std::string KEY_A_NAME      = "a";
-const std::string KEY_INSERT_NAME = "Insert";
+const char* KEY_C_NAME      = "c";
+const char* KEY_V_NAME      = "v";
+const char* KEY_X_NAME      = "x";
+const char* KEY_A_NAME      = "a";
+const char* KEY_INSERT_NAME = "Insert";
 
 } // namespace
 
index 29bab35..9aa4a06 100644 (file)
@@ -51,7 +51,7 @@ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT
 
 constexpr float MAX_FLOAT = std::numeric_limits<float>::max();
 
-const std::string EMPTY_STRING("");
+const char* EMPTY_STRING = "";
 
 } // namespace
 
index a1e6214..f2d49d2 100644 (file)
@@ -131,7 +131,7 @@ void Controller::InputFontHandler::SetInputFontFamily(Controller& controller, co
   }
 }
 
-const std::string& Controller::InputFontHandler::GetInputFontFamily(const Controller& controller)
+std::string Controller::InputFontHandler::GetInputFontFamily(const Controller& controller)
 {
   if(NULL != controller.mImpl->mEventData)
   {
index 2ac7464..bf38a00 100644 (file)
@@ -34,7 +34,7 @@ namespace Text
 struct Controller::InputFontHandler
 {
   static void               SetInputFontFamily(Controller& controller, const std::string& fontFamily);
-  static const std::string& GetInputFontFamily(const Controller& controller);
+  static std::string        GetInputFontFamily(const Controller& controller);
   static void               SetInputFontWeight(const Controller& controller, FontWeight weight);
   static bool               IsInputFontWeightDefined(const Controller& controller);
   static FontWeight         GetInputFontWeight(const Controller& controller);
index f410187..7842d05 100644 (file)
@@ -38,7 +38,7 @@
 
 namespace
 {
-const std::string EMPTY_STRING("");
+const char* EMPTY_STRING = "";
 }
 
 namespace Dali::Toolkit::Text
@@ -116,7 +116,7 @@ void Controller::InputProperties::SetInputShadowProperties(Controller& controlle
   }
 }
 
-const std::string& Controller::InputProperties::GetInputShadowProperties(const Controller& controller)
+std::string Controller::InputProperties::GetInputShadowProperties(const Controller& controller)
 {
   return controller.mImpl->mEventData ? controller.mImpl->mEventData->mInputStyle.shadowProperties : EMPTY_STRING;
 }
@@ -129,7 +129,7 @@ void Controller::InputProperties::SetInputUnderlineProperties(Controller& contro
   }
 }
 
-const std::string& Controller::InputProperties::GetInputUnderlineProperties(const Controller& controller)
+std::string Controller::InputProperties::GetInputUnderlineProperties(const Controller& controller)
 {
   return controller.mImpl->mEventData ? controller.mImpl->mEventData->mInputStyle.underlineProperties : EMPTY_STRING;
 }
@@ -142,7 +142,7 @@ void Controller::InputProperties::SetInputEmbossProperties(Controller& controlle
   }
 }
 
-const std::string& Controller::InputProperties::GetInputEmbossProperties(const Controller& controller)
+std::string Controller::InputProperties::GetInputEmbossProperties(const Controller& controller)
 {
   return controller.mImpl->mEventData ? controller.mImpl->mEventData->mInputStyle.embossProperties : controller.GetDefaultEmbossProperties();
 }
@@ -155,7 +155,7 @@ void Controller::InputProperties::SetInputOutlineProperties(Controller& controll
   }
 }
 
-const std::string& Controller::InputProperties::GetInputOutlineProperties(const Controller& controller)
+std::string Controller::InputProperties::GetInputOutlineProperties(const Controller& controller)
 {
   return controller.mImpl->mEventData ? controller.mImpl->mEventData->mInputStyle.outlineProperties : controller.GetDefaultOutlineProperties();
 }
index b0736f7..9b31d22 100644 (file)
@@ -39,19 +39,19 @@ struct Controller::InputProperties
 
   static void SetInputShadowProperties(Controller& controller, const std::string& shadowProperties);
 
-  static const std::string& GetInputShadowProperties(const Controller& controller);
+  static std::string GetInputShadowProperties(const Controller& controller);
 
   static void SetInputUnderlineProperties(Controller& controller, const std::string& underlineProperties);
 
-  static const std::string& GetInputUnderlineProperties(const Controller& controller);
+  static std::string GetInputUnderlineProperties(const Controller& controller);
 
   static void SetInputEmbossProperties(Controller& controller, const std::string& embossProperties);
 
-  static const std::string& GetInputEmbossProperties(const Controller& controller);
+  static std::string GetInputEmbossProperties(const Controller& controller);
 
   static void SetInputOutlineProperties(Controller& controller, const std::string& outlineProperties);
 
-  static const std::string& GetInputOutlineProperties(const Controller& controller);
+  static std::string GetInputOutlineProperties(const Controller& controller);
 
   static void SetInputModePassword(Controller& controller, bool passwordInput);
 
index da15a43..a0f03a6 100644 (file)
@@ -33,7 +33,7 @@ namespace
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
 #endif
 
-const std::string EMPTY_STRING("");
+const char* EMPTY_STRING = "";
 
 const char* const PLACEHOLDER_TEXT         = "text";
 const char* const PLACEHOLDER_TEXT_FOCUSED = "textFocused";
@@ -122,7 +122,7 @@ void Controller::PlaceholderHandler::SetPlaceholderFontFamily(Controller& contro
   }
 }
 
-const std::string& Controller::PlaceholderHandler::GetPlaceholderFontFamily(const Controller& controller)
+std::string Controller::PlaceholderHandler::GetPlaceholderFontFamily(const Controller& controller)
 {
   if((NULL != controller.mImpl->mEventData) && (NULL != controller.mImpl->mEventData->mPlaceholderFont))
   {
index c7107c9..3bbf87b 100644 (file)
@@ -39,7 +39,7 @@ struct Controller::PlaceholderHandler
   static void               SetPlaceholderText(Controller& controller, PlaceholderType type, const std::string& text);
   static void               GetPlaceholderText(const Controller& controller, PlaceholderType type, std::string& text);
   static void               SetPlaceholderFontFamily(Controller& controller, const std::string& placeholderTextFontFamily);
-  static const std::string& GetPlaceholderFontFamily(const Controller& controller);
+  static std::string        GetPlaceholderFontFamily(const Controller& controller);
   static void               SetPlaceholderTextFontWeight(Controller& controller, FontWeight weight);
   static bool               IsPlaceholderTextFontWeightDefined(const Controller& controller);
   static FontWeight         GetPlaceholderTextFontWeight(const Controller& controller);
index 1759095..1d91ff7 100644 (file)
@@ -45,7 +45,7 @@ namespace
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
 #endif
 
-const std::string EMPTY_STRING("");
+const char* EMPTY_STRING = "";
 
 template<typename Type>
 void EnsureCreated(Type*& object)
@@ -538,7 +538,7 @@ void Controller::SetDefaultFontFamily(const std::string& defaultFontFamily)
   }
 }
 
-const std::string& Controller::GetDefaultFontFamily() const
+std::string Controller::GetDefaultFontFamily() const
 {
   return mImpl->mFontDefaults ? mImpl->mFontDefaults->mFontDescription.family : EMPTY_STRING;
 }
@@ -548,7 +548,7 @@ void Controller::SetPlaceholderFontFamily(const std::string& placeholderTextFont
   PlaceholderHandler::SetPlaceholderFontFamily(*this, placeholderTextFontFamily);
 }
 
-const std::string& Controller::GetPlaceholderFontFamily() const
+std::string Controller::GetPlaceholderFontFamily() const
 {
   return PlaceholderHandler::GetPlaceholderFontFamily(*this);
 }
@@ -945,7 +945,7 @@ void Controller::SetDefaultEmbossProperties(const std::string& embossProperties)
   mImpl->mEmbossDefaults->properties = embossProperties;
 }
 
-const std::string& Controller::GetDefaultEmbossProperties() const
+std::string Controller::GetDefaultEmbossProperties() const
 {
   return mImpl->mEmbossDefaults ? mImpl->mEmbossDefaults->properties : EMPTY_STRING;
 }
@@ -956,7 +956,7 @@ void Controller::SetDefaultOutlineProperties(const std::string& outlinePropertie
   mImpl->mOutlineDefaults->properties = outlineProperties;
 }
 
-const std::string& Controller::GetDefaultOutlineProperties() const
+std::string Controller::GetDefaultOutlineProperties() const
 {
   return mImpl->mOutlineDefaults ? mImpl->mOutlineDefaults->properties : EMPTY_STRING;
 }
@@ -1006,7 +1006,7 @@ void Controller::SetInputFontFamily(const std::string& fontFamily)
   InputFontHandler::SetInputFontFamily(*this, fontFamily);
 }
 
-const std::string& Controller::GetInputFontFamily() const
+std::string Controller::GetInputFontFamily() const
 {
   return InputFontHandler::GetInputFontFamily(*this);
 }
@@ -1081,7 +1081,7 @@ void Controller::SetInputShadowProperties(const std::string& shadowProperties)
   InputProperties::SetInputShadowProperties(*this, shadowProperties);
 }
 
-const std::string& Controller::GetInputShadowProperties() const
+std::string Controller::GetInputShadowProperties() const
 {
   return InputProperties::GetInputShadowProperties(*this);
 }
@@ -1091,7 +1091,7 @@ void Controller::SetInputUnderlineProperties(const std::string& underlinePropert
   InputProperties::SetInputUnderlineProperties(*this, underlineProperties);
 }
 
-const std::string& Controller::GetInputUnderlineProperties() const
+std::string Controller::GetInputUnderlineProperties() const
 {
   return InputProperties::GetInputUnderlineProperties(*this);
 }
@@ -1101,7 +1101,7 @@ void Controller::SetInputEmbossProperties(const std::string& embossProperties)
   InputProperties::SetInputEmbossProperties(*this, embossProperties);
 }
 
-const std::string& Controller::GetInputEmbossProperties() const
+std::string Controller::GetInputEmbossProperties() const
 {
   return InputProperties::GetInputEmbossProperties(*this);
 }
@@ -1111,7 +1111,7 @@ void Controller::SetInputOutlineProperties(const std::string& outlineProperties)
   InputProperties::SetInputOutlineProperties(*this, outlineProperties);
 }
 
-const std::string& Controller::GetInputOutlineProperties() const
+std::string Controller::GetInputOutlineProperties() const
 {
   return InputProperties::GetInputOutlineProperties(*this);
 }
@@ -1236,7 +1236,7 @@ void Controller::SetInputStrikethroughProperties(const std::string& strikethroug
   }
 }
 
-const std::string& Controller::GetInputStrikethroughProperties() const
+std::string Controller::GetInputStrikethroughProperties() const
 {
   return (NULL != mImpl->mEventData) ? mImpl->mEventData->mInputStyle.strikethroughProperties : EMPTY_STRING;
 }
index 83b2090..c0a560f 100644 (file)
@@ -826,7 +826,7 @@ public: // Default style & Input style
    *
    * @return The default font family.
    */
-  const std::string& GetDefaultFontFamily() const;
+  std::string GetDefaultFontFamily() const;
 
   /**
    * @brief Sets the placeholder text font family.
@@ -839,7 +839,7 @@ public: // Default style & Input style
    *
    * @return The placeholder text font family
    */
-  const std::string& GetPlaceholderFontFamily() const;
+  std::string GetPlaceholderFontFamily() const;
 
   /**
    * @brief Sets the default font weight.
@@ -1255,7 +1255,7 @@ public: // Default style & Input style
    *
    * @return The emboss's properties string.
    */
-  const std::string& GetDefaultEmbossProperties() const;
+  std::string GetDefaultEmbossProperties() const;
 
   /**
    * @brief Sets the outline's properties string.
@@ -1271,7 +1271,7 @@ public: // Default style & Input style
    *
    * @return The outline's properties string.
    */
-  const std::string& GetDefaultOutlineProperties() const;
+  std::string GetDefaultOutlineProperties() const;
 
   /**
    * @brief Sets the default line spacing.
@@ -1347,7 +1347,7 @@ public: // Default style & Input style
    *
    * @return The input text's font family name.
    */
-  const std::string& GetInputFontFamily() const;
+  std::string GetInputFontFamily() const;
 
   /**
    * @brief Sets the input font's weight.
@@ -1448,7 +1448,7 @@ public: // Default style & Input style
    *
    * @return The shadow's properties string.
    */
-  const std::string& GetInputShadowProperties() const;
+  std::string GetInputShadowProperties() const;
 
   /**
    * @brief Sets the input underline's properties string.
@@ -1464,7 +1464,7 @@ public: // Default style & Input style
    *
    * @return The underline's properties string.
    */
-  const std::string& GetInputUnderlineProperties() const;
+  std::string GetInputUnderlineProperties() const;
 
   /**
    * @brief Sets the input emboss's properties string.
@@ -1480,7 +1480,7 @@ public: // Default style & Input style
    *
    * @return The emboss's properties string.
    */
-  const std::string& GetInputEmbossProperties() const;
+  std::string GetInputEmbossProperties() const;
 
   /**
    * @brief Sets input the outline's properties string.
@@ -1496,7 +1496,7 @@ public: // Default style & Input style
    *
    * @return The outline's properties string.
    */
-  const std::string& GetInputOutlineProperties() const;
+  std::string GetInputOutlineProperties() const;
 
   /**
    * @brief Sets the input strikethrough's properties string.
@@ -1512,7 +1512,7 @@ public: // Default style & Input style
    *
    * @return The strikethrough's properties string.
    */
-  const std::string& GetInputStrikethroughProperties() const;
+  std::string GetInputStrikethroughProperties() const;
 
   /**
    * @brief Set the control's interface.
index 8e9ec06..f39afce 100644 (file)
@@ -35,8 +35,7 @@ namespace Text
 {
 namespace
 {
-const std::string NONE("none");
-const std::string MULTIPLY("multiply");
+const char* MULTIPLY = "multiply";
 } // namespace
 
 void ProcessEmbeddedItem(const Tag& tag, EmbeddedItem& embeddedItem)
index 133d7fa..13afaa0 100644 (file)
@@ -37,9 +37,6 @@ namespace Text
 {
 namespace
 {
-const std::string  FONT_PREFIX("font-");
-const unsigned int FONT_PREFIX_LENGTH      = 5u;
-const unsigned int MIN_FONT_ATTRIBUTE_SIZE = 4u;   ///< The minimum length of any of the possible 'weight', 'width' , 'slant' or 'size' values.
 const unsigned int MAX_FONT_ATTRIBUTE_SIZE = 15u;  ///< The maximum length of any of the possible 'weight', 'width' or 'slant' values.
 const float        PIXEL_FORMAT_64_FACTOR  = 64.f; ///< 64.f is used to convert from point size to 26.6 pixel format.
 } // namespace
index b0ac16b..f7ed507 100644 (file)
@@ -44,23 +44,23 @@ const char        WEB_COLOR_TOKEN('#');
 const char* const HEX_COLOR_TOKEN("0x");
 const char* const ALPHA_ONE("FF");
 
-const std::string BLACK_COLOR("black");
-const std::string WHITE_COLOR("white");
-const std::string RED_COLOR("red");
-const std::string GREEN_COLOR("green");
-const std::string BLUE_COLOR("blue");
-const std::string YELLOW_COLOR("yellow");
-const std::string MAGENTA_COLOR("magenta");
-const std::string CYAN_COLOR("cyan");
-const std::string TRANSPARENT_COLOR("transparent");
-
-const std::string SOLID_UNDERLINE("solid");
-const std::string DASHED_UNDERLINE("dashed");
-const std::string DOUBLE_UNDERLINE("double");
-
-const std::string BEGIN_HORIZONTAL_ALIGNMENT("begin");
-const std::string CENTER_HORIZONTAL_ALIGNMENT("center");
-const std::string END_HORIZONTAL_ALIGNMENT("end");
+const char* BLACK_COLOR("black");
+const char* WHITE_COLOR("white");
+const char* RED_COLOR("red");
+const char* GREEN_COLOR("green");
+const char* BLUE_COLOR("blue");
+const char* YELLOW_COLOR("yellow");
+const char* MAGENTA_COLOR("magenta");
+const char* CYAN_COLOR("cyan");
+const char* TRANSPARENT_COLOR("transparent");
+
+const char* SOLID_UNDERLINE("solid");
+const char* DASHED_UNDERLINE("dashed");
+const char* DOUBLE_UNDERLINE("double");
+
+const char* BEGIN_HORIZONTAL_ALIGNMENT("begin");
+const char* CENTER_HORIZONTAL_ALIGNMENT("center");
+const char* END_HORIZONTAL_ALIGNMENT("end");
 
 } // namespace
 
index c15a11d..b022bb6 100644 (file)
@@ -32,17 +32,16 @@ namespace Text
 {
 namespace
 {
-const std::string COLOR_KEY("color");
-const std::string OFFSET_KEY("offset");
-const std::string BLUR_RADIUS_KEY("blurRadius");
-const std::string WIDTH_KEY("width");
-const std::string HEIGHT_KEY("height");
-const std::string ENABLE_KEY("enable");
-const std::string TYPE_KEY("type");
-const std::string DASH_WIDTH_KEY("dashWidth");
-const std::string DASH_GAP_KEY("dashGap");
-const std::string TRUE_TOKEN("true");
-const std::string FALSE_TOKEN("false");
+const char* COLOR_KEY = "color";
+const char* OFFSET_KEY = "offset";
+const char* BLUR_RADIUS_KEY = "blurRadius";
+const char* WIDTH_KEY = "width";
+const char* HEIGHT_KEY = "height";
+const char* ENABLE_KEY = "enable";
+const char* TYPE_KEY = "type";
+const char* DASH_WIDTH_KEY = "dashWidth";
+const char* DASH_GAP_KEY = "dashGap";
+const char* TRUE_TOKEN = "true";
 } // namespace
 
 bool ParseShadowProperties(const Property::Map& shadowPropertiesMap,
index ce5e860..f087a1e 100644 (file)
@@ -33,14 +33,13 @@ namespace Text
 {
 namespace
 {
-const std::string STYLE_KEY("style");
-const std::string WEIGHT_KEY("weight");
-const std::string WIDTH_KEY("width");
-const std::string SLANT_KEY("slant");
-const std::string FAMILY_KEY("family");
-const std::string TYPE_KEY("type");
-
-const std::string SYSTEM_TOKEN("system");
+const char* WEIGHT_KEY = "weight";
+const char* WIDTH_KEY = "width";
+const char* SLANT_KEY = "slant";
+const char* FAMILY_KEY = "family";
+const char* TYPE_KEY = "type";
+
+const char* SYSTEM_TOKEN = "system";
 
 } // namespace
 
index fc3ffde..fc77c6f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -16,7 +16,7 @@
  */
 
 // CLASS HEADER
-#include "texture-cache-manager.h"
+#include <dali-toolkit/internal/texture-manager/texture-cache-manager.h>
 
 // EXTERNAL HEADERS
 #include <dali/devel-api/common/hash.h>
@@ -193,15 +193,12 @@ Texture TextureCacheManager::GetTexture(const TextureCacheManager::TextureId& te
   return texture;
 }
 
-TextureSet TextureCacheManager::GetExternalTextureSet(const TextureCacheManager::TextureId& textureId)
+TextureCacheManager::ExternalTextureInfo& TextureCacheManager::GetExternalTextureInfo(const TextureCacheManager::TextureId& textureId)
 {
-  TextureSet        textureSet; // empty handle
   TextureCacheIndex cacheIndex = GetCacheIndexFromExternalTextureId(textureId);
-  if(cacheIndex != INVALID_CACHE_INDEX)
-  {
-    textureSet = mExternalTextures[cacheIndex.GetIndex()].textureSet;
-  }
-  return textureSet;
+  DALI_ASSERT_ALWAYS(cacheIndex != INVALID_CACHE_INDEX);
+
+  return mExternalTextures[cacheIndex.GetIndex()];
 }
 
 EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const TextureCacheManager::TextureId& bufferId)
@@ -230,14 +227,14 @@ EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const VisualUrl& u
   return encodedImageBuffer;
 }
 
-std::string TextureCacheManager::AddExternalTexture(const TextureSet& textureSet)
+std::string TextureCacheManager::AddExternalTexture(const TextureSet& textureSet, bool preMultiplied)
 {
   TextureId textureId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE, mExternalTextures.size()));
 
-  TextureCacheManager::ExternalTextureInfo textureInfo(textureId, textureSet);
+  TextureCacheManager::ExternalTextureInfo textureInfo(textureId, textureSet, preMultiplied);
   mExternalTextures.emplace_back(textureInfo);
 
-  DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalTexture() : New texture registered. textureId:%d\n", textureInfo.textureId);
+  DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalTexture() : New texture registered. textureId:%d, preMultiplied:%d\n", textureInfo.textureId, preMultiplied);
 
   return VisualUrl::CreateTextureUrl(std::to_string(textureInfo.textureId));
 }
index c1f1355..75686a1 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXTURE_CACHE_MANAGER_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -70,13 +70,14 @@ public:
   static constexpr TextureId         INVALID_TEXTURE_ID  = TextureManagerType::INVALID_TEXTURE_ID;
   static constexpr TextureCacheIndex INVALID_CACHE_INDEX = TextureManagerType::INVALID_CACHE_INDEX;
 
-  using UseAtlas       = TextureManagerType::UseAtlas;
-  using StorageType    = TextureManagerType::StorageType;
-  using LoadType       = TextureManagerType::LoadType;
-  using LoadState      = TextureManagerType::LoadState;
-  using ReloadPolicy   = TextureManagerType::ReloadPolicy;
-  using MultiplyOnLoad = TextureManagerType::MultiplyOnLoad;
-  using TextureInfo    = TextureManagerType::TextureInfo;
+  using UseAtlas            = TextureManagerType::UseAtlas;
+  using StorageType         = TextureManagerType::StorageType;
+  using LoadType            = TextureManagerType::LoadType;
+  using LoadState           = TextureManagerType::LoadState;
+  using ReloadPolicy        = TextureManagerType::ReloadPolicy;
+  using MultiplyOnLoad      = TextureManagerType::MultiplyOnLoad;
+  using TextureInfo         = TextureManagerType::TextureInfo;
+  using ExternalTextureInfo = TextureManagerType::ExternalTextureInfo;
 
 public:
   /**
@@ -126,11 +127,11 @@ public:
   Texture GetTexture(const TextureCacheManager::TextureId& textureId, uint32_t textureIndex = 0);
 
   /**
-   * @brief Get the external texture set if the texture id is valid
+   * @brief Get the external texture set information if the texture id is valid
    * @param[in] textureId The texture Id to look up
-   * @return the external texture set, or an empty handle if textureId is not valid
+   * @return the external texture information. Assert if textureId is not valid
    */
-  TextureSet GetExternalTextureSet(const TextureCacheManager::TextureId& textureId);
+  TextureCacheManager::ExternalTextureInfo& GetExternalTextureInfo(const TextureCacheManager::TextureId& textureId);
 
   /**
    * @brief Get the encoded image buffer
@@ -149,9 +150,10 @@ public:
   /**
    * Adds an external texture to the texture manager
    * @param[in] texture The texture to add
+   * @param[in] preMultiplied Whether this external texture preMultiplied or not. Default as false.
    * @return string containing the URL for the texture
    */
-  std::string AddExternalTexture(const TextureSet& texture);
+  std::string AddExternalTexture(const TextureSet& texture, bool preMultiplied);
 
   /**
    * Adds an encoded image buffer to the texture manager
@@ -300,23 +302,6 @@ private:
   // Private defined structs.
 
   /**
-   * @brief This struct is used to manage the life-cycle of ExternalTexture url.
-   */
-  struct ExternalTextureInfo
-  {
-    ExternalTextureInfo(const TextureCacheManager::TextureId& textureId,
-                        const TextureSet&                     textureSet)
-    : textureId(textureId),
-      textureSet(textureSet),
-      referenceCount(1u)
-    {
-    }
-    TextureCacheManager::TextureId textureId;
-    TextureSet                     textureSet;
-    std::int16_t                   referenceCount;
-  };
-
-  /**
    * @brief This struct is used to manage the life-cycle of EncodedImageBuffer url.
    */
   struct EncodedImageBufferInfo
index 643fced..c52fe91 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -16,7 +16,7 @@
  */
 
 // CLASS HEADER
-#include "texture-manager-impl.h"
+#include <dali-toolkit/internal/texture-manager/texture-manager-impl.h>
 
 // EXTERNAL HEADERS
 #include <dali/devel-api/adaptor-framework/environment-variable.h>
@@ -320,12 +320,18 @@ TextureSet TextureManager::LoadTexture(
     if(location.size() > 0u)
     {
       TextureId id = std::stoi(location);
-      textureSet   = mTextureCacheManager.GetExternalTextureSet(id);
-      if(textureSet)
+      auto externalTextureInfo   = mTextureCacheManager.GetExternalTextureInfo(id);
+      if(externalTextureInfo.textureSet)
       {
-        preMultiplyOnLoad = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
-        textureId         = id;
-        return textureSet;
+        textureId = id;
+
+        if(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD)
+        {
+          // Change preMultiplyOnLoad value so make caller determine to preMultiplyAlpha or not.
+          // TODO : Should we seperate input and output value?
+          preMultiplyOnLoad = externalTextureInfo.preMultiplied ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+        }
+        return externalTextureInfo.textureSet;
       }
     }
   }
index 6b3f1a7..a72a3aa 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXTURE_MANAGER_IMPL_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -322,9 +322,9 @@ public:
   /**
    * @copydoc TextureCacheManager::AddExternalTexture
    */
-  inline std::string AddExternalTexture(const TextureSet& texture)
+  inline std::string AddExternalTexture(const TextureSet& texture, bool preMultiplied = false)
   {
-    return mTextureCacheManager.AddExternalTexture(texture);
+    return mTextureCacheManager.AddExternalTexture(texture, preMultiplied);
   }
 
   /**
index 74057fc..2d95210 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEXTURE_MANAGER_TYPE_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -234,18 +234,18 @@ struct TextureInfo
               const TextureId&                  maskTextureId,
               const VisualUrl&                  url,
               const Dali::ImageDimensions&      desiredSize,
-              const float&                      scaleFactor,
+              float                             scaleFactor,
               const Dali::FittingMode::Type&    fittingMode,
               const Dali::SamplingMode::Type&   samplingMode,
-              const bool&                       loadSynchronously,
-              const bool&                       cropToMask,
+              bool                              loadSynchronously,
+              bool                              cropToMask,
               const UseAtlas&                   useAtlas,
               const TextureHash&                hash,
-              const bool&                       orientationCorrection,
-              const bool&                       preMultiplyOnLoad,
+              bool                              orientationCorrection,
+              bool                              preMultiplyOnLoad,
               const Dali::AnimatedImageLoading& animatedImageLoading,
-              const std::uint32_t&              frameIndex,
-              const bool&                       loadYuvPlanes)
+              std::uint32_t                     frameIndex,
+              bool                              loadYuvPlanes)
   : url(url),
     desiredSize(desiredSize),
     useSize(desiredSize),
@@ -311,6 +311,27 @@ struct TextureInfo
   bool loadYuvPlanes : 1;         ///< true if the image should be loaded as yuv planes
 };
 
+/**
+ * @brief This struct is used to manage the life-cycle of ExternalTexture url.
+ */
+struct ExternalTextureInfo
+{
+  ExternalTextureInfo(const TextureId&  textureId,
+                      const TextureSet& textureSet,
+                      bool              preMultiplied)
+  : textureId(textureId),
+    textureSet(textureSet),
+    referenceCount(1u),
+    preMultiplied(preMultiplied)
+  {
+  }
+
+  TextureId    textureId;         ///< The TextureId associated with this ExternalTexture
+  TextureSet   textureSet;        ///< The external texture
+  std::int16_t referenceCount;    ///< The reference count of clients using this ExternalTexture
+  bool         preMultiplied : 1; ///< True if the image's color was multiplied by it's alpha
+};
+
 } // namespace TextureManagerType
 
 } // namespace Internal
index f936fdc..05d03c6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -32,9 +32,9 @@ ImageUrl::~ImageUrl()
 {
 }
 
-ImageUrl ImageUrl::New(Texture& texture)
+ImageUrl ImageUrl::New(Texture& texture, bool preMultiplied)
 {
-  Toolkit::Internal::ImageUrlPtr internal = Toolkit::Internal::ImageUrl::New(texture);
+  Toolkit::Internal::ImageUrlPtr internal = Toolkit::Internal::ImageUrl::New(texture, preMultiplied);
   return ImageUrl(internal.Get());
 }
 
index 976f179..0a4a853 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_IMAGE_URL_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -60,9 +60,10 @@ public:
    * @brief Create an initialized ImageUrl.
    *
    * @param[in] texture The texture url is got from external buffer.
+   * @param[in] preMultiplied Whether this external texture preMultiplied or not. Default as false.
    * @return A handle to a newly allocated Dali resource.
    */
-  static ImageUrl New(Texture& texture);
+  static ImageUrl New(Texture& texture, bool preMultiplied = false);
 
   /**
    * @brief Create an initialized ImageUrl.
index 7b91059..8ca77d2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -15,7 +15,7 @@
  */
 
 // CLASS HEADER
-#include "image.h"
+#include <dali-toolkit/public-api/image-loader/image.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/image-loader/texture-manager.h>
@@ -34,29 +34,31 @@ Dali::Toolkit::ImageUrl GenerateUrl(Dali::FrameBuffer frameBuffer, Pixel::Format
 {
   Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, pixelFormat, width, height);
   frameBuffer.AttachColorTexture(texture, 0u, 0u);
-  Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::ImageUrl::New(texture);
+  // TODO : Need to check frameBuffer result use preMultiplied color or not. By default, we use premultiplied
+  Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::ImageUrl::New(texture, true);
   return imageUrl;
 }
 
 Dali::Toolkit::ImageUrl GenerateUrl(const Dali::FrameBuffer frameBuffer, uint8_t index)
 {
   Texture texture = Dali::DevelFrameBuffer::GetColorTexture(frameBuffer, index);
-  Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::ImageUrl::New(texture);
+  // TODO : Need to check frameBuffer result use preMultiplied color or not. By default, we use premultiplied
+  Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::ImageUrl::New(texture, true);
   return imageUrl;
 }
 
-Dali::Toolkit::ImageUrl GenerateUrl(const Dali::PixelData pixelData)
+Dali::Toolkit::ImageUrl GenerateUrl(const Dali::PixelData pixelData, bool preMultiplied)
 {
   Texture texture = Texture::New(TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight());
   texture.Upload(pixelData);
-  Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::ImageUrl::New(texture);
+  Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::ImageUrl::New(texture, preMultiplied);
   return imageUrl;
 }
 
-Dali::Toolkit::ImageUrl GenerateUrl(const Dali::NativeImageSourcePtr nativeImageSource)
+Dali::Toolkit::ImageUrl GenerateUrl(const Dali::NativeImageSourcePtr nativeImageSource, bool preMultiplied)
 {
   Texture texture = Dali::Texture::New(*nativeImageSource);
-  Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::ImageUrl::New(texture);
+  Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::ImageUrl::New(texture, preMultiplied);
   return imageUrl;
 }
 
index 6db6e7e..f3675ef 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_IMAGE_H
 
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -66,18 +66,20 @@ DALI_TOOLKIT_API Dali::Toolkit::ImageUrl GenerateUrl(const Dali::FrameBuffer fra
  * This Url can be used in visuals to render the pixel data.
  * @note This method does not check for duplicates, If same pixel data is entered multiple times, a different URL is returned each time.
  * @param[in] pixelData the pixel data to converted to Url
+ * @param[in] preMultiplied Whether this pixel data preMultiplied or not. Default as false.
  * @return the ImageUrl representing this pixel data
  */
-DALI_TOOLKIT_API Dali::Toolkit::ImageUrl GenerateUrl(const Dali::PixelData pixelData);
+DALI_TOOLKIT_API Dali::Toolkit::ImageUrl GenerateUrl(const Dali::PixelData pixelData, bool preMultiplied = false);
 
 /**
  * @brief Generate a Url from native image source.
  * This Url can be used in visuals to render the native image source.
  * @note This method does not check for duplicates, If same native image source is entered multiple times, a different URL is returned each time.
  * @param[in] nativeImageSource the native image source to converted to Url
+ * @param[in] preMultiplied Whether this native image source preMultiplied or not. Default as false.
  * @return the ImageUrl representing this native image source
  */
-DALI_TOOLKIT_API Dali::Toolkit::ImageUrl GenerateUrl(const Dali::NativeImageSourcePtr nativeImageSource);
+DALI_TOOLKIT_API Dali::Toolkit::ImageUrl GenerateUrl(const Dali::NativeImageSourcePtr nativeImageSource, bool preMultiplied = false);
 
 /**
  * @brief Generate a Url from encoded image buffer.