Merge "Use c-style string when webview loads contents." into devel/master
authordongsug song <dongsug.song@samsung.com>
Thu, 6 Apr 2023 05:41:35 +0000 (05:41 +0000)
committerGerrit Code Review <gerrit@review>
Thu, 6 Apr 2023 05:41:35 +0000 (05:41 +0000)
64 files changed:
automated-tests/src/dali-scene3d-internal/CMakeLists.txt
automated-tests/src/dali-scene3d-internal/utc-Dali-EnvironmentMapTask.cpp [new file with mode: 0644]
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-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
dali-scene3d/internal/common/environment-map-load-task.cpp
dali-scene3d/internal/common/environment-map-load-task.h
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/gltf2-util.cpp
dali-scene3d/internal/loader/gltf2-util.h
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/controls/scene-view/scene-view.cpp
dali-scene3d/public-api/controls/scene-view/scene-view.h
dali-scene3d/public-api/file.list
dali-scene3d/public-api/loader/blend-shape-details.cpp
dali-scene3d/public-api/loader/blend-shape-details.h
dali-scene3d/public-api/loader/environment-definition.cpp
dali-scene3d/public-api/loader/environment-definition.h
dali-scene3d/public-api/loader/environment-map-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.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/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]

index a7a419e..561e753 100755 (executable)
@@ -8,13 +8,16 @@ SET(CAPI_LIB "dali-scene3d")
 # List of test case sources (Only these get parsed for test cases)
 SET(TC_SOURCES
   utc-Dali-DliLoaderImpl.cpp
+  utc-Dali-EnvironmentMapTask.cpp
   utc-Dali-GlbLoaderImpl.cpp
   utc-Dali-Gltf2Asset.cpp
   utc-Dali-Gltf2LoaderImpl.cpp
   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)
diff --git a/automated-tests/src/dali-scene3d-internal/utc-Dali-EnvironmentMapTask.cpp b/automated-tests/src/dali-scene3d-internal/utc-Dali-EnvironmentMapTask.cpp
new file mode 100644 (file)
index 0000000..55b28d0
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include <toolkit-event-thread-callback.h>
+#include <dali-toolkit-test-suite-utils.h>
+#include <string_view>
+#include <dali-scene3d/internal/common/environment-map-load-task.h>
+
+using namespace Dali;
+using namespace Dali::Scene3D::Internal;
+
+typedef IntrusivePtr<EnvironmentMapLoadTask> EnvironmentMapLoadTaskPtr;
+
+bool called = false;
+void LoadComplete()
+{
+  called = true;
+}
+
+int UtcDaliEnvironmentMapTaskSuccess01(void)
+{
+  ToolkitTestApplication    application;
+  EnvironmentMapLoadTaskPtr environmentMapLoadTask;
+
+  auto path              = TEST_RESOURCE_DIR "/forest_radiance.ktx";
+  environmentMapLoadTask = new EnvironmentMapLoadTask(path, Dali::Scene3D::EnvironmentMapType::AUTO, Dali::MakeCallback(LoadComplete));
+  Dali::AsyncTaskManager::Get().AddTask(environmentMapLoadTask);
+
+  DALI_TEST_CHECK(!called);
+  application.SendNotification();
+  application.Render(16);
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  DALI_TEST_CHECK(called);
+  DALI_TEST_CHECK(environmentMapLoadTask->HasSucceeded());
+  DALI_TEST_CHECK(environmentMapLoadTask->GetLoadedTexture());
+  DALI_TEST_EQUALS(Dali::Scene3D::EnvironmentMapType::CUBEMAP, environmentMapLoadTask->GetEnvironmentMapType(), TEST_LOCATION);
+
+  environmentMapLoadTask.Reset();
+  END_TEST;
+}
index 03d2f97..81c9d8c 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);
 
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 9c813df..6e8bf22 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
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 28b929e..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,7 +55,7 @@ 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";
-const char* TEST_DLI_EXERCISE_FILE_NAME = TEST_RESOURCE_DIR "/exercise.dli";
+const char* TEST_DLI_EXERCISE_FILE_NAME            = TEST_RESOURCE_DIR "/exercise.dli";
 
 /**
  * For the diffuse and specular cube map texture.
@@ -148,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;
@@ -311,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;
@@ -363,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;
 }
@@ -1114,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);
 
@@ -1122,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
     {
@@ -1327,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);
   }
@@ -1338,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 6b5f5fb..8bc8239 100644 (file)
@@ -30,10 +30,10 @@ namespace Internal
 EnvironmentMapLoadTask::EnvironmentMapLoadTask(const std::string& environmentMapUrl, Dali::Scene3D::EnvironmentMapType environmentMapType, CallbackBase* callback)
 : AsyncTask(callback),
   mEnvironmentMapUrl(environmentMapUrl),
-  mEnvironmentMapType(environmentMapType),
   mIsReady(true),
   mHasSucceeded(false)
 {
+  mEnvironmentMapData.SetEnvironmentMapType(environmentMapType);
 }
 
 EnvironmentMapLoadTask::~EnvironmentMapLoadTask()
@@ -42,7 +42,6 @@ EnvironmentMapLoadTask::~EnvironmentMapLoadTask()
 
 void EnvironmentMapLoadTask::Process()
 {
-  mEnvironmentMapData.SetEnvironmentMapType(mEnvironmentMapType);
   mHasSucceeded = Scene3D::Loader::LoadEnvironmentMap(mEnvironmentMapUrl, mEnvironmentMapData);
 }
 
@@ -66,6 +65,11 @@ uint32_t EnvironmentMapLoadTask::GetMipmapLevels()
   return (HasSucceeded()) ? mEnvironmentMapData.GetMipmapLevels() : 1u;
 }
 
+Dali::Scene3D::EnvironmentMapType EnvironmentMapLoadTask::GetEnvironmentMapType()
+{
+  return mEnvironmentMapData.GetEnvironmentMapType();
+}
+
 } // namespace Internal
 
 } // namespace Scene3D
index 0bb134f..0517cbd 100644 (file)
  */
 
 // EXTERNAL INCLUDES
