Merge "FocusFinder use CalculateCurrentScreenExtents" into devel/master
authorEunki Hong <eunkiki.hong@samsung.com>
Wed, 21 Jun 2023 01:10:28 +0000 (01:10 +0000)
committerGerrit Code Review <gerrit@review>
Wed, 21 Jun 2023 01:10:28 +0000 (01:10 +0000)
33 files changed:
automated-tests/src/dali-scene3d/CMakeLists.txt
automated-tests/src/dali-scene3d/utc-Dali-Light.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/utc-Dali-AnimatedVectorImageVisual.cpp
dali-scene3d/internal/common/light-observer.h [moved from dali-scene3d/internal/common/image-based-light-observer.h with 72% similarity]
dali-scene3d/internal/controls/model/model-impl.cpp
dali-scene3d/internal/controls/model/model-impl.h
dali-scene3d/internal/controls/scene-view/scene-view-impl.cpp
dali-scene3d/internal/controls/scene-view/scene-view-impl.h
dali-scene3d/internal/file.list
dali-scene3d/internal/graphics/shaders/default-physically-based-shader.frag
dali-scene3d/internal/light/light-impl.cpp [new file with mode: 0644]
dali-scene3d/internal/light/light-impl.h [new file with mode: 0644]
dali-scene3d/internal/model-components/material-impl.cpp
dali-scene3d/internal/model-components/model-node-impl.cpp
dali-scene3d/internal/model-components/model-node-impl.h
dali-scene3d/internal/model-components/model-primitive-impl.cpp
dali-scene3d/internal/model-components/model-primitive-impl.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/light/light.cpp [new file with mode: 0644]
dali-scene3d/public-api/light/light.h [new file with mode: 0644]
dali-scene3d/public-api/loader/node-definition.cpp
dali-toolkit/internal/graphics/shaders/gradient-visual-shader.frag
dali-toolkit/internal/text/controller/text-controller-relayouter.cpp
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h
dali-toolkit/internal/visuals/gradient/gradient-visual.cpp
dali-toolkit/public-api/dali-toolkit-version.cpp
packaging/dali-toolkit.spec

index 6e8bf22..29b6775 100755 (executable)
@@ -34,6 +34,7 @@ SET(TC_SOURCES
   utc-Dali-StringCallback.cpp
   utc-Dali-Utils.cpp
   utc-Dali-ViewProjection.cpp
+  utc-Dali-Light.cpp
 )
 
 # List of test harness files (Won't get parsed for test cases)
