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)
--- /dev/null
+/*
+ * 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;
+}
-#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.
* @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
} // namespace Dali
-#endif // DALI_SCENE3D_INTERNAL_IMAGE_BASED_LIGHT_OBSERVER_H
+#endif // DALI_SCENE3D_INTERNAL_LIGHT_OBSERVER_H
// 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>
}
}
+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)
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();
{
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);
}
}
{
// Make ParentOrigin as Center.
Self().SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+ mLights.resize(Scene3D::Internal::Light::GetMaximumEnabledLightCount());
}
void Model::OnSceneConnection(int depth)
return;
}
- node.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), iblScaleFactor);
GetImplementation(node).SetImageBasedLightScaleFactor(iblScaleFactor);
uint32_t childrenCount = node.GetChildCount();
}
}
+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())
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));
// 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>
/**
* @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>;
*/
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.
WeakHandle<Scene3D::SceneView> mParentSceneView;
Dali::PropertyNotification mSizeNotification;
+ // Light
+ std::vector<Scene3D::Light> mLights;
+
// Asynchronous loading variable
ModelLoadTaskPtr mModelLoadTask;
EnvironmentMapLoadTaskPtr mIblDiffuseLoadTask;
// 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;
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";
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)
{
{
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;
}
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)
}
}
+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
// 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
*
* @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()
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);
*/
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;
CameraActor mDefaultCamera;
CameraActor mSelectedCamera;
std::vector<CameraActor> mCameras;
- std::vector<Scene3D::Internal::ImageBasedLightObserver*> mItems;
+ std::vector<Scene3D::Internal::LightObserver*> mItems;
Dali::FrameBuffer mFrameBuffer;
Dali::Texture mTexture;
Dali::RenderTask mRenderTask;
float mSkyboxIntensity{1.0f};
uint8_t mFrameBufferMultiSamplingLevel{4u};
+ // 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;
EnvironmentMapLoadTaskPtr mIblDiffuseLoadTask;
${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
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;
out vec4 FragColor;
const float c_MinRoughness = 0.04;
+const float M_PI = 3.141592653589793;
vec3 linear(vec3 color)
{
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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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
// 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>
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);
}
// INTERNAL INCLUDES
#include <dali-scene3d/internal/model-components/model-primitive-impl.h>
+#include <dali-scene3d/internal/light/light-impl.h>
namespace Dali
{
void ModelNode::Initialize()
{
OnInitialize();
+ mLights.resize(Scene3D::Internal::Light::GetMaximumEnabledLightCount());
}
void ModelNode::OnInitialize()
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)
{
{
continue;
}
+
RemoveModelPrimitive(i);
break;
}
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);
}
}
+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);
// 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>
*/
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.
*
Dali::Texture mDiffuseTexture;
float mIblScaleFactor{1.0f};
uint32_t mSpecularMipmapLevels{1u};
+
+ // Light
+ std::vector<Scene3D::Light> mLights;
/// @endcond
};
// 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>
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()
void ModelPrimitive::Initialize()
{
+ mLights.resize(Scene3D::Internal::Light::GetMaximumEnabledLightCount());
}
void ModelPrimitive::SetRenderer(Dali::Renderer renderer)
}
}
+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);
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);
// 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>
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.
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:
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;
return GetImpl(*this).GetImageBasedLightScaleFactor();
}
+uint32_t SceneView::GetActivatedLightCount() const
+{
+ return GetImpl(*this).GetActivatedLightCount();
+}
+
void SceneView::UseFramebuffer(bool useFramebuffer)
{
GetImpl(*this).UseFramebuffer(useFramebuffer);
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.
set(scene3d_src_files ${scene3d_src_files}
${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
--- /dev/null
+/*
+ * 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) = default;
+
+Light& Light::operator=(const Light& light) = default;
+
+Light& Light::operator=(Light&& rhs) = 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
--- /dev/null
+#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);
+
+ /**
+ * @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);
+
+ /**
+ * @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
// 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
{
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);
{