+#include <dali/public-api/adaptor-framework/async-task-manager.h>
 #include <dali/public-api/common/intrusive-ptr.h>
 #include <dali/public-api/images/pixel-data.h>
-#include <dali/public-api/adaptor-framework/async-task-manager.h>
 #include <memory>
 
 // INTERNAL INCLUDES
-#include <dali-scene3d/public-api/loader/load-result.h>
-#include <dali-scene3d/public-api/loader/scene-definition.h>
 #include <dali-scene3d/public-api/common/environment-map.h>
 #include <dali-scene3d/public-api/loader/environment-map-data.h>
+#include <dali-scene3d/public-api/loader/load-result.h>
+#include <dali-scene3d/public-api/loader/scene-definition.h>
 
 namespace Dali
 {
@@ -83,6 +83,12 @@ public:
    */
   uint32_t GetMipmapLevels();
 
+  /**
+   * Retrieves EnvironmentMap type of the loaded texture
+   * @return EnvironmentMap type of the loaded texture
+   */
+  Dali::Scene3D::EnvironmentMapType GetEnvironmentMapType();
+
 private:
   // Undefined
   EnvironmentMapLoadTask(const EnvironmentMapLoadTask& task) = delete;
@@ -93,7 +99,6 @@ private:
 private:
   std::string                               mEnvironmentMapUrl;
   Dali::Scene3D::Loader::EnvironmentMapData mEnvironmentMapData;
-  Dali::Scene3D::EnvironmentMapType         mEnvironmentMapType{Dali::Scene3D::EnvironmentMapType::AUTO};
 
   bool mIsReady;
   bool mHasSucceeded;
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..584c667 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, {}, {}, {}};
 
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 e2e6912..c606f8f 100644 (file)
@@ -783,7 +783,7 @@ void SceneView::OnSkyboxLoadComplete()
 
   mSkyboxTexture = mSkyboxLoadTask->GetLoadedTexture();
   Shader skyboxShader;