diff --git a/automated-tests/src/dali-scene3d/utc-Dali-Light.cpp b/automated-tests/src/dali-scene3d/utc-Dali-Light.cpp
new file mode 100644 (file)
index 0000000..2ac5bcb
--- /dev/null
@@ -0,0 +1,591 @@
+/*
+ * 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 <dali/devel-api/actors/actor-devel.h>
+#include <stdlib.h>
+#include <iostream>
+
+#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/light/light.h>
+#include <toolkit-event-thread-callback.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+void light_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void light_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+namespace
+{
+const char* TEST_GLTF_FILE_NAME = TEST_RESOURCE_DIR "/BoxAnimated.gltf";
+} // namespace
+
+// Negative test case for a method
+int UtcDaliLightUninitialized(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliLightUninitialized");
+
+  Scene3D::Light light;
+
+  try
+  {
+    // New() must be called to create a Model or it wont be valid.
+    Actor a = Actor::New();
+    light.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(!light);
+  }
+  END_TEST;
+}
+
+// Positive test case for a method
+int UtcDaliLightNew(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliLightNew");
+
+  Scene3D::Light light = Scene3D::Light::New();
+  DALI_TEST_CHECK(light);
+  END_TEST;
+}
+
+// Positive test case for a method
+int UtcDaliLightDownCast(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliLightDownCast");
+
+  Scene3D::Light light = Scene3D::Light::New();
+  BaseHandle     handle(light);
+
+  Scene3D::Light light2 = Scene3D::Light::DownCast(handle);
+  DALI_TEST_CHECK(light);
+  DALI_TEST_CHECK(light2);
+  DALI_TEST_CHECK(light == light2);
+  END_TEST;
+}
+
+// Positive test case for a method
+int UtcDaliLightCopyAndAssignment(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::Light light = Scene3D::Light::New();
+  DALI_TEST_CHECK(light);
+
+  Scene3D::Light copy(light);
+  DALI_TEST_CHECK(light == copy);
+
+  Scene3D::Light assign;
+  DALI_TEST_CHECK(!assign);
+
+  assign = copy;
+  DALI_TEST_CHECK(assign == light);
+
+  END_TEST;
+}
+
+int UtcDaliLightMoveConstructor(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::Light light = Scene3D::Light::New();
+  DALI_TEST_EQUALS(1, light.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+  light.SetProperty(Actor::Property::SENSITIVE, false);
+  DALI_TEST_CHECK(false == light.GetProperty<bool>(Actor::Property::SENSITIVE));
+
+  Scene3D::Light moved = std::move(light);
+  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(!light);
+
+  END_TEST;
+}
+
+int UtcDaliLightMoveAssignment(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::Light light = Scene3D::Light::New();
+  DALI_TEST_EQUALS(1, light.GetBaseObject().ReferenceCount(), TEST_LOCATION);
+  light.SetProperty(Actor::Property::SENSITIVE, false);
+  DALI_TEST_CHECK(false == light.GetProperty<bool>(Actor::Property::SENSITIVE));
+
+  Scene3D::Light moved;
+  moved = std::move(light);
+  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(!light);
+
+  END_TEST;
+}
+
+// For TC coverage
+int UtcDaliLightSize(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::Light light = Scene3D::Light::New();
+  application.GetScene().Add(light);
+
+  application.SendNotification();
+  application.Render();
+
+  light.SetProperty(Dali::Actor::Property::SIZE, Vector3::ONE);
+  DALI_TEST_EQUALS(Vector3::ONE, light.GetProperty<Vector3>(Dali::Actor::Property::SIZE), 0.01f, TEST_LOCATION);
+  DALI_TEST_EQUALS(Vector3::ZERO, light.GetNaturalSize(), 0.01f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  light.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FIXED);
+  DALI_TEST_EQUALS(ResizePolicy::FIXED, light.GetProperty<ResizePolicy::Type>(Dali::Actor::Property::WIDTH_RESIZE_POLICY), TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  float widthForHeight = light.GetWidthForHeight(light.GetProperty<float>(Dali::Actor::Property::SIZE_HEIGHT));
+  float heightForWidth = light.GetHeightForWidth(light.GetProperty<float>(Dali::Actor::Property::SIZE_WIDTH));
+  DALI_TEST_EQUALS(0.0f, widthForHeight, 0.01f, TEST_LOCATION);
+  DALI_TEST_EQUALS(0.0f, heightForWidth, 0.01f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliLightOnScene01(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::Light light = Scene3D::Light::New();
+  application.GetScene().Add(light);
+
+  application.SendNotification();
+  application.Render();
+
+  // Light is added on layer when on scene
+  DALI_TEST_EQUALS(true, light.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE), TEST_LOCATION);
+
+  application.GetScene().Remove(light);
+
+  application.SendNotification();
+  application.Render();
+
+  // Light is removed from layer when on scene
+  DALI_TEST_EQUALS(false, light.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE), TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Add a light on SceneView directly
+int UtcDaliLightAdd01(void)
+{
+  ToolkitTestApplication application;
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  Scene3D::Light light = Scene3D::Light::New();
+  light.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(light, Vector3(1.0f, 0.0f, 0.0f));
+  sceneView.Add(light);
+
+  application.SendNotification();
+  application.Render();
+
+  Renderer renderer = model.FindChildByName("node2").GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+
+  DALI_TEST_EQUALS(1u, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+  auto countPropertyIndex = renderer.GetPropertyIndex("uLightCount");
+  DALI_TEST_CHECK(countPropertyIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, renderer.GetProperty<int32_t>(countPropertyIndex), TEST_LOCATION);
+  auto colorPropertyIndex = renderer.GetPropertyIndex("uLightColor[0]");
+  DALI_TEST_EQUALS(Vector3(0.0f, 0.0f, 1.0f), renderer.GetCurrentProperty<Vector3>(colorPropertyIndex), 0.01f, TEST_LOCATION);
+  auto directionPropertyIndex = renderer.GetPropertyIndex("uLightDirection[0]");
+  DALI_TEST_EQUALS(Vector3(1.0f, 0.0f, 0.0f), renderer.GetCurrentProperty<Vector3>(directionPropertyIndex), 0.01f, TEST_LOCATION);
+
+  light.Enable(false);
+
+  DALI_TEST_EQUALS(0u, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(0, renderer.GetProperty<int32_t>(countPropertyIndex), TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Add a light on an Actor that is child of SceneView.
+int UtcDaliLightAdd02(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  Actor actor = Actor::New();
+  sceneView.Add(actor);
+
+  Scene3D::Light light = Scene3D::Light::New();
+  light.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(light, Vector3(1.0f, 0.0f, 0.0f));
+  actor.Add(light);
+
+  application.SendNotification();
+  application.Render();
+
+  Renderer renderer = model.FindChildByName("node2").GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+
+  DALI_TEST_EQUALS(1u, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+  auto countPropertyIndex = renderer.GetPropertyIndex("uLightCount");
+  DALI_TEST_CHECK(countPropertyIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, renderer.GetProperty<int32_t>(countPropertyIndex), TEST_LOCATION);
+  auto colorPropertyIndex = renderer.GetPropertyIndex("uLightColor[0]");
+  DALI_TEST_EQUALS(Vector3(0.0f, 0.0f, 1.0f), renderer.GetCurrentProperty<Vector3>(colorPropertyIndex), 0.01f, TEST_LOCATION);
+  auto directionPropertyIndex = renderer.GetPropertyIndex("uLightDirection[0]");
+  DALI_TEST_EQUALS(Vector3(1.0f, 0.0f, 0.0f), renderer.GetCurrentProperty<Vector3>(directionPropertyIndex), 0.01f, TEST_LOCATION);
+
+  light.Enable(false);
+
+  DALI_TEST_EQUALS(0u, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(0, renderer.GetProperty<int32_t>(countPropertyIndex), TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Enable a light after it is added on SceneView.
+int UtcDaliLightAdd03(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  Scene3D::Light light = Scene3D::Light::New();
+  light.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(light, Vector3(1.0f, 0.0f, 0.0f));
+  light.Enable(false);
+  sceneView.Add(light);
+
+  application.SendNotification();
+  application.Render();
+
+  Renderer renderer = model.FindChildByName("node2").GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+
+  DALI_TEST_EQUALS(0u, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+  auto countPropertyIndex = renderer.GetPropertyIndex("uLightCount");
+  DALI_TEST_CHECK(countPropertyIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(0, renderer.GetProperty<int32_t>(countPropertyIndex), TEST_LOCATION);
+
+  light.Enable(true);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(1u, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(1, renderer.GetProperty<int32_t>(countPropertyIndex), TEST_LOCATION);
+  auto colorPropertyIndex = renderer.GetPropertyIndex("uLightColor[0]");
+  DALI_TEST_EQUALS(Vector3(0.0f, 0.0f, 1.0f), renderer.GetCurrentProperty<Vector3>(colorPropertyIndex), 0.01f, TEST_LOCATION);
+  auto directionPropertyIndex = renderer.GetPropertyIndex("uLightDirection[0]");
+  DALI_TEST_EQUALS(Vector3(1.0f, 0.0f, 0.0f), renderer.GetCurrentProperty<Vector3>(directionPropertyIndex), 0.01f, TEST_LOCATION);
+
+  light.Enable(false);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(0u, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(0, renderer.GetProperty<int32_t>(countPropertyIndex), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliLightAdd04(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  Scene3D::Light light1 = Scene3D::Light::New();
+  light1.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(light1, Vector3(1.0f, 0.0f, 0.0f));
+  sceneView.Add(light1);
+
+  Scene3D::Light light2 = Scene3D::Light::New();
+  light2.SetProperty(Dali::Actor::Property::COLOR, Color::RED);
+  Dali::DevelActor::LookAt(light2, Vector3(0.0f, 0.0f, -1.0f));
+  sceneView.Add(light2);
+
+  application.SendNotification();
+  application.Render();
+
+  Renderer renderer = model.FindChildByName("node2").GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+
+  DALI_TEST_EQUALS(2u, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+  auto countPropertyIndex = renderer.GetPropertyIndex("uLightCount");
+  DALI_TEST_CHECK(countPropertyIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(2, renderer.GetProperty<int32_t>(countPropertyIndex), TEST_LOCATION);
+  auto colorPropertyIndex1 = renderer.GetPropertyIndex("uLightColor[0]");
+  DALI_TEST_EQUALS(Vector3(0.0f, 0.0f, 1.0f), renderer.GetCurrentProperty<Vector3>(colorPropertyIndex1), 0.01f, TEST_LOCATION);
+  auto directionPropertyIndex1 = renderer.GetPropertyIndex("uLightDirection[0]");
+  DALI_TEST_EQUALS(Vector3(1.0f, 0.0f, 0.0f), renderer.GetCurrentProperty<Vector3>(directionPropertyIndex1), 0.01f, TEST_LOCATION);
+  auto colorPropertyIndex2 = renderer.GetPropertyIndex("uLightColor[1]");
+  DALI_TEST_EQUALS(Vector3(1.0f, 0.0f, 0.0f), renderer.GetCurrentProperty<Vector3>(colorPropertyIndex2), 0.01f, TEST_LOCATION);
+  auto directionPropertyIndex2 = renderer.GetPropertyIndex("uLightDirection[1]");
+  DALI_TEST_EQUALS(Vector3(0.0f, 0.0f, -1.0f), renderer.GetCurrentProperty<Vector3>(directionPropertyIndex2), 0.01f, TEST_LOCATION);
+
+  light1.Enable(false);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(1u, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(1, renderer.GetProperty<int32_t>(countPropertyIndex), TEST_LOCATION);
+
+  // After light1 is disable, shader uniforms of lights are reordered.
+  DALI_TEST_EQUALS(Vector3(1.0f, 0.0f, 0.0f), renderer.GetCurrentProperty<Vector3>(colorPropertyIndex1), 0.01f, TEST_LOCATION);
+  DALI_TEST_EQUALS(Vector3(0.0f, 0.0f, -1.0f), renderer.GetCurrentProperty<Vector3>(directionPropertyIndex1), 0.01f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Check unactivated light in SceneView becomes activated when a light become disabled.
+int UtcDaliLightAdd05(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  uint32_t                    maxLightCount = Scene3D::Light::GetMaximumEnabledLightCount();
+  std::vector<Scene3D::Light> lightList;
+  for(uint32_t i = 0; i < maxLightCount; ++i)
+  {
+    Scene3D::Light light = Scene3D::Light::New();
+    light.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+    Dali::DevelActor::LookAt(light, Vector3(1.0f, 0.0f, 0.0f));
+    sceneView.Add(light);
+    lightList.push_back(light);
+  }
+
+  Scene3D::Light light2 = Scene3D::Light::New();
+  light2.SetProperty(Dali::Actor::Property::COLOR, Color::RED);
+  Dali::DevelActor::LookAt(light2, Vector3(0.0f, 0.0f, -1.0f));
+  sceneView.Add(light2);
+  lightList.push_back(light2);
+
+  application.SendNotification();
+  application.Render();
+
+  Renderer renderer = model.FindChildByName("node2").GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+
+  std::vector<int32_t> enableAnswers;
+  enableAnswers.assign(maxLightCount, 1);
+
+  DALI_TEST_EQUALS(maxLightCount, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+  auto countPropertyIndex = renderer.GetPropertyIndex("uLightCount");
+  DALI_TEST_CHECK(countPropertyIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(static_cast<int32_t>(maxLightCount), renderer.GetProperty<int32_t>(countPropertyIndex), TEST_LOCATION);
+  for(uint32_t i = 0; i < maxLightCount; ++i)
+  {
+    std::string colorStringKey     = std::string("uLightColor[") + std::to_string(i) + "]";
+    auto        colorPropertyIndex = renderer.GetPropertyIndex(colorStringKey);
+    DALI_TEST_EQUALS(Vector3(0.0f, 0.0f, 1.0f), renderer.GetCurrentProperty<Vector3>(colorPropertyIndex), 0.01f, TEST_LOCATION);
+
+    std::string directionStringKey     = std::string("uLightDirection[") + std::to_string(i) + "]";
+    auto        directionPropertyIndex = renderer.GetPropertyIndex(directionStringKey);
+    DALI_TEST_EQUALS(Vector3(1.0f, 0.0f, 0.0f), renderer.GetCurrentProperty<Vector3>(directionPropertyIndex), 0.01f, TEST_LOCATION);
+  }
+
+  lightList[2].Enable(false);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(maxLightCount, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(static_cast<int32_t>(maxLightCount), renderer.GetProperty<int32_t>(countPropertyIndex), TEST_LOCATION);
+  for(uint32_t i = 0; i < maxLightCount; ++i)
+  {
+    Vector3 color, direction;
+    if(i == 2)
+    {
+      color     = Vector3(1.0f, 0.0f, 0.0f);
+      direction = Vector3(0.0f, 0.0f, -1.0f);
+    }
+    else
+    {
+      color     = Vector3(0.0f, 0.0f, 1.0f);
+      direction = Vector3(1.0f, 0.0f, 0.0f);
+    }
+    std::string colorStringKey     = std::string("uLightColor[") + std::to_string(i) + "]";
+    auto        colorPropertyIndex = renderer.GetPropertyIndex(colorStringKey);
+    DALI_TEST_EQUALS(color, renderer.GetCurrentProperty<Vector3>(colorPropertyIndex), 0.01f, TEST_LOCATION);
+
+    std::string directionStringKey     = std::string("uLightDirection[") + std::to_string(i) + "]";
+    auto        directionPropertyIndex = renderer.GetPropertyIndex(directionStringKey);
+    DALI_TEST_EQUALS(direction, renderer.GetCurrentProperty<Vector3>(directionPropertyIndex), 0.01f, TEST_LOCATION);
+  }
+
+  for(uint32_t i = 0; i < lightList.size(); ++i)
+  {
+    if(i == 2)
+    {
+      DALI_TEST_EQUALS(false, lightList[i].IsEnabled(), TEST_LOCATION);
+    }
+    else
+    {
+      DALI_TEST_EQUALS(true, lightList[i].IsEnabled(), TEST_LOCATION);
+    }
+  }
+
+  END_TEST;
+}
+
+int UtcDaliLightModelAddAndRemove(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Light light = Scene3D::Light::New();
+  light.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(light, Vector3(1.0f, 0.0f, 0.0f));
+  sceneView.Add(light);
+
+  application.SendNotification();
+  application.Render();
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  Renderer renderer = model.FindChildByName("node2").GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+
+  DALI_TEST_EQUALS(1u, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+  auto countPropertyIndex = renderer.GetPropertyIndex("uLightCount");
+  DALI_TEST_CHECK(countPropertyIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, renderer.GetProperty<int32_t>(countPropertyIndex), TEST_LOCATION);
+
+  auto colorPropertyIndex = renderer.GetPropertyIndex("uLightColor[0]");
+  DALI_TEST_EQUALS(Vector3(0.0f, 0.0f, 1.0f), renderer.GetCurrentProperty<Vector3>(colorPropertyIndex), 0.01f, TEST_LOCATION);
+  auto directionPropertyIndex = renderer.GetPropertyIndex("uLightDirection[0]");
+  DALI_TEST_EQUALS(Vector3(1.0f, 0.0f, 0.0f), renderer.GetCurrentProperty<Vector3>(directionPropertyIndex), 0.01f, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(1u, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+
+  model.Unparent();
+
+  DALI_TEST_EQUALS(1u, sceneView.GetActivatedLightCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(0, renderer.GetProperty<int32_t>(countPropertyIndex), TEST_LOCATION);
+
+  END_TEST;
+}
index 92cc203..6a5a089 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -974,8 +974,7 @@ int UtcDaliAnimatedVectorImageVisualAnimationFinishedSignal(void)
 
   Property::Map propertyMap;
   propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
-    .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
-    .Add(DevelImageVisual::Property::LOOP_COUNT, 3);
+    .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME);
 
   Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
   DALI_TEST_CHECK(visual);
@@ -991,14 +990,24 @@ int UtcDaliAnimatedVectorImageVisualAnimationFinishedSignal(void)
 
   application.GetScene().Add(actor);
 
+  application.SendNotification();
+  application.Render();
+
+  // Trigger count is 1 - render a frame
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  propertyMap.Clear();
+  propertyMap.Add(DevelImageVisual::Property::LOOP_COUNT, 3);
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, propertyMap);
+
   Property::Map attributes;
   DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes);
 
   application.SendNotification();
   application.Render();
 
-  // Wait for animation finish - render, finish
-  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  // Wait for animation finish
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
 
   Property::Map    map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
   Property::Value* value = map.Find(DevelImageVisual::Property::PLAY_STATE);
@@ -1269,8 +1278,6 @@ int UtcDaliAnimatedVectorImageVisualStopBehavior(void)
   Property::Map propertyMap;
   propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
     .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
-    .Add(DevelImageVisual::Property::LOOP_COUNT, 3)
-    .Add(DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::FIRST_FRAME)
     .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false);
 
   Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
@@ -1285,14 +1292,25 @@ int UtcDaliAnimatedVectorImageVisualStopBehavior(void)
 
   application.GetScene().Add(actor);
 
+  application.SendNotification();
+  application.Render();
+
+  // Trigger count is 2 - load & render a frame
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  propertyMap.Clear();
+  propertyMap.Add(DevelImageVisual::Property::LOOP_COUNT, 3);
+  propertyMap.Add(DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::FIRST_FRAME);
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, propertyMap);
+
   Property::Map attributes;
   DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes);
 
   application.SendNotification();
   application.Render();
 
-  // Trigger count is 3 - load, render, animation finished
-  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION);
+  // Trigger count is 1 - animation finished
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
 
   Property::Map    map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
   Property::Value* value = map.Find(DevelImageVisual::Property::CURRENT_FRAME_NUMBER);
@@ -1377,9 +1395,6 @@ int UtcDaliAnimatedVectorImageVisualLoopingMode(void)
   Property::Map propertyMap;
   propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
     .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
-    .Add(DevelImageVisual::Property::LOOP_COUNT, 3)
-    .Add(DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::LAST_FRAME)
-    .Add(DevelImageVisual::Property::LOOPING_MODE, DevelImageVisual::LoopingMode::AUTO_REVERSE)
     .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false);
 
   Visual::Base visual = VisualFactory::Get().CreateVisual(propertyMap);
@@ -1394,14 +1409,26 @@ int UtcDaliAnimatedVectorImageVisualLoopingMode(void)
 
   application.GetScene().Add(actor);
 
+  application.SendNotification();
+  application.Render();
+
+  // Trigger count is 2 - load, render
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  propertyMap.Clear();
+  propertyMap.Add(DevelImageVisual::Property::LOOP_COUNT, 3);
+  propertyMap.Add(DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::LAST_FRAME);
+  propertyMap.Add(DevelImageVisual::Property::LOOPING_MODE, DevelImageVisual::LoopingMode::AUTO_REVERSE);
+  DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, propertyMap);
+
   Property::Map attributes;
   DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, attributes);
 
   application.SendNotification();
   application.Render();
 
-  // Trigger count is 3 - load, render, animation finished
-  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION);
+  // Trigger count is 1 - animation finished
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
 
   Property::Map    map   = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
   Property::Value* value = map.Find(DevelImageVisual::Property::CURRENT_FRAME_NUMBER);
@@ -1566,6 +1593,12 @@ int UtcDaliAnimatedVectorImageVisualMultipleInstances(void)
 
   application.GetScene().Add(actor1);
 
+  application.SendNotification();
+  application.Render();
+
+  // Trigger count is 2 - load & render a frame
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
   propertyMap.Clear();
   propertyMap.Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
     .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
@@ -1585,8 +1618,8 @@ int UtcDaliAnimatedVectorImageVisualMultipleInstances(void)
   application.SendNotification();
   application.Render();
 
-  // Trigger count is 4 - load & render a frame for each instance
-  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(4), true, TEST_LOCATION);
+  // Trigger count is 2 - load & render a frame
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
 
   DevelControl::DoAction(actor2, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PLAY, Property::Map());
 
@@ -1,5 +1,5 @@
-#ifndef DALI_SCENE3D_INTERNAL_IMAGE_BASED_LIGHT_OBSERVER_H
-#define DALI_SCENE3D_INTERNAL_IMAGE_BASED_LIGHT_OBSERVER_H
+#ifndef DALI_SCENE3D_INTERNAL_LIGHT_OBSERVER_H
+#define DALI_SCENE3D_INTERNAL_LIGHT_OBSERVER_H
 
 /*
  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
 #include <dali-toolkit/public-api/dali-toolkit-common.h>
 #include <dali/public-api/rendering/texture.h>
 
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/light/light.h>
+
 namespace Dali
 {
 namespace Scene3D
 {
 namespace Internal
 {
-class ImageBasedLightObserver
+class LightObserver
 {
 public:
   /**
    * @brief Constructor.
    */
-  ImageBasedLightObserver() = default;
+  LightObserver() = default;
 
   /**
    * @brief Virtual destructor.
    */
-  virtual ~ImageBasedLightObserver() = default;
+  virtual ~LightObserver() = default;
 
   /**
    * @brief Notifies Image Based Light Textures are changed by parent SceneView.
@@ -57,6 +60,21 @@ public:
    * @param[in] scaleFactor scale factor that controls light source intensity in [0.0f, 1.0f].
    */
   virtual void NotifyImageBasedLightScaleFactor(float scaleFactor) = 0;
+
+  /**
+   * @brief Notifies new light is added on SceneView
+   *
+   * @param[in] lightIndex Index of added light.
+   * @param[in] light Added light.
+   */
+  virtual void NotifyLightAdded(uint32_t lightIndex, Scene3D::Light light) = 0;
+
+  /**
+   * @brief Notifies a light is removed from SceneView
+   *
+   * @param[in] lightIndex Index of light to be removed.
+   */
+  virtual void NotifyLightRemoved(uint32_t lightIndex) = 0;
 };
 
 } // namespace Internal
@@ -65,4 +83,4 @@ public:
 
 } // namespace Dali
 
-#endif // DALI_SCENE3D_INTERNAL_IMAGE_BASED_LIGHT_OBSERVER_H
+#endif // DALI_SCENE3D_INTERNAL_LIGHT_OBSERVER_H
index 497c9a2..73823d5 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/light/light-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>
@@ -117,7 +118,8 @@ void ConfigureBlendShapeShaders(
   Dali::Scene3D::Loader::ResourceBundle& resources, const Dali::Scene3D::Loader::SceneDefinition& scene, Actor root, std::vector<Dali::Scene3D::Loader::BlendshapeShaderConfigurationRequest>&& requests)
 {
   std::vector<std::string> errors;
-  auto                     onError = [&errors](const std::string& msg) { errors.push_back(msg); };
+  auto                     onError = [&errors](const std::string& msg)
+  { errors.push_back(msg); };
   if(!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError))
   {
     Dali::Scene3D::Loader::ExceptionFlinger flinger(ASSERT_LOCATION);
@@ -170,6 +172,46 @@ void AddModelTreeToAABB(BoundingVolume& AABB, const Dali::Scene3D::Loader::Scene
   }
 }
 
+void AddLightRecursively(Scene3D::ModelNode node, Scene3D::Light light, uint32_t lightIndex)
+{
+  if(!node)
+  {
+    return;
+  }
+
+  GetImplementation(node).AddLight(light, lightIndex);
+
+  uint32_t childrenCount = node.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
+  {
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(childNode)
+    {
+      AddLightRecursively(childNode, light, lightIndex);
+    }
+  }
+}
+
+void RemoveLightRecursively(Scene3D::ModelNode node, uint32_t lightIndex)
+{
+  if(!node)
+  {
+    return;
+  }
+
+  GetImplementation(node).RemoveLight(lightIndex);
+
+  uint32_t childrenCount = node.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
+  {
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(childNode)
+    {
+      RemoveLightRecursively(childNode, lightIndex);
+    }
+  }
+}
+
 } // anonymous namespace
 
 Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
@@ -240,6 +282,15 @@ void Model::AddModelNode(Scene3D::ModelNode modelNode)
     UpdateImageBasedLightScaleFactor();
   }
 
+  uint32_t maxLightCount = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
+  for(uint32_t i = 0; i < maxLightCount; ++i)
+  {
+    if(mLights[i])
+    {
+      AddLightRecursively(modelNode, mLights[i], i);
+    }
+  }
+
   if(Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
   {
     NotifyResourceReady();
@@ -250,6 +301,14 @@ void Model::RemoveModelNode(Scene3D::ModelNode modelNode)
 {
   if(mModelRoot)
   {
+    uint32_t maxLightCount = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
+    for(uint32_t i = 0; i < maxLightCount; ++i)
+    {
+      if(mLights[i])
+      {
+        RemoveLightRecursively(modelNode, i);
+      }
+    }
     mModelRoot.Remove(modelNode);
   }
 }
@@ -466,6 +525,7 @@ void Model::OnInitialize()
 {
   // Make ParentOrigin as Center.
   Self().SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  mLights.resize(Scene3D::Internal::Light::GetMaximumEnabledLightCount());
 }
 
 void Model::OnSceneConnection(int depth)
@@ -633,7 +693,6 @@ void Model::UpdateImageBasedLightScaleFactorRecursively(Scene3D::ModelNode node,
     return;
   }
 
-  node.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), iblScaleFactor);
   GetImplementation(node).SetImageBasedLightScaleFactor(iblScaleFactor);
 
   uint32_t childrenCount = node.GetChildCount();
@@ -742,6 +801,21 @@ void Model::NotifyImageBasedLightScaleFactor(float scaleFactor)
   }
 }
 
+void Model::NotifyLightAdded(uint32_t lightIndex, Scene3D::Light light)
+{
+  mLights[lightIndex] = light;
+  AddLightRecursively(mModelRoot, light, lightIndex);
+}
+
+void Model::NotifyLightRemoved(uint32_t lightIndex)
+{
+  if(mLights[lightIndex])
+  {
+    RemoveLightRecursively(mModelRoot, lightIndex);
+    mLights[lightIndex].Reset();
+  }
+}
+
 void Model::OnModelLoadComplete()
 {
   if(!mModelLoadTask->HasSucceeded())
@@ -772,6 +846,15 @@ void Model::OnModelLoadComplete()
     mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular;
   }
 
+  uint32_t maxLightCount = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
+  for(uint32_t i = 0; i < maxLightCount; ++i)
+  {
+    if(mLights[i])
+    {
+      AddLightRecursively(mModelRoot, mLights[i], i);
+    }
+  }
+
   UpdateImageBasedLightTexture();
   UpdateImageBasedLightScaleFactor();
   Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