-  if(mSkyboxEnvironmentMapType == Scene3D::EnvironmentMapType::CUBEMAP)
+  if(mSkyboxLoadTask->GetEnvironmentMapType() == Scene3D::EnvironmentMapType::CUBEMAP)
   {
     skyboxShader = Shader::New(SHADER_SKYBOX_SHADER_VERT.data(), SHADER_SKYBOX_SHADER_FRAG.data());
   }
index 7a6c868..4921731 100644 (file)
@@ -18,4 +18,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 4c46c3a..06cac24 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;
@@ -540,7 +541,8 @@ void AddTextureStage(uint32_t semantic, MaterialDefinition& materialDefinition,
 
 void ConvertMaterial(const gltf2::Material& material, const std::unordered_map<std::string, ImageMetadata>& imageMetaData, decltype(ResourceBundle::mMaterials)& outMaterials, ConversionContext& context)
 {
-  auto getTextureMetaData = [](const std::unordered_map<std::string, ImageMetadata>& metaData, const gltf2::TextureInfo& info) {
+  auto getTextureMetaData = [](const std::unordered_map<std::string, ImageMetadata>& metaData, const gltf2::TextureInfo& info)
+  {
     if(!info.mTexture->mSource->mUri.empty())
     {
       if(auto search = metaData.find(info.mTexture->mSource->mUri.data()); search != metaData.end())
@@ -556,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)));
   }
 
@@ -617,8 +621,8 @@ void ConvertMaterial(const gltf2::Material& material, const std::unordered_map<s
 
   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;
@@ -890,7 +894,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;
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
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..984b2fe
--- /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>
+#include "material-modify-observer.h"
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.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..08c5c73
--- /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;                                              ///< 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;
+  Scene3D::Loader::RendererState::Type mRendererState;
+
+  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..0df438c 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
 {
@@ -31,11 +32,11 @@ Model::Model()
 
 Model::Model(const Model& model) = default;
 
-Model::Model(Model&& rhs) = default;
+Model::Model(Model&& rhs) noexcept = default;
 
 Model& Model::operator=(const Model& model) = default;
 
-Model& Model::operator=(Model&& rhs) = default;
+Model& Model::operator=(Model&& rhs) noexcept = default;
 
 Model::~Model()
 {
@@ -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..8025c64 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.
@@ -113,7 +115,7 @@ public:
    * @SINCE_2_1.41
    * @param[in] rhs A reference to the moved handle
    */
-  Model(Model&& rhs);
+  Model(Model&& rhs) noexcept;
 
   /**
    * @brief Assignment operator.
@@ -131,7 +133,7 @@ public:
    * @param[in] rhs A reference to the moved handle
    * @return A reference to this
    */
-  Model& operator=(Model&& rhs);
+  Model& operator=(Model&& rhs) noexcept;
 
   /**
    * @brief Downcasts an Object handle to 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 68bb529..b6cc0ac 100644 (file)
@@ -31,11 +31,11 @@ SceneView::SceneView()
 
 SceneView::SceneView(const SceneView& sceneView) = default;
 
-SceneView::SceneView(SceneView&& rhs) = default;
+SceneView::SceneView(SceneView&& rhs) noexcept = default;
 
 SceneView& SceneView::operator=(const SceneView& sceneView) = default;
 
-SceneView& SceneView::operator=(SceneView&& rhs) = default;
+SceneView& SceneView::operator=(SceneView&& rhs) noexcept = default;
 
 SceneView::~SceneView()
 {
index 3866879..d4e0008 100644 (file)
@@ -145,7 +145,7 @@ public:
    * @SINCE_2_1.38
    * @param[in] rhs A reference to the moved handle
    */
-  SceneView(SceneView&& rhs);
+  SceneView(SceneView&& rhs) noexcept;
 
   /**
    * @brief Assignment operator.
@@ -163,7 +163,7 @@ public:
    * @param[in] rhs A reference to the moved handle
    * @return A reference to this
    */
-  SceneView& operator=(SceneView&& rhs);
+  SceneView& operator=(SceneView&& rhs) noexcept;
 
   /**
    * @brief Downcasts an Object handle to SceneView.
index e812ae7..0e2d3b9 100644 (file)
@@ -36,4 +36,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 3ef97b2..4cf288c 100644 (file)
@@ -36,7 +36,7 @@ const char* BlendShapes::COMPONENTS("blendShapeComponents");
 
 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;
 
@@ -44,17 +44,21 @@ void BlendShapes::ConfigureProperties(const std::pair<MeshDefinition, MeshGeomet
   char        unnormalizeFactorNameBuffer[64];
   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& blendShape : mesh.first.mBlendShapes)
+  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 b995ed3..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,6 +51,16 @@ 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 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.
@@ -64,7 +76,7 @@ struct DALI_SCENE3D_API BlendShapes
    * @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 5f4605e..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,6 +36,7 @@ std::string GetDaliImagePath()
 }
 
 static constexpr float DEFAULT_INTENSITY = 1.0f;
+
 } // unnamed namespace
 
 namespace Dali::Scene3D::Loader
@@ -43,12 +45,30 @@ namespace
 {
 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 bbfe829..43f3653 100644 (file)
@@ -27,6 +27,8 @@
 // INTERNAL INCLUDES
 #include <dali-scene3d/public-api/loader/ktx-loader.h>
 
+#include <dali/integration-api/debug.h>
+
 namespace Dali
 {
 namespace
@@ -207,7 +209,6 @@ bool LoadEnvironmentMap(const std::string& environmentMapUrl, EnvironmentMapData
   std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
 
   bool successed = (extension == KTX_EXTENSION) ? Dali::Scene3D::Loader::LoadKtxData(environmentMapUrl, environmentMapData) : LoadEnvironmentMapData(environmentMapUrl, environmentMapData);
-
   return successed;
 }
 
index f69ff72..eb87ae1 100644 (file)
@@ -109,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;
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 3d5f6eb..f7c606e 100644 (file)
@@ -914,6 +914,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())
   {
@@ -1071,4 +1083,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 d2baa33..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 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 f73d39b..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
@@ -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 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 4c9fc70..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,7 +44,6 @@ namespace Dali::Scene3D::Loader
 {
 namespace
 {
-const char* JOINT_MATRIX{"jointMatrix"};
 
 const std::map<Property::Type, Constraint (*)(Actor&, Property::Index)>& GetConstraintFactory()
 {
@@ -178,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);
@@ -196,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)
@@ -244,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());
@@ -303,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, 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;
 }
 
@@ -475,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);
 
@@ -969,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());
     });
@@ -1059,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);
     }
   }
 }
@@ -1115,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 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 0e5e499..fc18d40 100644 (file)
@@ -28,4 +28,5 @@ namespace Dali::Scene3D::Loader
 const unsigned int Skinning::MAX_JOINTS = 64;
 
 const char* Skinning::BONE_UNIFORM_NAME = "uBone";
+const char* Skinning::JOINT_MATRIX      = "jointMatrix";
 } // namespace Dali::Scene3D::Loader
index 41a9790..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.
    */
@@ -37,6 +50,7 @@ struct DALI_SCENE3D_API Skinning
    * @brief Name of bone matrix uniform (array).
    */
   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..99c1098
--- /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) noexcept = default;
+
+Material::~Material()
+{
+}
+
+Material& Material::operator=(const Material& handle) = default;
+
+Material& Material::operator=(Material&& rhs) noexcept = 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..d42c838
--- /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) noexcept;
+
+  /**
+   * @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) noexcept;
+
+  /**
+   * @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..583cdd0
--- /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) noexcept = default;
+
+ModelNode::~ModelNode()
+{
+}
+
+ModelNode& ModelNode::operator=(const ModelNode& handle) = default;
+
+ModelNode& ModelNode::operator=(ModelNode&& rhs) noexcept = 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..dc3e717
--- /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) noexcept;
+
+  /**
+   * @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) noexcept;
+
+  /**
+   * @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..eaadc0f
--- /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) noexcept = default;
+
+ModelPrimitive::~ModelPrimitive()
+{
+}
+
+ModelPrimitive& ModelPrimitive::operator=(const ModelPrimitive& handle) = default;
+
+ModelPrimitive& ModelPrimitive::operator=(ModelPrimitive&& rhs) noexcept = 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..97ee9d3
--- /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) noexcept;
+
+  /**
+   * @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) noexcept;
+
+  /**
+   * @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