@@ -888,7 +971,8 @@ void Model::CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene)
   mAnimations.clear();
   if(!mModelLoadTask->GetAnimations().empty())
   {
-    auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property) {
+    auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property)
+    {
       if(property.mNodeIndex == Scene3D::Loader::INVALID_INDEX)
       {
         return mModelRoot.FindChildByName(property.mNodeName);
index d7924bf..52aed7d 100644 (file)
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/common/environment-map-load-task.h>
-#include <dali-scene3d/internal/common/image-based-light-observer.h>
+#include <dali-scene3d/internal/common/light-observer.h>
 #include <dali-scene3d/internal/common/model-load-task.h>
 #include <dali-scene3d/public-api/controls/model/model.h>
 #include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
+#include <dali-scene3d/public-api/light/light.h>
 #include <dali-scene3d/public-api/loader/load-result.h>
 #include <dali-scene3d/public-api/model-components/model-node.h>
 
@@ -47,7 +48,7 @@ namespace Internal
 /**
  * @brief Impl class for Model.
  */
-class Model : public Dali::Toolkit::Internal::Control, public ImageBasedLightObserver
+class Model : public Dali::Toolkit::Internal::Control, public LightObserver
 {
 public:
   using AnimationData = std::pair<std::string, Dali::Animation>;
@@ -247,17 +248,27 @@ private:
    */
   void ApplyCameraTransform(Dali::CameraActor camera) const;
 
-public: // Overrides ImageBasedLightObserver Methods.
+public: // Overrides LightObserver Methods.
   /**
-   * @copydoc Dali::Scene3D::Internal::ImageBasedLightObserver::NotifyImageBasedLightTexture()
+   * @copydoc Dali::Scene3D::Internal::LightObserver::NotifyImageBasedLightTexture()
    */
   void NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor, uint32_t specularMipmapLevels) override;
 
   /**
-   * @copydoc Dali::Scene3D::Internal::ImageBasedLightObserver::NotifyImageBasedLightScaleFactor()
+   * @copydoc Dali::Scene3D::Internal::LightObserver::NotifyImageBasedLightScaleFactor()
    */
   void NotifyImageBasedLightScaleFactor(float scaleFactor) override;
 
+  /**
+   * @copydoc Dali::Scene3D::Internal::LightObserver::NotifyLightAdded()
+   */
+  void NotifyLightAdded(uint32_t lightIndex, Scene3D::Light light) override;
+
+  /**
+   * @copydoc Dali::Scene3D::Internal::LightObserver::NotifyLightRemoved()
+   */
+  void NotifyLightRemoved(uint32_t lightIndex) override;
+
 private:
   /**
    * @brief Asynchronously model loading finished.
@@ -323,6 +334,9 @@ private:
   WeakHandle<Scene3D::SceneView> mParentSceneView;
   Dali::PropertyNotification     mSizeNotification;
 
+  // Light
+  std::vector<Scene3D::Light> mLights;
+
   // Asynchronous loading variable
   ModelLoadTaskPtr          mModelLoadTask;
   EnvironmentMapLoadTaskPtr mIblDiffuseLoadTask;
index 415d116..9c599c5 100644 (file)
@@ -39,6 +39,7 @@
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/controls/model/model-impl.h>
 #include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-scene3d/internal/light/light-impl.h>
 
 using namespace Dali;
 
@@ -61,6 +62,7 @@ DALI_TYPE_REGISTRATION_END()
 
 Property::Index   RENDERING_BUFFER    = Dali::Toolkit::Control::CONTROL_PROPERTY_END_INDEX + 1;
 constexpr int32_t DEFAULT_ORIENTATION = 0;
+constexpr int32_t INVALID_INDEX = -1;
 
 static constexpr std::string_view SKYBOX_INTENSITY_STRING = "uIntensity";
 
@@ -273,16 +275,28 @@ void SceneView::SelectCamera(const std::string& name)
   UpdateCamera(GetCamera(name));
 }
 
-void SceneView::RegisterSceneItem(Scene3D::Internal::ImageBasedLightObserver* item)
+void SceneView::RegisterSceneItem(Scene3D::Internal::LightObserver* item)
 {
   if(item)
   {
     item->NotifyImageBasedLightTexture(mDiffuseTexture, mSpecularTexture, mIblScaleFactor, mSpecularMipmapLevels);
+
+    if(mActivatedLightCount > 0)
+    {
+      uint32_t maxLightCount = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
+      for(uint32_t i = 0; i < maxLightCount; ++i)
+      {
+        if(mActivatedLights[i])
+        {
+          item->NotifyLightAdded(i, mActivatedLights[i]);
+        }
+      }
+    }
     mItems.push_back(item);
   }
 }
 
-void SceneView::UnregisterSceneItem(Scene3D::Internal::ImageBasedLightObserver* item)
+void SceneView::UnregisterSceneItem(Scene3D::Internal::LightObserver* item)
 {
   if(item)
   {
@@ -290,6 +304,17 @@ void SceneView::UnregisterSceneItem(Scene3D::Internal::ImageBasedLightObserver*
     {
       if(mItems[i] == item)
       {
+        if(mActivatedLightCount > 0)
+        {
+          uint32_t maxLightCount = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
+          for(uint32_t i = 0; i < maxLightCount; ++i)
+          {
+            if(mActivatedLights[i])
+            {
+              item->NotifyLightRemoved(i);
+            }
+          }
+        }
         mItems.erase(mItems.begin() + i);
         break;
       }
@@ -412,6 +437,64 @@ float SceneView::GetImageBasedLightScaleFactor() const
   return mIblScaleFactor;
 }
 
+void SceneView::AddLight(Scene3D::Light light)
+{
+  bool enabled = AddLightInternal(light);
+  mLights.push_back(std::make_pair(light, enabled));
+}
+
+void SceneView::RemoveLight(Scene3D::Light light)
+{
+  if(mActivatedLights.empty())
+  {
+    return;
+  }
+
+  bool needToDisable = false;
+  for(uint32_t i = 0; i < mLights.size(); ++i)
+  {
+    if(mLights[i].first == light)
+    {
+      // If mLights[i].second is true, it means the light is currently activated in Scene.
+      // Then it should be removed from mActivatedLights list too.
+      needToDisable = mLights[i].second;
+      mLights.erase(mLights.begin() + i);
+      break;
+    }
+  }
+
+  uint32_t maxNumberOfLight = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
+  if(needToDisable)
+  {
+    uint32_t removedIndex = RemoveLightInternal(light);
+    if(mActivatedLightCount < maxNumberOfLight && mLights.size() >= maxNumberOfLight)
+    {
+      for(auto && lightItem : mLights)
+      {
+        if(lightItem.second == false)
+        {
+          lightItem.second = AddLightInternal(lightItem.first);
+          break;
+        }
+      }
+    }
+
+    // To remove empty entity of mActivatedLights, moves last object to empty position.
+    // Because one Light is removed, mActivatedLights[mActivatedLightCount] is current last object of the list.
+    if(!mActivatedLights[removedIndex] && mActivatedLightCount > 0 && removedIndex < mActivatedLightCount)
+    {
+      Scene3D::Light reorderingLight = mActivatedLights[mActivatedLightCount];
+      RemoveLightInternal(reorderingLight);
+      AddLightInternal(reorderingLight);
+    }
+  }
+}
+
+uint32_t SceneView::GetActivatedLightCount() const
+{
+  return mActivatedLightCount;
+}
+
 void SceneView::UseFramebuffer(bool useFramebuffer)
 {
   if(mUseFrameBuffer != useFramebuffer)
@@ -862,6 +945,66 @@ void SceneView::NotifyImageBasedLightTextureChange()
   }
 }
 
+bool SceneView::AddLightInternal(Scene3D::Light light)
+{
+  uint32_t maxNumberOfLight = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
+  if(mActivatedLightCount == 0)
+  {
+    mActivatedLights.resize(maxNumberOfLight);
+  }
+
+  bool enabled = false;
+  if(mActivatedLightCount < maxNumberOfLight)
+  {
+    uint32_t newLightIndex = 0u;
+    for(; newLightIndex < maxNumberOfLight; ++newLightIndex)
+    {
+      if(!mActivatedLights[newLightIndex])
+      {
+        mActivatedLights[newLightIndex] = light;
+        break;
+      }
+    }
+
+    for(auto&& item : mItems)
+    {
+      if(item)
+      {
+        item->NotifyLightAdded(newLightIndex, light);
+      }
+    }
+
+    mActivatedLightCount++;
+    enabled = true;
+  }
+  return enabled;
+}
+
+int32_t SceneView::RemoveLightInternal(Scene3D::Light light)
+{
+  int32_t removedIndex = INVALID_INDEX;
+  uint32_t maxNumberOfLight = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
+  for(uint32_t i = 0; i < maxNumberOfLight; ++i)
+  {
+    if(mActivatedLights[i] == light)
+    {
+      for(auto&& item : mItems)
+      {
+        if(item)
+        {
+          item->NotifyLightRemoved(i);
+        }
+      }
+      mActivatedLights[i].Reset();
+      mActivatedLightCount--;
+      removedIndex = i;
+      break;
+    }
+  }
+
+  return removedIndex;
+}
+
 } // namespace Internal
 } // namespace Scene3D
 } // namespace Dali
index 3aa93b4..cf117f9 100644 (file)
@@ -33,7 +33,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/common/environment-map-load-task.h>
-#include <dali-scene3d/internal/common/image-based-light-observer.h>
+#include <dali-scene3d/internal/common/light-observer.h>
 #include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
 
 namespace Dali
@@ -105,14 +105,14 @@ public:
    *
    * @param[in] item scene observer to be registered.
    */
-  void RegisterSceneItem(Scene3D::Internal::ImageBasedLightObserver* item);
+  void RegisterSceneItem(Scene3D::Internal::LightObserver* item);
 
   /**
    * @brief Unregister an item
    *
    * @param[in] item scene observer to be unregistered.
    */
-  void UnregisterSceneItem(Scene3D::Internal::ImageBasedLightObserver* item);
+  void UnregisterSceneItem(Scene3D::Internal::LightObserver* item);
 
   /**
    * @copydoc SceneView::SetImageBasedLightSource()
@@ -130,6 +130,30 @@ public:
   float GetImageBasedLightScaleFactor() const;
 
   /**
+   * @brief Adds a Light object to this SceneView.
+   * Multiple light object can be added on this SceneView
+   * But the number of lights those actually turned on has limitation.
+   * Scene3D::Light::GetMaximumEnabledLightCount() method can be used to know the maximum namber.
+   *
+   * @param[in] light Light object to be added.
+   */
+  void AddLight(Scene3D::Light light);
+
+  /**
+   * @brief Removes a Light object to this SceneView.
+   * If the light is currently turned on, and SceneView has lights more than maximum number of enabled light.
+   * One of light in queue is turned on after this light is removed.
+   *
+   * @param[in] light Light object to be removed.
+   */
+  void RemoveLight(Scene3D::Light light);
+
+  /**
+   * @copydoc SceneView::GetActivatedLightCount()
+   */
+  uint32_t GetActivatedLightCount() const;
+
+  /**
    * @copydoc SceneView::UseFramebuffer()
    */
   void UseFramebuffer(bool useFramebuffer);
@@ -291,26 +315,47 @@ private:
    */
   void NotifyImageBasedLightTextureChange();
 
+  /**
+   * @brief Internal method to add an Light object to this SceneView.
+   *
+   * @param[in] light Light object to be added.
+   * @return True if successed to enable.
+   */
+  bool AddLightInternal(Scene3D::Light light);
+
+  /**
+   * @brief Internal method to remove an Light object to this SceneView.
+   *
+   * @param[in] light Light object to be added.
+   * @return Index of removed light in enabled light list. If failed to remove it returns negative value;
+   */
+  int32_t RemoveLightInternal(Scene3D::Light light);
+
 private:
   Toolkit::Visual::Base mVisual;
 
   /////////////////////////////////////////////////////////////
   // FrameBuffer and Rendertask to render child objects as a 3D Scene
-  Dali::WeakHandle<Dali::Window>                           mWindow;
-  Integration::SceneHolder                                 mSceneHolder;
-  CameraActor                                              mDefaultCamera;
-  CameraActor                                              mSelectedCamera;
-  std::vector<CameraActor>                                 mCameras;
-  std::vector<Scene3D::Internal::ImageBasedLightObserver*> mItems;
-  Dali::FrameBuffer                                        mFrameBuffer;
-  Dali::Texture                                            mTexture;
-  Dali::RenderTask                                         mRenderTask;
-  Layer                                                    mRootLayer;
-  int32_t                                                  mWindowOrientation;
-  Dali::Actor                                              mSkybox;
-  Quaternion                                               mSkyboxOrientation;
-  float                                                    mSkyboxIntensity{1.0f};
-  uint8_t                                                  mFrameBufferMultiSamplingLevel{0u};
+  Dali::WeakHandle<Dali::Window>                 mWindow;
+  Integration::SceneHolder                       mSceneHolder;
+  CameraActor                                    mDefaultCamera;
+  CameraActor                                    mSelectedCamera;
+  std::vector<CameraActor>                       mCameras;
+  std::vector<Scene3D::Internal::LightObserver*> mItems;
+  Dali::FrameBuffer                              mFrameBuffer;
+  Dali::Texture                                  mTexture;
+  Dali::RenderTask                               mRenderTask;
+  Layer                                          mRootLayer;
+  int32_t                                        mWindowOrientation;
+  Dali::Actor                                    mSkybox;
+  Quaternion                                     mSkyboxOrientation;
+  float                                          mSkyboxIntensity{1.0f};
+  uint8_t                                        mFrameBufferMultiSamplingLevel{0u};
+
+  // Light
+  std::vector<std::pair<Scene3D::Light, bool>> mLights; // Pair of Light object and flag that denotes the light is currently activated or not.
+  std::vector<Scene3D::Light>                  mActivatedLights;
+  uint32_t                                     mActivatedLightCount{0u};
 
   // Asynchronous Loading.
   EnvironmentMapLoadTaskPtr mSkyboxLoadTask;
index 3706b72..56200d2 100644 (file)
@@ -10,6 +10,7 @@ set(scene3d_src_files ${scene3d_src_files}
        ${scene3d_internal_dir}/common/model-load-task.cpp
        ${scene3d_internal_dir}/controls/model/model-impl.cpp
        ${scene3d_internal_dir}/controls/scene-view/scene-view-impl.cpp
+       ${scene3d_internal_dir}/light/light-impl.cpp
        ${scene3d_internal_dir}/loader/dli-loader-impl.cpp
        ${scene3d_internal_dir}/loader/gltf2-asset.cpp
        ${scene3d_internal_dir}/loader/gltf2-util.cpp
index e701162..9a436f5 100644 (file)
@@ -72,6 +72,12 @@ uniform sampler2D sSpecular;
 uniform sampler2D sSpecularColor;
 #endif
 
+// For Light (Currently Directional Only)
+#define MAX_LIGHTS 5
+uniform mediump int uLightCount;
+uniform mediump vec3 uLightDirection[MAX_LIGHTS];
+uniform mediump vec3 uLightColor[MAX_LIGHTS];
+
 //// For IBL
 uniform sampler2D sbrdfLUT;
 uniform samplerCube sDiffuseEnvSampler;
@@ -94,6 +100,7 @@ in highp vec3 vPositionToCamera;
 out vec4 FragColor;
 
 const float c_MinRoughness = 0.04;
+const float M_PI = 3.141592653589793;
 
 vec3 linear(vec3 color)
 {
@@ -201,6 +208,42 @@ void main()
 
   lowp vec3 color = (diffuse + specular) * uIblIntensity;
 
+  // Punctual Light
+  if(uLightCount > 0)
+  {
+    // Compute reflectance.
+    lowp float reflectance = max(max(f0.r, f0.g), f0.b);
+    lowp float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
+    lowp float r = perceptualRoughness * perceptualRoughness;
+    lowp float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));
+    mediump float roughnessSq = r * r;
+    lowp vec3 diffuseColorPunctual = baseColor.rgb * (vec3(1.0) - f0);
+    diffuseColorPunctual *= ( 1.0 - metallic );
+
+    for(int i = 0; i < uLightCount; ++i)
+    {
+      mediump vec3 l = normalize(-uLightDirection[i]);               // Vector from surface point to light
+      mediump vec3 h = normalize(l+v);                              // Half vector between both l and v
+      mediump float VdotH = dot(v, h);
+      lowp vec3 specularReflection = f0 + (reflectance90 - f0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);
+
+      mediump float NdotL = clamp(dot(n, l), 0.001, 1.0);
+      lowp float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));
+      lowp float geometricOcclusion = attenuationL * attenuationV;
+
+      mediump float NdotH = dot(n, h);
+      lowp float f = (NdotH * roughnessSq - NdotH) * NdotH + 1.0;
+      lowp float microfacetDistribution = roughnessSq / (M_PI * f * f);;
+
+      // Calculation of analytical lighting contribution
+      lowp vec3 diffuseContrib = ( 1.0 - specularReflection ) * ( diffuseColorPunctual / M_PI );
+      lowp vec3 specContrib = specularReflection * geometricOcclusion * microfacetDistribution / ( 4.0 * NdotL * NdotV );
+
+      // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
+      color += NdotL * uLightColor[i] * (diffuseContrib + specContrib);
+    }
+  }
+
 #ifdef OCCLUSION
   lowp float ao = texture(sOcclusion, vUV).r;
   color = mix(color, color * ao, uOcclusionStrength);
diff --git a/dali-scene3d/internal/light/light-impl.cpp b/dali-scene3d/internal/light/light-impl.cpp
new file mode 100644 (file)
index 0000000..00eb0f4
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * 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/light/light-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/controls/scene-view/scene-view-impl.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+namespace
+{
+static constexpr uint32_t         MAX_NUMBER_OF_LIGHT = 5;
+static constexpr std::string_view LIGHT_COUNT_STRING("uLightCount");
+static constexpr std::string_view LIGHT_DIRECTION_STRING("uLightDirection");
+static constexpr std::string_view LIGHT_COLOR_STRING("uLightColor");
+
+/**
+ * Creates control through type registry
+ */
+BaseHandle Create()
+{
+  return Scene3D::Light::New();
+}
+
+// Setup properties, signals and actions using the type-registry.
+DALI_TYPE_REGISTRATION_BEGIN(Scene3D::Light, Dali::CustomActor, Create);
+DALI_TYPE_REGISTRATION_END()
+} // unnamed namespace
+
+Dali::Scene3D::Light Light::New()
+{
+  // Create the implementation, temporarily owned on stack
+  IntrusivePtr<Light> nodeImpl = new Light();
+
+  // Pass ownership to handle
+  Scene3D::Light handle(*nodeImpl);
+
+  // Second-phase init of the implementation
+  // This can only be done after the CustomActor connection has been made...
+  nodeImpl->Initialize();
+
+  return handle;
+}
+
+Light::Light()
+: CustomActorImpl(ActorFlags::DISABLE_SIZE_NEGOTIATION)
+{
+}
+
+Light::~Light()
+{
+}
+
+void Light::Initialize()
+{
+  Self().SetProperty(Dali::Actor::Property::COLOR, Color::WHITE);
+}
+
+void Light::Enable(bool enable)
+{
+  if(enable == mIsEnabled)
+  {
+    return;
+  }
+  mIsEnabled = enable;
+
+  Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
+  if(!sceneView)
+  {
+    return;
+  }
+
+  if(mIsEnabled)
+  {
+    GetImpl(sceneView).AddLight(Scene3D::Light::DownCast(Self()));
+  }
+  else
+  {
+    GetImpl(sceneView).RemoveLight(Scene3D::Light::DownCast(Self()));
+  }
+}
+
+bool Light::IsEnabled() const
+{
+  return mIsEnabled;
+}
+
+void Light::OnSceneConnection(int depth)
+{
+  Actor parent = Self().GetParent();
+  while(parent)
+  {
+    Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(parent);
+    if(sceneView)
+    {
+      mParentSceneView = sceneView;
+      if(mIsEnabled)
+      {
+        GetImpl(sceneView).AddLight(Scene3D::Light::DownCast(Self()));
+      }
+      break;
+    }
+    parent = parent.GetParent();
+  }
+}
+
+void Light::OnSceneDisconnection()
+{
+  Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
+  if(sceneView)
+  {
+    mParentSceneView.Reset();
+    GetImpl(sceneView).RemoveLight(Scene3D::Light::DownCast(Self()));
+  }
+}
+
+void Light::OnChildAdd(Actor& child)
+{
+}
+
+void Light::OnChildRemove(Actor& child)
+{
+}
+
+void Light::OnSizeSet(const Vector3& targetSize)
+{
+}
+
+void Light::OnSizeAnimation(Animation& animation, const Vector3& targetSize)
+{
+  // @todo size negotiate background to new size, animate as well?
+}
+
+void Light::OnRelayout(const Vector2& size, RelayoutContainer& container)
+{
+}
+
+void Light::OnSetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension)
+{
+}
+
+Vector3 Light::GetNaturalSize()
+{
+  return Vector3::ZERO;
+}
+
+float Light::CalculateChildSize(const Dali::Actor& child, Dimension::Type dimension)
+{
+  return 0.0f;
+}
+
+float Light::GetHeightForWidth(float width)
+{
+  return 0.0f;
+}
+
+float Light::GetWidthForHeight(float height)
+{
+  return 0.0f;
+}
+
+bool Light::RelayoutDependentOnChildren(Dimension::Type dimension)
+{
+  return false;
+}
+
+void Light::OnCalculateRelayoutSize(Dimension::Type dimension)
+{
+}
+
+void Light::OnLayoutNegotiated(float size, Dimension::Type dimension)
+{
+}
+
+Light& GetImplementation(Dali::Scene3D::Light& handle)
+{
+  CustomActorImpl& customInterface = handle.GetImplementation();
+  Light&           impl            = dynamic_cast<Internal::Light&>(customInterface);
+  return impl;
+}
+
+const Light& GetImplementation(const Dali::Scene3D::Light& handle)
+{
+  const CustomActorImpl& customInterface = handle.GetImplementation();
+  // downcast to control
+  const Light& impl = dynamic_cast<const Internal::Light&>(customInterface);
+  return impl;
+}
+
+// Public Method
+
+uint32_t Light::GetMaximumEnabledLightCount()
+{
+  return MAX_NUMBER_OF_LIGHT;
+}
+
+std::string_view Light::GetLightCountUniformName()
+{
+  return LIGHT_COUNT_STRING;
+}
+
+std::string_view Light::GetLightDirectionUniformName()
+{
+  return LIGHT_DIRECTION_STRING;
+}
+
+std::string_view Light::GetLightColorUniformName()
+{
+  return LIGHT_COLOR_STRING;
+}
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
diff --git a/dali-scene3d/internal/light/light-impl.h b/dali-scene3d/internal/light/light-impl.h
new file mode 100644 (file)
index 0000000..70b51f5
--- /dev/null
@@ -0,0 +1,239 @@
+#ifndef DALI_SCENE3D_LIGHT_IMPL_H
+#define DALI_SCENE3D_LIGHT_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 <dali/public-api/object/weak-handle.h>
+#include <string_view>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
+#include <dali-scene3d/public-api/light/light.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+/**
+ * @addtogroup dali_scene3d_light
+ * @{
+ */
+
+namespace Internal
+{
+/**
+ * @brief This is the internal base class for custom node of Light.
+ *
+ * @SINCE_2_2.32
+ */
+class DALI_SCENE3D_API Light : public CustomActorImpl
+{
+public:
+  // Creation & Destruction
+  /**
+   * @brief Creates a new LightImpl 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.32
+   * @return A handle to the Light instance
+   */
+  static Scene3D::Light New();
+
+  /**
+   * @copydoc Scene3D::Light::Enable()
+   */
+  void Enable(bool enable);
+
+  /**
+   * @copydoc Scene3D::Light::IsEnabled()
+   */
+  bool IsEnabled() const;
+
+protected:
+  /**
+   * @brief Virtual destructor.
+   * @SINCE_2_2.32
+   */
+  virtual ~Light();
+
+protected: // From CustomActorImpl
+  /**
+   * @copydoc CustomActorImpl::OnSceneConnection()
+   * @note If overridden, then an up-call to Light::OnSceneConnection MUST be made at the end.
+   */
+  void OnSceneConnection(int depth) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnSceneDisconnection()
+   * @note If overridden, then an up-call to Light::OnSceneDisconnection MUST be made at the end.
+   */
+  void OnSceneDisconnection() override;
+
+  /**
+   * @copydoc CustomActorImpl::OnChildAdd()
+   * @note If overridden, then an up-call to Light::OnChildAdd MUST be made at the end.
+   */
+  void OnChildAdd(Actor& child) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnChildRemove()
+   * @note If overridden, then an up-call to Light::OnChildRemove MUST be made at the end.
+   */
+  void OnChildRemove(Actor& child) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnSizeSet()
+   * @note If overridden, then an up-call to Light::OnSizeSet MUST be made at the end.
+   */
+  void OnSizeSet(const Vector3& targetSize) override;
+
+  /**
+   * @copydoc CustomActorImpl::OnSizeAnimation()
+   * @note If overridden, then an up-call to Light::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 Light constructor.
+   */
+  Light();
+
+  /**
+   * @brief Second phase initialization.
+   */
+  void Initialize();
+
+public: // Public Method
+
+  /**
+   * @copydoc Scene3D::Light::GetMaximumEnabledLightCount()
+   */
+  static uint32_t GetMaximumEnabledLightCount();
+
+  /**
+   * @brief Retrieves Light Enabled Uniform Name for shader()
+   * @return string_view for LightEnabledUniformName
+   */
+  static std::string_view GetLightCountUniformName();
+
+  /**
+   * @brief Retrieves Light Direction Uniform Name for shader()
+   * @return string_view for LightDirectionUniformName
+   */
+  static std::string_view GetLightDirectionUniformName();
+
+  /**
+   * @brief Retrieves Light Color Uniform Name for shader()
+   * @return string_view for LightColorUniformName
+   */
+  static std::string_view GetLightColorUniformName();
+
+private:
+  /// @cond internal
+
+  // Not copyable or movable
+  DALI_INTERNAL        Light(const Light&)     = delete; ///< Deleted copy constructor.
+  DALI_INTERNAL        Light(Light&&)          = delete; ///< Deleted move constructor.
+  DALI_INTERNAL Light& operator=(const Light&) = delete; ///< Deleted copy assignment operator.
+  DALI_INTERNAL Light& operator=(Light&&)      = delete; ///< Deleted move assignment operator.
+
+private:
+  WeakHandle<Scene3D::SceneView> mParentSceneView;
+  bool mIsEnabled{true};
+  /// @endcond
+};
+
+/**
+ * @brief Gets implementation from the handle.
+ *
+ * @param handle
+ * @return Implementation
+ * @pre handle is initialized and points to a node
+ */
+DALI_SCENE3D_API Internal::Light& GetImplementation(Dali::Scene3D::Light& handle);
+
+/**
+ * @brief Gets implementation from the handle.
+ *
+ * @param handle
+ * @return Implementation
+ * @pre Handle is initialized and points to a node.
+ */
+DALI_SCENE3D_API const Internal::Light& GetImplementation(const Dali::Scene3D::Light& handle);
+
+} // namespace Internal
+
+/**
+ * @}
+ */
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_LIGHT_IMPL_H
index 7cfb847..a117d48 100644 (file)
@@ -26,6 +26,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-scene3d/internal/light/light-impl.h>
 #include <dali-scene3d/internal/model-components/material-modify-observer.h>
 #include <dali-scene3d/public-api/loader/node-definition.h>
 #include <dali-scene3d/public-api/loader/renderer-state.h>
@@ -696,6 +697,21 @@ void Material::SetRendererUniform(Dali::Renderer renderer)
   renderer.RegisterProperty(Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), 1.0f);
   renderer.RegisterProperty(Scene3D::Loader::NodeDefinition::GetIblYDirectionUniformName().data(), Vector3(1.0f, -1.0, 1.0));
 
+  std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
+  renderer.RegisterProperty(lightCountPropertyName, 0);
+
+  uint32_t maxLightCount = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
+  for(uint32_t i = 0; i < maxLightCount; ++i)
+  {
+    std::string lightDirectionPropertyName(Scene3D::Internal::Light::GetLightDirectionUniformName());
+    lightDirectionPropertyName += "[" + std::to_string(i) + "]";
+    renderer.RegisterProperty(lightDirectionPropertyName, Vector3::ZAXIS);
+
+    std::string lightColorPropertyName(Scene3D::Internal::Light::GetLightColorUniformName());
+    lightColorPropertyName += "[" + std::to_string(i) + "]";
+    renderer.RegisterProperty(lightColorPropertyName, Vector3(Color::WHITE));
+  }
+
   Scene3D::Loader::RendererState::Apply(mRendererState, renderer);
 }
 
index 54db4bc..64b3ec2 100644 (file)
@@ -25,6 +25,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/model-components/model-primitive-impl.h>
+#include <dali-scene3d/internal/light/light-impl.h>
 
 namespace Dali
 {
@@ -74,6 +75,7 @@ ModelNode::~ModelNode()
 void ModelNode::Initialize()
 {
   OnInitialize();
+  mLights.resize(Scene3D::Internal::Light::GetMaximumEnabledLightCount());
 }
 
 void ModelNode::OnInitialize()
@@ -191,6 +193,15 @@ void ModelNode::AddModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive)
     GetImplementation(modelPrimitive).SetImageBasedLightTexture(mDiffuseTexture, mSpecularTexture, mIblScaleFactor, mSpecularMipmapLevels);
   }
 
+  uint32_t maxLightCount = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
+  for(uint32_t i = 0; i < maxLightCount; ++i)
+  {
+    if(mLights[i])
+    {
+      GetImplementation(modelPrimitive).AddLight(mLights[i], i);
+    }
+  }
+
   Dali::Renderer renderer = GetImplementation(modelPrimitive).GetRenderer();
   if(renderer)
   {
@@ -220,6 +231,7 @@ void ModelNode::RemoveModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitiv
     {
       continue;
     }
+
     RemoveModelPrimitive(i);
     break;
   }
@@ -232,6 +244,15 @@ void ModelNode::RemoveModelPrimitive(uint32_t index)
     return;
   }
 
+  uint32_t maxLightCount = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
+  for(uint32_t i = 0; i < maxLightCount; ++i)
+  {
+    if(mLights[i])
+    {
+      GetImplementation(mModelPrimitiveContainer[index]).RemoveLight(i);
+    }
+  }
+
   Actor self = Self();
   GetImplementation(mModelPrimitiveContainer[index]).RemovePrimitiveObserver(this);
 
@@ -280,6 +301,24 @@ void ModelNode::SetImageBasedLightScaleFactor(float iblScaleFactor)
   }
 }
 
+void ModelNode::AddLight(Scene3D::Light light, uint32_t lightIndex)
+{
+  mLights[lightIndex] = light;
+  for(auto&& primitive : mModelPrimitiveContainer)
+  {
+    GetImplementation(primitive).AddLight(light, lightIndex);
+  }
+}
+
+void ModelNode::RemoveLight(uint32_t lightIndex)
+{
+  for(auto&& primitive : mModelPrimitiveContainer)
+  {
+    GetImplementation(primitive).RemoveLight(lightIndex);
+  }
+  mLights[lightIndex].Reset();
+}
+
 void ModelNode::SetBlendShapeData(Scene3D::Loader::BlendShapes::BlendShapeData& data, Scene3D::ModelPrimitive primitive)
 {
   GetImplementation(primitive).SetBlendShapeData(data);
index e3c718c..4b9a66d 100644 (file)
@@ -25,6 +25,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/model-components/model-primitive-modify-observer.h>
+#include <dali-scene3d/public-api/light/light.h>
 #include <dali-scene3d/public-api/loader/mesh-definition.h>
 #include <dali-scene3d/public-api/loader/skinning-details.h>
 #include <dali-scene3d/public-api/model-components/model-node.h>
@@ -232,6 +233,9 @@ public: // Public Method
    */
   void SetImageBasedLightScaleFactor(float iblScaleFactor);
 
+  void AddLight(Scene3D::Light light, uint32_t lightIndex);
+  void RemoveLight(uint32_t lightIndex);
+
   /**
    * @brief Sets the blend shape data for a ModelPrimitive.
    *
@@ -280,6 +284,9 @@ private:
   Dali::Texture           mDiffuseTexture;
   float                   mIblScaleFactor{1.0f};
   uint32_t                mSpecularMipmapLevels{1u};
+
+  // Light
+  std::vector<Scene3D::Light> mLights;
   /// @endcond
 };
 
index adb78aa..569be53 100644 (file)
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/image-loading.h>
+#include <dali/public-api/animation/constraint.h>
 #include <dali/public-api/object/type-registry-helper.h>
 #include <dali/public-api/object/type-registry.h>
 
 // INTERNAL INCLUDES
+#include <dali-scene3d/internal/light/light-impl.h>
 #include <dali-scene3d/internal/model-components/material-impl.h>
 #include <dali-scene3d/public-api/loader/environment-definition.h>
 
@@ -52,6 +54,8 @@ 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";
+
+static constexpr uint32_t INDEX_FOR_LIGHT_CONSTRAINT_TAG = 10;
 } // unnamed namespace
 
 ModelPrimitivePtr ModelPrimitive::New()
@@ -78,6 +82,7 @@ ModelPrimitive::~ModelPrimitive()
 
 void ModelPrimitive::Initialize()
 {
+  mLights.resize(Scene3D::Internal::Light::GetMaximumEnabledLightCount());
 }
 
 void ModelPrimitive::SetRenderer(Dali::Renderer renderer)
@@ -175,6 +180,52 @@ void ModelPrimitive::SetImageBasedLightScaleFactor(float iblScaleFactor)
   }
 }
 
+void ModelPrimitive::AddLight(Scene3D::Light light, uint32_t lightIndex)
+{
+  if(mLights[lightIndex])
+  {
+    RemoveLight(lightIndex);
+  }
+
+  mLights[lightIndex] = light;
+  // TODO  Remove light at lightIndex if it is already set.
+  if(mRenderer && mMaterial)
+  {
+    mLightCount++;
+    std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
+    mRenderer.RegisterProperty(lightCountPropertyName, mLightCount);
+
+    std::string lightDirectionPropertyName(Scene3D::Internal::Light::GetLightDirectionUniformName());
+    lightDirectionPropertyName += "[" + std::to_string(lightIndex) + "]";
+    auto             lightDirectionPropertyIndex = mRenderer.RegisterProperty(lightDirectionPropertyName, Vector3::ZAXIS);
+    Dali::Constraint lightDirectionConstraint    = Dali::Constraint::New<Vector3>(mRenderer, lightDirectionPropertyIndex, [](Vector3& output, const PropertyInputContainer& inputs)
+                                                                               { output = inputs[0]->GetQuaternion().Rotate(Vector3::ZAXIS); });
+    lightDirectionConstraint.AddSource(Source{light, Dali::Actor::Property::WORLD_ORIENTATION});
+    lightDirectionConstraint.ApplyPost();
+    lightDirectionConstraint.SetTag(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
+
+    std::string lightColorPropertyName(Scene3D::Internal::Light::GetLightColorUniformName());
+    lightColorPropertyName += "[" + std::to_string(lightIndex) + "]";
+    auto             lightColorPropertyIndex = mRenderer.RegisterProperty(lightColorPropertyName, Vector3(Color::WHITE));
+    Dali::Constraint lightColorConstraint    = Dali::Constraint::New<Vector3>(mRenderer, lightColorPropertyIndex, [](Vector3& output, const PropertyInputContainer& inputs)
+                                                                           { output = Vector3(inputs[0]->GetVector4()); });
+    lightColorConstraint.AddSource(Source{light, Dali::Actor::Property::COLOR});
+    lightColorConstraint.ApplyPost();
+    lightColorConstraint.SetTag(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
+  }
+}
+
+void ModelPrimitive::RemoveLight(uint32_t lightIndex)
+{
+  mLightCount--;
+  std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
+  mRenderer.RegisterProperty(lightCountPropertyName, mLightCount);
+
+  mRenderer.RemoveConstraints(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
+
+  mLights[lightIndex].Reset();
+}
+
 void ModelPrimitive::SetBlendShapeData(Scene3D::Loader::BlendShapes::BlendShapeData& data)
 {
   mBlendShapeData = std::move(data);
@@ -338,6 +389,16 @@ void ModelPrimitive::CreateRenderer()
   mRenderer = Renderer::New(mGeometry, mShader);
   mRenderer.SetTextures(mTextureSet);
   UpdateRendererUniform();
+
+  uint32_t maxLightCount = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
+  for(uint32_t i = 0; i < maxLightCount; ++i)
+  {
+    if(mLights[i])
+    {
+      AddLight(mLights[i], i);
+    }
+  }
+
   for(auto* observer : mObservers)
   {
     observer->OnRendererCreated(mRenderer);
index b7d26b4..027ecac 100644 (file)
@@ -29,6 +29,7 @@
 // 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/light/light.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>
@@ -139,6 +140,21 @@ public:
   void SetImageBasedLightScaleFactor(float iblScaleFactor);
 
   /**
+   * @brief Adds a Light on this medel primitive
+   *
+   * @param[in] light Added light.
+   * @param[in] lightIndex Index of added light.
+   */
+  void AddLight(Scene3D::Light light, uint32_t lightIndex);
+
+  /**
+   * @brief Removes a light at the lightIndex of this model primitive
+   *
+   * @param[in] lightIndex Index of added light.
+   */
+  void RemoveLight(uint32_t lightIndex);
+
+  /**
    * @brief Sets the blend shape data for this model primitive.
    *
    * @param[in] data The blend shape data to set.
@@ -197,9 +213,9 @@ private:
 
 private:
   // Delete copy & move operator
-  ModelPrimitive(const ModelPrimitive&)                = delete;
-  ModelPrimitive(ModelPrimitive&&)                     = delete;
-  ModelPrimitive& operator=(const ModelPrimitive& rhs) = delete;
+  ModelPrimitive(const ModelPrimitive&)                    = delete;
+  ModelPrimitive(ModelPrimitive&&)                         = delete;
+  ModelPrimitive& operator=(const ModelPrimitive& rhs)     = delete;
   ModelPrimitive& operator=(ModelPrimitive&& rhs) noexcept = delete;
 
 private:
@@ -212,6 +228,10 @@ private:
   Dali::TextureSet        mTextureSet;
   Dali::Scene3D::Material mMaterial;
 
+  // Light
+  std::vector<Scene3D::Light> mLights;
+  int32_t                     mLightCount{0};
+
   // For IBL
   Dali::Texture mSpecularTexture;
   Dali::Texture mDiffuseTexture;
index b6cc0ac..1bfc81e 100644 (file)
@@ -117,6 +117,11 @@ float SceneView::GetImageBasedLightScaleFactor() const
   return GetImpl(*this).GetImageBasedLightScaleFactor();
 }
 
+uint32_t SceneView::GetActivatedLightCount() const
+{
+  return GetImpl(*this).GetActivatedLightCount();
+}
+
 void SceneView::UseFramebuffer(bool useFramebuffer)
 {
   GetImpl(*this).UseFramebuffer(useFramebuffer);
index d4e0008..1f4ec52 100644 (file)
@@ -295,6 +295,14 @@ public:
   float GetImageBasedLightScaleFactor() const;
 
   /**
+   * @brief Gets Enabled Light Count of this SceneView
+   *
+   * @SINCE_2_2.32
+   * @return The number of enabled light count;
+   */
+  uint32_t GetActivatedLightCount() const;
+
+  /**
    * @brief Sets whether to use FBO or not for the SceneView.
    * If useFramebuffer is true, rendering result of SceneView is drawn on FBO and it is mapping on this SceneView plane.
    * If useFramebuffer is false, each item in SceneView is rendered on window directly.
index 0e2d3b9..f039518 100644 (file)
@@ -6,6 +6,7 @@ set(scene3d_src_files ${scene3d_src_files}
        ${scene3d_public_api_dir}/algorithm/path-finder-waypoint.cpp
        ${scene3d_public_api_dir}/controls/model/model.cpp
        ${scene3d_public_api_dir}/controls/scene-view/scene-view.cpp
+       ${scene3d_public_api_dir}/light/light.cpp
        ${scene3d_public_api_dir}/loader/alpha-function-helper.cpp
        ${scene3d_public_api_dir}/loader/animated-property.cpp
        ${scene3d_public_api_dir}/loader/animation-definition.cpp
diff --git a/dali-scene3d/public-api/light/light.cpp b/dali-scene3d/public-api/light/light.cpp
new file mode 100644 (file)
index 0000000..0b7f493
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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/light/light.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/light/light-impl.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+Light::Light()
+{
+}
+
+Light::Light(const Light& light) = default;
+
+Light::Light(Light&& rhs) noexcept = default;
+
+Light& Light::operator=(const Light& light) = default;
+
+Light& Light::operator=(Light&& rhs) noexcept = default;
+
+Light::~Light()
+{
+}
+
+Light Light::New()
+{
+  return Internal::Light::New();
+}
+
+Light Light::DownCast(BaseHandle handle)
+{
+  Light result;
+
+  CustomActor custom = Dali::CustomActor::DownCast(handle);
+  if(custom)
+  {
+    CustomActorImpl& customImpl = custom.GetImplementation();
+
+    Internal::Light* impl = dynamic_cast<Internal::Light*>(&customImpl);
+
+    if(impl)
+    {
+      result = Light(customImpl.GetOwner());
+    }
+  }
+
+  return result;
+}
+
+void Light::Enable(bool enable)
+{
+  Internal::GetImplementation(*this).Enable(enable);
+}
+
+bool Light::IsEnabled() const
+{
+  return Internal::GetImplementation(*this).IsEnabled();
+}
+
+uint32_t Light::GetMaximumEnabledLightCount()
+{
+  return Internal::Light::GetMaximumEnabledLightCount();
+}
+
+Light::Light(Internal::Light& implementation)
+: CustomActor(implementation)
+{
+}
+
+Light::Light(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::Light*>(&CustomActor(internal).GetImplementation()));
+  }
+}
+
+} // namespace Scene3D
+
+} // namespace Dali
diff --git a/dali-scene3d/public-api/light/light.h b/dali-scene3d/public-api/light/light.h
new file mode 100644 (file)
index 0000000..eabb278
--- /dev/null
@@ -0,0 +1,187 @@
+#ifndef DALI_SCENE3D_LIGHT_H
+#define DALI_SCENE3D_LIGHT_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/controls/control.h>
+#include <dali/public-api/common/dali-common.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/api.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal DALI_INTERNAL
+{
+class Light;
+}
+
+/**
+ * @addtogroup dali_toolkit_controls_light
+ * @{
+ */
+
+/**
+ * @brief This class is to define 3D Light source.
+ * Currently this Light class supports Directional Light that lits every position from the same direction. (e.g, Sun light)
+ * If a Light object is added on SceneView, the 3D objects in the SceneView are shined the Light.
+ * DALi Scene3D limits the maximum enabled light count per each SceneView.
+ * Currently the maximum number is set to 5, and it can be retrieved by using GetMaximumEnabledLightCount().
+ * If more than 5 enabled Light objects are added on SceneView, SceneView turns on only 5 lights in the order the lights were added.
+ * This Light can be added to SceneView directly but also it can be added on other Actor.
+ * When a parent actor is added to a SceneView, its Light behaves in the SceneView the same as if it were added directly to the SceneView.
+ * @note Light inherits Actor, so Light color and direction can be controlled by setting Actor's COLOR and ORIENTATION Property.
+ * Dali::DevelActor::LookAt() method can be used to set light direction easily.
+ * @note Default light direction is to Z-axis
+ * @code
+ * Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+ * Scene3D::Lightlight = Scene3D::Light::New();
+ * light.SetProperty(Dali::Actor::Property::COLOR, Color::BROWN);
+ * Dali::DevelActor::LookAt(light, Vector3(1.0f, 1.0f, 1.0f));
+ * sceneView.Add(light);
+ * @endcode
+ * @SINCE_2_2.32
+ */
+class DALI_SCENE3D_API Light : public Dali::CustomActor
+{
+public:
+  /**
+   * @brief Create an initialized Light.
+   *
+   * @SINCE_2_2.32
+   * @return A handle to a newly allocated Dali resource
+   */
+  static Light New();
+
+  /**
+   * @brief Creates an uninitialized Light.
+   *
+   * Only derived versions can be instantiated. Calling member
+   * functions with an uninitialized Dali::Object is not allowed.
+   *
+   * @SINCE_2_2.32
+   */
+  Light();
+
+  /**
+   * @brief Destructor.
+   *
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   *
+   * @SINCE_2_2.32
+   */
+  ~Light();
+
+  /**
+   * @brief Copy constructor.
+   *
+   * @SINCE_2_2.32
+   * @param[in] light Handle to an object
+   */
+  Light(const Light& light);
+
+  /**
+   * @brief Move constructor
+   *
+   * @SINCE_2_2.32
+   * @param[in] rhs A reference to the moved handle
+   */
+  Light(Light&& rhs) noexcept;
+
+  /**
+   * @brief Assignment operator.
+   *
+   * @SINCE_2_2.32
+   * @param[in] light Handle to an object
+   * @return reference to this
+   */
+  Light& operator=(const Light& light);
+
+  /**
+   * @brief Move assignment
+   *
+   * @SINCE_2_2.32
+   * @param[in] rhs A reference to the moved handle
+   * @return A reference to this
+   */
+  Light& operator=(Light&& rhs) noexcept;
+
+  /**
+   * @brief Downcasts an Object handle to Light.
+   *
+   * If handle points to a Light, the downcast produces valid handle.
+   * If not, the returned handle is left uninitialized.
+   *
+   * @SINCE_2_2.32
+   * @param[in] handle Handle to an object
+   * @return Handle to a Light or an uninitialized handle
+   */
+  static Light DownCast(BaseHandle handle);
+
+  /**
+   * @brief Enables this light.
+   * The Light is turned on when the Light object is added on SceneView and enabled.
+   * @SINCE_2_2.32
+   * @note SceneView can turn on only up to maximum enabled light count that can be retrieved by GetMaximumEnabledLightCount().
+   * @param[in] enable True to make this Light enable.
+   */
+  void Enable(bool enable);
+
+  /**
+   * @brief Checks whether this light is enabled or not.
+   * @SINCE_2_2.32
+   * @return True if this light is enabled.
+   */
+  bool IsEnabled() const;
+
+  /**
+   * @brief Retrieves maximum enabled light count.
+   * @SINCE_2_2.32
+   * @return The number of maximum enabled light count.
+   */
+  static uint32_t GetMaximumEnabledLightCount();
+
+public: // Not intended for application developers
+  /// @cond internal
+  /**
+   * @brief Creates a handle using the Toolkit::Internal implementation.
+   *
+   * @param[in] implementation The Control implementation
+   */
+  DALI_INTERNAL Light(Internal::Light& implementation);
+
+  /**
+   * @brief Allows the creation of this Control from an Internal::CustomActor pointer.
+   *
+   * @param[in] internal A pointer to the internal CustomActor
+   */
+  DALI_INTERNAL Light(Dali::Internal::CustomActor* internal);
+  /// @endcond
+};
+
+/**
+ * @}
+ */
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_LIGHT_H
index c00a87b..16d0189 100644 (file)
 // CLASS HEADER
 #include <dali-scene3d/public-api/loader/node-definition.h>
 
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.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>
+#include <dali-scene3d/internal/light/light-impl.h>
 
 namespace Dali
 {
@@ -341,6 +343,21 @@ void ModelRenderable::OnCreate(const NodeDefinition& nodeDefinition, NodeDefinit
   renderer.RegisterProperty(IBL_INTENSITY_STRING.data(), resources.mEnvironmentMaps[envIndex].first.mIblIntensity);
   renderer.RegisterProperty(IBL_Y_DIRECTION.data(), resources.mEnvironmentMaps[envIndex].first.mYDirection);
 
+  std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
+  renderer.RegisterProperty(lightCountPropertyName, 0);
+
+  uint32_t maxLightCount = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
+  for(uint32_t i = 0; i < maxLightCount; ++i)
+  {
+    std::string lightDirectionPropertyName(Scene3D::Internal::Light::GetLightDirectionUniformName());
+    lightDirectionPropertyName += "[" + std::to_string(i) + "]";
+    renderer.RegisterProperty(lightDirectionPropertyName, Vector3::ZAXIS);
+
+    std::string lightColorPropertyName(Scene3D::Internal::Light::GetLightColorUniformName());
+    lightColorPropertyName += "[" + std::to_string(i) + "]";
+    renderer.RegisterProperty(lightColorPropertyName, Vector3(Color::WHITE));
+  }
+
   node.SetProperty(Actor::Property::COLOR, mColor);
 
   {
index 0d24481..5104f6b 100644 (file)
@@ -8,6 +8,9 @@ INPUT mediump vec4 vCornerRadius;
 #endif
 #endif
 
+// scale factor to fit start and end position of gradient.
+uniform mediump float uTextureCoordinateScaleFactor;
+
 uniform sampler2D sTexture; // sampler1D?
 uniform lowp vec4 uColor;
 uniform lowp vec3 mixColor;
@@ -188,7 +191,8 @@ mediump float calculateCornerOpacity()
 void main()
 {
 #ifdef RADIAL
-  lowp vec4 textureColor = TEXTURE(sTexture, vec2(length(vTexCoord), 0.5)) * vec4(mixColor, 1.0) * uColor;
+  mediump float radialTexCoord = ((length(vTexCoord) - 0.5) * uTextureCoordinateScaleFactor) + 0.5;
+  lowp vec4 textureColor = TEXTURE(sTexture, vec2(radialTexCoord, 0.5)) * vec4(mixColor, 1.0) * uColor;
 #else
   lowp vec4 textureColor = TEXTURE(sTexture, vec2(vTexCoord.y, 0.5)) * vec4(mixColor, 1.0) * uColor;
 #endif
index 2f6745b..d595d00 100644 (file)
@@ -36,6 +36,7 @@ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT
 #endif
 
 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
+DALI_INIT_TRACE_FILTER(gTraceFilter2, DALI_TRACE_PERFORMANCE_MARKER, false);
 
 constexpr float MAX_FLOAT = std::numeric_limits<float>::max();
 
@@ -557,7 +558,7 @@ Controller::UpdateTextType Controller::Relayouter::Relayout(Controller& controll
 bool Controller::Relayouter::DoRelayout(Controller::Impl& impl, const Size& size, OperationsMask operationsRequired, Size& layoutSize)
 {
   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::Relayouter::DoRelayout %p size %f,%f\n", &impl, size.width, size.height);
-  DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_DORELAYOUT");
+  DALI_TRACE_SCOPE(gTraceFilter2, "DALI_TEXT_DORELAYOUT");
   bool viewUpdated(false);
 
   // Calculate the operations to be done.
index 5d987b9..8a4ada9 100644 (file)
@@ -377,7 +377,7 @@ void AnimatedVectorImageVisual::DoSetProperty(Property::Index index, const Prope
 void AnimatedVectorImageVisual::OnInitialize(void)
 {
   mVectorAnimationTask->ResourceReadySignal().Connect(this, &AnimatedVectorImageVisual::OnResourceReady);
-  mVectorAnimationTask->SetAnimationFinishedCallback(new EventThreadCallback(MakeCallback(this, &AnimatedVectorImageVisual::OnAnimationFinished)));
+  mVectorAnimationTask->SetAnimationFinishedCallback(MakeCallback(this, &AnimatedVectorImageVisual::OnAnimationFinished));
 
   mVectorAnimationTask->RequestLoad(mUrl.GetUrl(), IsSynchronousLoadingRequired());
 
index 40f1a64..6d4b2ef 100644 (file)
@@ -53,8 +53,7 @@ VectorAnimationTask::VectorAnimationTask(VisualFactoryCache& factoryCache)
   mVectorAnimationThread(factoryCache.GetVectorAnimationManager().GetVectorAnimationThread()),
   mConditionalWait(),
   mResourceReadySignal(),
-  mAnimationFinishedTrigger(),
-  mLoadCompletedTrigger(new EventThreadCallback(MakeCallback(this, &VectorAnimationTask::OnLoadCompleted))),
+  mLoadCompletedCallback(MakeCallback(this, &VectorAnimationTask::OnLoadCompleted)),
   mPlayState(PlayState::STOPPED),
   mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME),
   mLoopingMode(DevelImageVisual::LoopingMode::RESTART),
@@ -104,13 +103,13 @@ void VectorAnimationTask::Finalize()
   ConditionalWait::ScopedLock lock(mConditionalWait);
 
   // Release some objects in the main thread
-  if(mAnimationFinishedTrigger)
+  if(mAnimationFinishedCallback)
   {
-    mAnimationFinishedTrigger.reset();
+    mVectorAnimationThread.RemoveEventTriggerCallback(mAnimationFinishedCallback.get());
   }
-  if(mLoadCompletedTrigger)
+  if(mLoadCompletedCallback)
   {
-    mLoadCompletedTrigger.reset();
+    mVectorAnimationThread.RemoveEventTriggerCallback(mLoadCompletedCallback.get());
   }
 
   mVectorRenderer.Finalize();
@@ -142,7 +141,7 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
     mLoadFailed  = true;
     if(!synchronousLoading)
     {
-      mLoadCompletedTrigger->Trigger();
+      mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
     }
     return false;
   }
@@ -157,7 +156,7 @@ bool VectorAnimationTask::Load(bool synchronousLoading)
   mLoadRequest = false;
   if(!synchronousLoading)
   {
-    mLoadCompletedTrigger->Trigger();
+    mVectorAnimationThread.AddEventTriggerCallback(mLoadCompletedCallback.get());
   }
 
   DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationTask::Load: file = %s [%d frames, %f fps] [%p]\n", mUrl.c_str(), mTotalFrame, mFrameRate, this);
@@ -263,13 +262,10 @@ void VectorAnimationTask::PauseAnimation()
   }
 }
 
-void VectorAnimationTask::SetAnimationFinishedCallback(EventThreadCallback* callback)
+void VectorAnimationTask::SetAnimationFinishedCallback(CallbackBase* callback)
 {
   ConditionalWait::ScopedLock lock(mConditionalWait);
-  if(callback)
-  {
-    mAnimationFinishedTrigger = std::unique_ptr<EventThreadCallback>(callback);
-  }
+  mAnimationFinishedCallback = std::unique_ptr<CallbackBase>(callback);
 }
 
 void VectorAnimationTask::SetLoopCount(int32_t count)
@@ -547,9 +543,9 @@ bool VectorAnimationTask::Rasterize()
     // Animation is finished
     {
       ConditionalWait::ScopedLock lock(mConditionalWait);
-      if(mNeedAnimationFinishedTrigger && mAnimationFinishedTrigger)
+      if(mNeedAnimationFinishedTrigger && mAnimationFinishedCallback)
       {
-        mAnimationFinishedTrigger->Trigger();
+        mVectorAnimationThread.AddEventTriggerCallback(mAnimationFinishedCallback.get());
       }
     }
 
index 0b2c43e..e1b80c3 100644 (file)
@@ -181,7 +181,7 @@ public:
    * @brief This callback is called after the animation is finished.
    * @param[in] callback The animation finished callback
    */
-  void SetAnimationFinishedCallback(EventThreadCallback* callback);
+  void SetAnimationFinishedCallback(CallbackBase* callback);
 
   /**
    * @brief Gets the playing range in frame number.
@@ -361,8 +361,8 @@ private:
   VectorAnimationThread&               mVectorAnimationThread;
   ConditionalWait                      mConditionalWait;
   ResourceReadySignalType              mResourceReadySignal;
-  std::unique_ptr<EventThreadCallback> mAnimationFinishedTrigger;
-  std::unique_ptr<EventThreadCallback> mLoadCompletedTrigger;
+  std::unique_ptr<CallbackBase>        mAnimationFinishedCallback{};
+  std::unique_ptr<CallbackBase>        mLoadCompletedCallback{};
   PlayState                            mPlayState;
   DevelImageVisual::StopBehavior::Type mStopBehavior;
   DevelImageVisual::LoopingMode::Type  mLoopingMode;
index 02198f0..08b52d6 100644 (file)
@@ -51,6 +51,8 @@ VectorAnimationThread::VectorAnimationThread()
 {
   mAsyncTaskManager = Dali::AsyncTaskManager::Get();
   mSleepThread.Start();
+
+  mEventTrigger = std::unique_ptr<EventThreadCallback>(new EventThreadCallback(MakeCallback(this, &VectorAnimationThread::OnEventCallbackTriggered)));
 }
 
 VectorAnimationThread::~VectorAnimationThread()
@@ -148,6 +150,28 @@ void VectorAnimationThread::OnAwakeFromSleep()
   }
 }
 
+void VectorAnimationThread::AddEventTriggerCallback(CallbackBase* callback)
+{
+  ConditionalWait::ScopedLock lock(mConditionalWait);
+  mTriggerEventCallbacks.push_back(callback);
+
+  if(!mEventTriggered)
+  {
+    mEventTrigger->Trigger();
+    mEventTriggered = true;
+  }
+}
+
+void VectorAnimationThread::RemoveEventTriggerCallback(CallbackBase* callback)
+{
+  ConditionalWait::ScopedLock lock(mConditionalWait);
+  auto                        iter = std::find(mTriggerEventCallbacks.begin(), mTriggerEventCallbacks.end(), callback);
+  if(iter != mTriggerEventCallbacks.end())
+  {
+    mTriggerEventCallbacks.erase(iter);
+  }
+}
+
 void VectorAnimationThread::Run()
 {
   SetThreadName("VectorAnimationThread");
@@ -237,6 +261,19 @@ void VectorAnimationThread::Rasterize()
   }
 }
 
+void VectorAnimationThread::OnEventCallbackTriggered()
+{
+  ConditionalWait::ScopedLock lock(mConditionalWait);
+
+  for(auto&& iter : mTriggerEventCallbacks)
+  {
+    CallbackBase::Execute(*iter);
+  }
+
+  mTriggerEventCallbacks.clear();
+  mEventTriggered = false;
+}
+
 VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback)
 : mConditionalWait(),
   mAwakeCallback(std::unique_ptr<CallbackBase>(callback)),
index 0fd37e4..79d1a48 100644 (file)
@@ -51,7 +51,7 @@ public:
   ~VectorAnimationThread() override;
 
   /**
-   * Add a animation task into the vector animation thread, called by main thread.
+   * @brief Add a animation task into the vector animation thread, called by main thread.
    *
    * @param[in] task The task added to the thread.
    */
@@ -59,6 +59,7 @@ public:
 
   /**
    * @brief Called when the rasterization is completed from the rasterize thread.
+   *
    * @param[in] task The completed task
    * @param[in] success true if the task succeeded, false otherwise.
    * @param[in] keepAnimation true if the animation is running, false otherwise.
@@ -70,6 +71,22 @@ public:
    */
   void OnAwakeFromSleep();
 
+  /**
+   * @brief Add an event trigger callback.
+   *
+   * @param callback The callback to add
+   * @note Ownership of the callback is NOT passed onto this class.
+   * @note The callback will be excuted in the main thread.
+   */
+  void AddEventTriggerCallback(CallbackBase* callback);
+
+  /**
+   * @brief Remove an event trigger callback.
+   *
+   * @param callback The callback to remove
+   */
+  void RemoveEventTriggerCallback(CallbackBase* callback);
+
 protected:
   /**
    * @brief The entry function of the animation thread.
@@ -83,6 +100,11 @@ private:
   void Rasterize();
 
   /**
+   * @brief Called when the event callback is triggered.
+   */
+  void OnEventCallbackTriggered();
+
+  /**
    * @brief The thread to sleep until the next frame time.
    */
   class SleepThread : public Thread
@@ -130,15 +152,18 @@ private:
   VectorAnimationThread& operator=(const VectorAnimationThread& thread) = delete;
 
 private:
-  std::vector<VectorAnimationTaskPtr> mAnimationTasks;
-  std::vector<VectorAnimationTaskPtr> mCompletedTasks;
-  std::vector<VectorAnimationTaskPtr> mWorkingTasks;
-  SleepThread                         mSleepThread;
-  ConditionalWait                     mConditionalWait;
-  bool                                mNeedToSleep;
-  bool                                mDestroyThread;
-  const Dali::LogFactoryInterface&    mLogFactory;
-  Dali::AsyncTaskManager              mAsyncTaskManager;
+  std::vector<VectorAnimationTaskPtr>  mAnimationTasks;
+  std::vector<VectorAnimationTaskPtr>  mCompletedTasks;
+  std::vector<VectorAnimationTaskPtr>  mWorkingTasks;
+  std::vector<CallbackBase*>           mTriggerEventCallbacks{}; // Callbacks are not owned
+  SleepThread                          mSleepThread;
+  ConditionalWait                      mConditionalWait;
+  std::unique_ptr<EventThreadCallback> mEventTrigger{};
+  bool                                 mNeedToSleep;
+  bool                                 mDestroyThread;
+  bool                                 mEventTriggered{false};
+  const Dali::LogFactoryInterface&     mLogFactory;
+  Dali::AsyncTaskManager               mAsyncTaskManager;
 };
 
 } // namespace Internal
index ea84e5e..8267973 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@ namespace Internal
 {
 namespace
 {
-const int CUSTOM_PROPERTY_COUNT(1); // alignment
+static constexpr int32_t CUSTOM_PROPERTY_COUNT(1); // alignment
 
 DALI_ENUM_TO_STRING_TABLE_BEGIN(UNITS)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::GradientVisual::Units, OBJECT_BOUNDING_BOX)
@@ -61,11 +61,12 @@ DALI_ENUM_TO_STRING_TABLE_BEGIN(SPREAD_METHOD)
 DALI_ENUM_TO_STRING_TABLE_END(SPREAD_METHOD)
 
 // uniform names
-const char* const UNIFORM_ALIGNMENT_MATRIX_NAME("uAlignmentMatrix");
+static constexpr std::string_view UNIFORM_ALIGNMENT_MATRIX_NAME("uAlignmentMatrix");
+static constexpr std::string_view UNIFORM_TEXTURE_COORDINATE_SCALE_FACTOR_NAME("uTextureCoordinateScaleFactor");
 
 // default offset value
-const unsigned int DEFAULT_OFFSET_MINIMUM = 0.0f;
-const unsigned int DEFAULT_OFFSET_MAXIMUM = 1.0f;
+static constexpr float DEFAULT_OFFSET_MINIMUM = 0.0f;
+static constexpr float DEFAULT_OFFSET_MAXIMUM = 1.0f;
 
 VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[16] =
   {
@@ -253,7 +254,7 @@ void GradientVisual::OnInitialize()
   Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
   Shader   shader   = GenerateShader();
 
-  //Set up the texture set
+  // Set up the texture set
   TextureSet    textureSet    = TextureSet::New();
   Dali::Texture lookupTexture = mGradient->GenerateLookupTexture();
   textureSet.SetTexture(0u, lookupTexture);
@@ -274,7 +275,10 @@ void GradientVisual::OnInitialize()
 
   mImpl->mRenderer.RegisterUniqueProperty(UNIFORM_ALIGNMENT_MATRIX_NAME, mGradientTransform);
 
-  //Register transform properties
+  float textureSize = static_cast<float>(lookupTexture.GetWidth());
+  mImpl->mRenderer.RegisterUniqueProperty(UNIFORM_TEXTURE_COORDINATE_SCALE_FACTOR_NAME, (textureSize - 1.0f) / textureSize);
+
+  // Register transform properties
   mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
 }
 
index 85b0118..b82d80e 100644 (file)
@@ -29,7 +29,7 @@ namespace Toolkit
 {
 const unsigned int TOOLKIT_MAJOR_VERSION = 2;
 const unsigned int TOOLKIT_MINOR_VERSION = 2;
-const unsigned int TOOLKIT_MICRO_VERSION = 30;
+const unsigned int TOOLKIT_MICRO_VERSION = 31;
 const char* const  TOOLKIT_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index aefb64a..0fb2797 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2-toolkit
 Summary:    Dali 3D engine Toolkit
-Version:    2.2.30
+Version:    2.2.31
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT