"camera" : 1,
"translation" : [ 0.5, 0.5, 3.0 ],
"children": [
- 4
+ 4, 5, 6, 7
]
},
{
0.0,
1.0
]
+ },
+ {
+ "camera" : 3,
+ "translation" : [ 0.0, 0.0, 0.0 ]
+ },
+ {
+ "camera" : 4,
+ "translation" : [ 0.0, 0.0, 0.0 ]
+ },
+ {
+ "camera" : 5,
+ "translation" : [ 0.0, 0.0, 0.0 ]
}
],
"scene" : 0,
"zfar": 100.0,
"znear": 0.01
}
+ },
+ {
+ "type": "perspective",
+ "perspective": {
+ "aspectRatio": 1.0,
+ "yfov": 0.7,
+ "znear": 0.01
+ }
+ },
+ {
+ "type": "perspective",
+ "perspective": {
+ "aspectRatio": 1.0,
+ "zfar": 100.0,
+ "znear": 0.01
+ }
+ },
+ {
+ "type": "orthographic",
+ "orthographic": {
+ "xmag": 1.0,
+ "ymag": 1.0,
+ "znear": 0.01
+ }
}
],
"samplers": [
{\r
"attributes": {\r
"POSITION": 0,\r
- "NORMAL": 1,\r
"TEXCOORD_0": 2\r
},\r
"indices": 3,\r
utc-Dali-Hash.cpp
utc-Dali-JsonReader.cpp
utc-Dali-JsonUtil.cpp
+ utc-Dali-ModelCacheManager.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.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali-scene3d/internal/common/model-cache-manager.h>
+#include <dali-scene3d/public-api/controls/model/model.h>
+#include "dali-scene3d/public-api/loader/resource-bundle.h"
+#include "dali-scene3d/public-api/loader/scene-definition.h"
+#include <toolkit-event-thread-callback.h>
+#include <string>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+using namespace Dali::Scene3D::Internal;
+
+namespace
+{
+/**
+ * For the AnimatedCube.gltf and its Assets
+ * Donated by Norbert Nopper for glTF testing.
+ * Take from https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/AnimatedCube
+ */
+const char* TEST_GLTF_FILE_NAME = TEST_RESOURCE_DIR "/AnimatedCube.gltf";
+
+static bool gResourceReadyCalled = false;
+void OnResourceReady(Control control)
+{
+ gResourceReadyCalled = true;
+}
+}
+
+int UtcDaliModelCacheManagerLoadModel(void)
+{
+ ToolkitTestApplication application;
+
+ ModelCacheManager cacheManager = ModelCacheManager::Get();
+ DALI_TEST_EQUALS(cacheManager.GetModelCacheRefCount(TEST_GLTF_FILE_NAME), 0u, TEST_LOCATION);
+
+ // Load the first instance of the same model and add it to the scene
+ Scene3D::Model model1 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+ application.GetScene().Add(model1);
+
+ gResourceReadyCalled = false;
+ model1.ResourceReadySignal().Connect(&OnResourceReady);
+ DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ application.SendNotification();
+ application.Render();
+
+ // Check that the loading has finished for mode1
+ DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(cacheManager.GetModelCacheRefCount(TEST_GLTF_FILE_NAME), 1u, TEST_LOCATION);
+ DALI_TEST_EQUALS(cacheManager.IsSceneLoading(TEST_GLTF_FILE_NAME), false, TEST_LOCATION);
+ DALI_TEST_EQUALS(cacheManager.IsSceneLoaded(TEST_GLTF_FILE_NAME), true, TEST_LOCATION);
+
+ // Load the second instance of the same model and add it to the scene
+ Scene3D::Model model2 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+ application.GetScene().Add(model2);
+
+ gResourceReadyCalled = false;
+ model2.ResourceReadySignal().Connect(&OnResourceReady);
+ DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ application.SendNotification();
+ application.Render();
+
+ // Check that the loading has finished for model2
+ DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(cacheManager.GetModelCacheRefCount(TEST_GLTF_FILE_NAME), 2u, TEST_LOCATION);
+ DALI_TEST_EQUALS(cacheManager.IsSceneLoading(TEST_GLTF_FILE_NAME), false, TEST_LOCATION);
+ DALI_TEST_EQUALS(cacheManager.IsSceneLoaded(TEST_GLTF_FILE_NAME), true, TEST_LOCATION);
+
+ Actor meshActor1 = model1.FindChildByName("AnimatedCube");
+ Actor meshActor2 = model2.FindChildByName("AnimatedCube");
+ DALI_TEST_CHECK(meshActor1);
+ DALI_TEST_CHECK(meshActor2);
+
+ Renderer renderer1 = meshActor1.GetRendererAt(0u);
+ Renderer renderer2 = meshActor2.GetRendererAt(0u);
+ DALI_TEST_CHECK(renderer1);
+ DALI_TEST_CHECK(renderer2);
+
+ // Check that the two instances use the shared textures and geometries from the cache
+ DALI_TEST_EQUALS(renderer1.GetTextures(), renderer2.GetTextures(), TEST_LOCATION);
+ DALI_TEST_EQUALS(renderer1.GetGeometry(), renderer2.GetGeometry(), TEST_LOCATION);
+
+ // Destroy model1
+ model1.Unparent();
+ model1.Reset();
+
+ application.SendNotification();
+ application.Render();
+
+ // Check that the reference count of the cmodel cache is decreased by 1 after model1 is destroyed
+ DALI_TEST_EQUALS(cacheManager.GetModelCacheRefCount(TEST_GLTF_FILE_NAME), 1u, TEST_LOCATION);
+
+ // Load another instance of the same model and add it to the scene
+ Scene3D::Model model3 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+ application.GetScene().Add(model3);
+
+ gResourceReadyCalled = false;
+ model3.ResourceReadySignal().Connect(&OnResourceReady);
+ DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ application.SendNotification();
+ application.Render();
+
+ // Check that the loading has finished for model3
+ DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(cacheManager.GetModelCacheRefCount(TEST_GLTF_FILE_NAME), 2u, TEST_LOCATION);
+ DALI_TEST_EQUALS(cacheManager.IsSceneLoading(TEST_GLTF_FILE_NAME), false, TEST_LOCATION);
+ DALI_TEST_EQUALS(cacheManager.IsSceneLoaded(TEST_GLTF_FILE_NAME), true, TEST_LOCATION);
+
+ Actor meshActor3 = model3.FindChildByName("AnimatedCube");
+ DALI_TEST_CHECK(meshActor3);
+
+ Renderer renderer3 = meshActor3.GetRendererAt(0u);
+ DALI_TEST_CHECK(renderer3);
+
+ // Check that model2 and model3 use the shared textures and geometries from the cache
+ DALI_TEST_EQUALS(renderer2.GetTextures(), renderer3.GetTextures(), TEST_LOCATION);
+ DALI_TEST_EQUALS(renderer2.GetGeometry(), renderer3.GetGeometry(), TEST_LOCATION);
+
+ // Destroy model2 and model3
+ model2.Unparent();
+ model2.Reset();
+
+ model3.Unparent();
+ model3.Reset();
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(cacheManager.GetModelCacheRefCount(TEST_GLTF_FILE_NAME), 0u, TEST_LOCATION);
+
+ END_TEST;
+}
+
/*
- * 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.
Vector3::ZAXIS * -100.f);
camParams.orthographicSize = 3.0f;
camParams.aspectRatio = 1.0f;
- camParams.yFov = Degree(Radian(M_PI * .5)).degree;
+ camParams.yFovDegree = Degree(Radian(M_PI * .5));
camParams.zNear = 1.f;
camParams.zFar = 1000.f;
if(camParams.isPerspective)
{
- DALI_TEST_EQUAL(camera.GetProperty(Dali::CameraActor::Property::FIELD_OF_VIEW).Get<float>(), Radian(Degree(camParams.yFov)).radian);
+ DALI_TEST_EQUAL(camera.GetProperty(Dali::CameraActor::Property::FIELD_OF_VIEW).Get<float>(), Radian(camParams.yFovDegree).radian);
}
else
{
LoadGltfScene(TEST_RESOURCE_DIR "/AnimatedCube.gltf", sdf, ctx.loadResult);
DALI_TEST_EQUAL(1u, ctx.scene.GetRoots().size());
- DALI_TEST_EQUAL(6u, ctx.scene.GetNodeCount());
+ DALI_TEST_EQUAL(9u, ctx.scene.GetNodeCount());
// Default envmap is used
DALI_TEST_EQUAL(1u, ctx.resources.mEnvironmentMaps.size());
DALI_TEST_EQUAL(2u, ctx.resources.mShaders.size());
DALI_TEST_EQUAL(0u, ctx.resources.mSkeletons.size());
- DALI_TEST_EQUAL(3u, ctx.cameras.size());
+ DALI_TEST_EQUAL(6u, ctx.cameras.size());
DALI_TEST_EQUAL(0u, ctx.lights.size());
DALI_TEST_EQUAL(1u, ctx.animations.size());
DALI_TEST_EQUAL(0u, ctx.animationGroups.size());
/*
- * 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.
#include <dali-scene3d/public-api/controls/model/model.h>
+#include <dali/devel-api/actors/camera-actor-devel.h>
+
using namespace Dali;
using namespace Dali::Toolkit;
const char* TEST_GLTF_ANIMATION_TEST_FILE_NAME = TEST_RESOURCE_DIR "/animationTest.gltf";
const char* TEST_GLTF_MULTIPLE_PRIMITIVE_FILE_NAME = TEST_RESOURCE_DIR "/simpleMultiplePrimitiveTest.gltf";
const char* TEST_DLI_FILE_NAME = TEST_RESOURCE_DIR "/arc.dli";
-const char* TEST_DLI_EXERCISE_FILE_NAME = TEST_RESOURCE_DIR "/exercise.dli";
+// @TODO: The test cases for loading the DLI model below is temporarily disabled.
+// Need to fix how resources are loaded when a model contains multiple scenes and
+// each scene has its own root node.
+#ifdef MULTIPLE_SCENES_MODEL_SUPPORT
+const char* TEST_DLI_EXERCISE_FILE_NAME = TEST_RESOURCE_DIR "/exercise.dli";
+#endif
/**
* For the diffuse and specular cube map texture.
* These textures are based off version of Wave engine sample
* Take from https://github.com/WaveEngine/Samples
*
- * Copyright (c) 2022 Wave Coorporation
+ * Copyright (c) 2023 Wave Coorporation
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
- Texture newDiffuseTexture = textureSet.GetTexture(7u);
- Texture newSpecularTexture = textureSet.GetTexture(8u);
+ TextureSet newTextureSet = renderer.GetTextures();
+ Texture newDiffuseTexture = newTextureSet.GetTexture(7u);
+ Texture newSpecularTexture = newTextureSet.GetTexture(8u);
DALI_TEST_NOT_EQUALS(diffuseTexture, newDiffuseTexture, 0.0f, TEST_LOCATION);
DALI_TEST_NOT_EQUALS(specularTexture, newSpecularTexture, 0.0f, TEST_LOCATION);
int UtcDaliModelAnimation03(void)
{
+#ifdef MULTIPLE_SCENES_MODEL_SUPPORT
ToolkitTestApplication application;
Scene3D::Model model = Scene3D::Model::New(TEST_DLI_EXERCISE_FILE_NAME);
Animation animationByName = model.GetAnimation("idleClip");
DALI_TEST_CHECK(animationByName);
DALI_TEST_EQUALS(animationByIndex, animationByName, TEST_LOCATION);
+#else
+ tet_result(TET_PASS);
+#endif
+
+ END_TEST;
+}
+
+int UtcDaliModelCameraGenerate01(void)
+{
+#ifdef MULTIPLE_SCENES_MODEL_SUPPORT
+ ToolkitTestApplication application;
+
+ Scene3D::Model model = Scene3D::Model::New(TEST_DLI_EXERCISE_FILE_NAME);
+ model.SetProperty(Dali::Actor::Property::SIZE, Vector2(50, 50));
+ application.GetScene().Add(model);
+
+ gResourceReadyCalled = false;
+ model.ResourceReadySignal().Connect(&OnResourceReady);
+ DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
+ uint32_t cameraCount = model.GetCameraCount();
+ DALI_TEST_EQUALS(1, cameraCount, TEST_LOCATION);
+
+ CameraActor generatedCamera = model.GenerateCamera(0u);
+ DALI_TEST_CHECK(generatedCamera);
+
+ generatedCamera = model.GenerateCamera(1u); // Fail to generate camera
+ DALI_TEST_CHECK(!generatedCamera);
+#else
+ tet_result(TET_PASS);
+#endif
+
+ END_TEST;
+}
+
+int UtcDaliModelCameraGenerate02(void)
+{
+ ToolkitTestApplication application;
+
+ Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+ model.SetProperty(Dali::Actor::Property::SIZE, Vector2(50, 50));
+ application.GetScene().Add(model);
+
+ gResourceReadyCalled = false;
+ model.ResourceReadySignal().Connect(&OnResourceReady);
+ DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
+ uint32_t cameraCount = model.GetCameraCount();
+ DALI_TEST_EQUALS(6, cameraCount, TEST_LOCATION);
+
+ CameraActor generatedCamera0 = model.GenerateCamera(0u);
+ DALI_TEST_CHECK(generatedCamera0);
+ CameraActor generatedCamera1 = model.GenerateCamera(1u);
+ DALI_TEST_CHECK(generatedCamera1);
+ CameraActor generatedCamera2 = model.GenerateCamera(2u);
+ DALI_TEST_CHECK(generatedCamera2);
+ CameraActor generatedCamera3 = model.GenerateCamera(3u); // Infinity far camera
+ DALI_TEST_CHECK(generatedCamera3);
+ CameraActor generatedCamera4 = model.GenerateCamera(4u); // Broken camera 1
+ DALI_TEST_CHECK(!generatedCamera4);
+ CameraActor generatedCamera5 = model.GenerateCamera(5u); // Broken camera 2
+ DALI_TEST_CHECK(!generatedCamera5);
+ CameraActor generatedCamera6 = model.GenerateCamera(6u); // Out of bound
+ DALI_TEST_CHECK(!generatedCamera6);
+
+ CameraActor appliedCamera;
+ DALI_TEST_EQUALS(model.ApplyCamera(0u, appliedCamera), false, TEST_LOCATION); // Cannot apply into empty camera.
+
+ auto CompareCameraProperties = [](CameraActor lhs, CameraActor rhs, const char* location) {
+ DALI_TEST_EQUALS(lhs.GetProperty<int>(Dali::CameraActor::Property::PROJECTION_MODE), rhs.GetProperty<int>(Dali::CameraActor::Property::PROJECTION_MODE), TEST_LOCATION);
+ DALI_TEST_EQUALS(lhs.GetProperty<float>(Dali::CameraActor::Property::NEAR_PLANE_DISTANCE), rhs.GetProperty<float>(Dali::CameraActor::Property::NEAR_PLANE_DISTANCE), TEST_LOCATION);
+
+ if(lhs.GetProperty<int>(Dali::CameraActor::Property::PROJECTION_MODE) == static_cast<int>(Dali::Camera::ProjectionMode::PERSPECTIVE_PROJECTION))
+ {
+ DALI_TEST_EQUALS(lhs.GetProperty<float>(Dali::CameraActor::Property::FIELD_OF_VIEW), rhs.GetProperty<float>(Dali::CameraActor::Property::FIELD_OF_VIEW), TEST_LOCATION);
+ // TODO : Open this test when infinity far projection implement.
+ //DALI_TEST_EQUALS(lhs.GetProperty<float>(Dali::CameraActor::Property::FAR_PLANE_DISTANCE), rhs.GetProperty<float>(Dali::CameraActor::Property::FAR_PLANE_DISTANCE), TEST_LOCATION);
+ }
+ else
+ {
+ DALI_TEST_EQUALS(lhs.GetProperty<float>(Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE), rhs.GetProperty<float>(Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE), TEST_LOCATION);
+ DALI_TEST_EQUALS(lhs.GetProperty<float>(Dali::CameraActor::Property::FAR_PLANE_DISTANCE), rhs.GetProperty<float>(Dali::CameraActor::Property::FAR_PLANE_DISTANCE), TEST_LOCATION);
+ }
+ };
+
+ appliedCamera = CameraActor::New();
+ DALI_TEST_EQUALS(model.ApplyCamera(0u, appliedCamera), true, TEST_LOCATION);
+ CompareCameraProperties(generatedCamera0, appliedCamera, TEST_LOCATION);
+ DALI_TEST_EQUALS(model.ApplyCamera(1u, appliedCamera), true, TEST_LOCATION);
+ CompareCameraProperties(generatedCamera1, appliedCamera, TEST_LOCATION);
+ DALI_TEST_EQUALS(model.ApplyCamera(2u, appliedCamera), true, TEST_LOCATION);
+ CompareCameraProperties(generatedCamera2, appliedCamera, TEST_LOCATION);
+ DALI_TEST_EQUALS(model.ApplyCamera(3u, appliedCamera), true, TEST_LOCATION);
+ CompareCameraProperties(generatedCamera3, appliedCamera, TEST_LOCATION);
+ DALI_TEST_EQUALS(model.ApplyCamera(4u, appliedCamera), false, TEST_LOCATION); // Broken camera 1
+ DALI_TEST_EQUALS(model.ApplyCamera(5u, appliedCamera), false, TEST_LOCATION); // Broken camera 2
+ DALI_TEST_EQUALS(model.ApplyCamera(6u, appliedCamera), false, TEST_LOCATION); // Cannot apply over the index.
END_TEST;
}
application.SendNotification();
application.Render();
- Actor actor = model.FindChildByName("AnimatedCube");
- Vector4 childColor = actor[Dali::Actor::Property::COLOR];
+ Actor actor = model.FindChildByName("AnimatedCube");
+ Vector4 childColor = actor[Dali::Actor::Property::COLOR];
Vector4 childWorldColor = actor[Dali::Actor::Property::WORLD_COLOR];
DALI_TEST_EQUALS(childColor, Color::WHITE, TEST_LOCATION);
END_TEST;
}
+
int UtcDaliModelResourceReady(void)
{
ToolkitTestApplication application;
END_TEST;
}
+
+int UtcDaliModelResourceCacheCheck(void)
+{
+ ToolkitTestApplication application;
+
+ // Load three instances of the same model and add them to the scene
+ Scene3D::Model model1 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+ Scene3D::Model model2 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+ Scene3D::Model model3 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+
+ application.GetScene().Add(model1);
+ application.GetScene().Add(model2);
+ application.GetScene().Add(model3);
+
+ gResourceReadyCalled = false;
+ model1.ResourceReadySignal().Connect(&OnResourceReady);
+ model2.ResourceReadySignal().Connect(&OnResourceReady);
+ model3.ResourceReadySignal().Connect(&OnResourceReady);
+ DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION);
+ application.SendNotification();
+ application.Render();
+
+ // Check that the loading has finished for all the three instances
+ DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
+ Actor meshActor1 = model1.FindChildByName("AnimatedCube");
+ Actor meshActor2 = model2.FindChildByName("AnimatedCube");
+ Actor meshActor3 = model3.FindChildByName("AnimatedCube");
+ DALI_TEST_CHECK(meshActor1);
+ DALI_TEST_CHECK(meshActor2);
+ DALI_TEST_CHECK(meshActor3);
+
+ Renderer renderer1 = meshActor1.GetRendererAt(0u);
+ Renderer renderer2 = meshActor2.GetRendererAt(0u);
+ Renderer renderer3 = meshActor3.GetRendererAt(0u);
+ DALI_TEST_CHECK(renderer1);
+ DALI_TEST_CHECK(renderer2);
+ DALI_TEST_CHECK(renderer3);
+
+ // Check that all the three instances use the shared textures and geometries from the cache
+ // but have their own shader objects
+ DALI_TEST_EQUALS(renderer1.GetTextures(), renderer2.GetTextures(), TEST_LOCATION);
+ DALI_TEST_EQUALS(renderer1.GetTextures(), renderer3.GetTextures(), TEST_LOCATION);
+ DALI_TEST_EQUALS(renderer1.GetGeometry(), renderer2.GetGeometry(), TEST_LOCATION);
+ DALI_TEST_EQUALS(renderer1.GetGeometry(), renderer3.GetGeometry(), TEST_LOCATION);
+ DALI_TEST_NOT_EQUALS(renderer1.GetShader(), renderer2.GetShader(), 0.0f, TEST_LOCATION);
+ DALI_TEST_NOT_EQUALS(renderer1.GetShader(), renderer3.GetShader(), 0.0f, TEST_LOCATION);
+ DALI_TEST_NOT_EQUALS(renderer2.GetShader(), renderer3.GetShader(), 0.0f, TEST_LOCATION);
+
+ // Destroy model1
+ model1.Unparent();
+ model1.Reset();
+
+ // Check that all the other two instances still use the shared textures and geometries from the cache
+ // but have their own shader objects
+ DALI_TEST_EQUALS(renderer2.GetTextures(), renderer3.GetTextures(), TEST_LOCATION);
+ DALI_TEST_EQUALS(renderer2.GetGeometry(), renderer3.GetGeometry(), TEST_LOCATION);
+ DALI_TEST_NOT_EQUALS(renderer2.GetShader(), renderer3.GetShader(), 0.0f, TEST_LOCATION);
+
+ // Set new IBL textures for model2, and this should apply to model2 instance only
+ gResourceReadyCalled = false;
+ DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+ model2.SetImageBasedLightSource(TEST_DIFFUSE_TEXTURE, TEST_SPECULAR_TEXTURE);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ application.SendNotification();
+ application.Render();
+
+ // Check that the new IBL textures are loaded for model2
+ DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
+ // Check that the two instances still use the shared geometries from the cache
+ // but now have their own shader objects and different texture set
+ DALI_TEST_NOT_EQUALS(renderer2.GetTextures(), renderer3.GetTextures(), 0.0f, TEST_LOCATION);
+ DALI_TEST_EQUALS(renderer2.GetGeometry(), renderer3.GetGeometry(), TEST_LOCATION);
+ DALI_TEST_NOT_EQUALS(renderer2.GetShader(), renderer3.GetShader(), 0.0f, TEST_LOCATION);
+
+ // Check that the two instances now have their own diffuse texture and specular texture,
+ // but all the other textures are still the same
+ TextureSet textureSet2 = renderer2.GetTextures();
+ TextureSet textureSet3 = renderer3.GetTextures();
+ DALI_TEST_EQUALS(textureSet2.GetTextureCount(), 9u, TEST_LOCATION);
+ DALI_TEST_EQUALS(textureSet3.GetTextureCount(), 9u, TEST_LOCATION);
+
+ for (uint32_t i = 0; i < 7u; i++)
+ {
+ DALI_TEST_EQUALS(textureSet2.GetTexture(i), textureSet3.GetTexture(i), TEST_LOCATION);
+ }
+
+ DALI_TEST_NOT_EQUALS(textureSet2.GetTexture(7u), textureSet3.GetTexture(7u), 0.0f, TEST_LOCATION);
+ DALI_TEST_NOT_EQUALS(textureSet2.GetTexture(8u), textureSet3.GetTexture(8u), 0.0f, TEST_LOCATION);
+
+ END_TEST;
+}
+
application.SendNotification();
application.Render();
- DALI_TEST_NOT_EQUALS(GetDiffuseTexture(modelView1), GetDiffuseTexture(modelView2), 0.0f, TEST_LOCATION);
- DALI_TEST_NOT_EQUALS(GetSpecularTexture(modelView1), GetSpecularTexture(modelView2), 0.0f, TEST_LOCATION);
+ DALI_TEST_EQUALS(GetDiffuseTexture(modelView1), GetDiffuseTexture(modelView2), TEST_LOCATION);
+ DALI_TEST_EQUALS(GetSpecularTexture(modelView1), GetSpecularTexture(modelView2), TEST_LOCATION);
DALI_TEST_NOT_EQUALS(GetDiffuseTexture(modelView1), GetDiffuseTexture(modelView3), 0.0f, TEST_LOCATION);
DALI_TEST_NOT_EQUALS(GetSpecularTexture(modelView1), GetSpecularTexture(modelView3), 0.0f, TEST_LOCATION);
--- /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/common/model-cache-manager.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/common/singleton-service.h>
+#include <dali/public-api/object/base-object.h>
+#include <unordered_map>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/loader/load-result.h>
+#include <dali-scene3d/public-api/loader/scene-definition.h>
+
+namespace Dali::Scene3D::Internal
+{
+class ModelCacheManager::Impl : public Dali::BaseObject
+{
+public:
+ /**
+ * @brief Constructor
+ */
+ Impl()
+ {
+ }
+
+ Dali::Scene3D::Loader::LoadResult GetModelLoadResult(std::string modelUri)
+ {
+ ModelCache& cache = mModelCache[modelUri];
+ return Dali::Scene3D::Loader::LoadResult{cache.resources, cache.scene, cache.metaData, cache.animationDefinitions, cache.amimationGroupDefinitions, cache.cameraParameters, cache.lights};
+ }
+
+ uint32_t GetModelCacheRefCount(std::string modelUri)
+ {
+ ModelCache& cache = mModelCache[modelUri];
+ return cache.refCount;
+ }
+
+ Dali::ConditionalWait& GetLoadSceneConditionalWaitInstance(std::string modelUri)
+ {
+ ModelCache& cache = mModelCache[modelUri];
+ return cache.loadSceneConditionalWait;
+ }
+
+ Dali::ConditionalWait& GetLoadRawResourceConditionalWaitInstance(std::string modelUri)
+ {
+ ModelCache& cache = mModelCache[modelUri];
+ return cache.loadRawResourceConditionalWait;
+ }
+
+ void ReferenceModelCache(std::string modelUri)
+ {
+ ModelCache& cache = mModelCache[modelUri];
+ cache.refCount++;
+ }
+
+ void UnreferenceModelCache(std::string modelUri)
+ {
+ ModelCache& cache = mModelCache[modelUri];
+ if(cache.refCount > 0)
+ {
+ cache.refCount--;
+ }
+
+ if(cache.refCount == 0)
+ {
+ mModelCache.erase(modelUri);
+ }
+ }
+
+ bool IsSceneLoaded(std::string modelUri)
+ {
+ ModelCache& cache = mModelCache[modelUri];
+ return cache.isSceneLoaded;
+ }
+
+ void SetSceneLoaded(std::string modelUri, bool isSceneLoaded)
+ {
+ ModelCache& cache = mModelCache[modelUri];
+ cache.isSceneLoaded = isSceneLoaded;
+ }
+
+ bool IsSceneLoading(std::string modelUri)
+ {
+ ModelCache& cache = mModelCache[modelUri];
+ return cache.isSceneLoading;
+ }
+
+ void SetSceneLoading(std::string modelUri, bool isSceneLoading)
+ {
+ ModelCache& cache = mModelCache[modelUri];
+ cache.isSceneLoading = isSceneLoading;
+ }
+
+protected:
+ /**
+ * A reference counted object may only be deleted by calling Unreference()
+ */
+ virtual ~Impl()
+ {
+ }
+
+private:
+ struct ModelCache
+ {
+ Dali::Scene3D::Loader::ResourceBundle resources{}; ///< The bundle to store resources in.
+ Dali::Scene3D::Loader::SceneDefinition scene{}; ///< The scene definition to populate.
+ Dali::Scene3D::Loader::SceneMetadata metaData{}; ///< The metadata of the scene.
+
+ std::vector<Dali::Scene3D::Loader::AnimationDefinition> animationDefinitions{}; ///< The list of animation definitions, in lexicographical order of their names.
+ std::vector<Dali::Scene3D::Loader::AnimationGroupDefinition> amimationGroupDefinitions{}; ///< The list of animation group definitions, in lexicographical order of their names.
+ std::vector<Dali::Scene3D::Loader::CameraParameters> cameraParameters{}; ///< The camera parameters that were loaded from the scene.
+ std::vector<Dali::Scene3D::Loader::LightParameters> lights{}; ///< The light parameters that were loaded from the scene.
+
+ uint32_t refCount{0}; ///< The reference count of this model cache.
+ Dali::ConditionalWait loadSceneConditionalWait{}; ///< The conditionalWait instance used to synchronise the loading of the scene for the same model in different threads.
+ Dali::ConditionalWait loadRawResourceConditionalWait{}; ///< The conditionalWait instance used to synchronise the loading of the shared raw resources for the same model in different threads.
+
+ bool isSceneLoaded{false}; ///< Whether the scene of the model has been loaded.
+ bool isSceneLoading{false}; ///< Whether the scene loading of the model is in progress.
+ };
+
+ using ModelResourceCache = std::unordered_map<std::string, ModelCache>;
+ ModelResourceCache mModelCache;
+};
+
+ModelCacheManager::ModelCacheManager() = default;
+
+ModelCacheManager::~ModelCacheManager() = default;
+
+ModelCacheManager ModelCacheManager::Get()
+{
+ ModelCacheManager manager;
+
+ // Check whether the ModelCacheManager is already created
+ SingletonService singletonService(SingletonService::Get());
+ if(singletonService)
+ {
+ Dali::BaseHandle handle = singletonService.GetSingleton(typeid(ModelCacheManager));
+ if(handle)
+ {
+ // If so, downcast the handle of singleton to ModelCacheManager
+ manager = ModelCacheManager(dynamic_cast<ModelCacheManager::Impl*>(handle.GetObjectPtr()));
+ }
+
+ if(!manager)
+ {
+ // If not, create the ModelCacheManager and register it as a singleton
+ manager = ModelCacheManager(new ModelCacheManager::Impl());
+ singletonService.Register(typeid(manager), manager);
+ }
+ }
+
+ return manager;
+}
+
+ModelCacheManager::ModelCacheManager(ModelCacheManager::Impl* impl)
+: BaseHandle(impl)
+{
+}
+
+Dali::Scene3D::Loader::LoadResult ModelCacheManager::GetModelLoadResult(std::string modelUri)
+{
+ ModelCacheManager::Impl& impl = static_cast<ModelCacheManager::Impl&>(GetBaseObject());
+ return impl.GetModelLoadResult(modelUri);
+}
+
+uint32_t ModelCacheManager::GetModelCacheRefCount(std::string modelUri)
+{
+ ModelCacheManager::Impl& impl = static_cast<ModelCacheManager::Impl&>(GetBaseObject());
+ return impl.GetModelCacheRefCount(modelUri);
+}
+
+Dali::ConditionalWait& ModelCacheManager::GetLoadSceneConditionalWaitInstance(std::string modelUri)
+{
+ ModelCacheManager::Impl& impl = static_cast<ModelCacheManager::Impl&>(GetBaseObject());
+ return impl.GetLoadSceneConditionalWaitInstance(modelUri);
+}
+
+Dali::ConditionalWait& ModelCacheManager::GetLoadRawResourceConditionalWaitInstance(std::string modelUri)
+{
+ ModelCacheManager::Impl& impl = static_cast<ModelCacheManager::Impl&>(GetBaseObject());
+ return impl.GetLoadRawResourceConditionalWaitInstance(modelUri);
+}
+
+void ModelCacheManager::ReferenceModelCache(std::string modelUri)
+{
+ ModelCacheManager::Impl& impl = static_cast<ModelCacheManager::Impl&>(GetBaseObject());
+ impl.ReferenceModelCache(modelUri);
+}
+
+void ModelCacheManager::UnreferenceModelCache(std::string modelUri)
+{
+ ModelCacheManager::Impl& impl = static_cast<ModelCacheManager::Impl&>(GetBaseObject());
+ impl.UnreferenceModelCache(modelUri);
+}
+
+bool ModelCacheManager::IsSceneLoaded(std::string modelUri)
+{
+ ModelCacheManager::Impl& impl = static_cast<ModelCacheManager::Impl&>(GetBaseObject());
+ return impl.IsSceneLoaded(modelUri);
+}
+
+void ModelCacheManager::SetSceneLoaded(std::string modelUri, bool isSceneLoaded)
+{
+ ModelCacheManager::Impl& impl = static_cast<ModelCacheManager::Impl&>(GetBaseObject());
+ impl.SetSceneLoaded(modelUri, isSceneLoaded);
+}
+
+bool ModelCacheManager::IsSceneLoading(std::string modelUri)
+{
+ ModelCacheManager::Impl& impl = static_cast<ModelCacheManager::Impl&>(GetBaseObject());
+ return impl.IsSceneLoading(modelUri);
+}
+
+void ModelCacheManager::SetSceneLoading(std::string modelUri, bool isSceneLoading)
+{
+ ModelCacheManager::Impl& impl = static_cast<ModelCacheManager::Impl&>(GetBaseObject());
+ impl.SetSceneLoading(modelUri, isSceneLoading);
+}
+
+} // namespace Dali::Scene3D::Internal
--- /dev/null
+#ifndef DALI_SCENE3D_MODEL_CACHE_MANAGER_H
+#define DALI_SCENE3D_MODEL_CACHE_MANAGER_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/threading/conditional-wait.h>
+#include <dali/public-api/object/base-handle.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/loader/load-result.h>
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Internal
+{
+class ModelCacheManager;
+
+/**
+ * A singleton class to manage the cache of 3D models so that the resources of the same model
+ * are only loaded once and kept in the cache. The cached resources will be reused when the
+ * same model is loaded multiple times.
+ */
+class ModelCacheManager : public Dali::BaseHandle
+{
+public:
+ /**
+ * @brief Creates a ModelCacheManager handle.
+ *
+ * Calling member functions with an uninitialised handle is not allowed.
+ */
+ ModelCacheManager();
+
+ /**
+ * @brief Destructor
+ *
+ * This is non-virtual since derived Handle types must not contain data or virtual methods.
+ */
+ ~ModelCacheManager();
+
+ /**
+ * @brief Create or retrieve the ModelCacheManager singleton.
+ *
+ * @return A handle to the ModelCacheManager.
+ */
+ static ModelCacheManager Get();
+
+ /**
+ * @brief Retrieves the load result for the model with the given URI.
+ * If there is no existing load result for the given model, a new one will be created.
+ * @param[in] modelUri The unique model URI with its absolute path.
+ * @return A reference to the model's load result.
+ */
+ Dali::Scene3D::Loader::LoadResult GetModelLoadResult(std::string modelUri);
+
+ /**
+ * @brief Retrieves the reference count of the cache for the model with the given URI.
+ * @param[in] modelUri The unique model URI with its absolute path.
+ * @return The reference count of the cache.
+ */
+ uint32_t GetModelCacheRefCount(std::string modelUri);
+
+ /**
+ * @brief Retrieves the ConditionalWait object to synchronize the scene loading of the model
+ * with the given URI between multiple threads.
+ * @param[in] modelUri The unique model URI with its absolute path.
+ * @return The ConditionalWait object.
+ */
+ Dali::ConditionalWait& GetLoadSceneConditionalWaitInstance(std::string modelUri);
+
+ /**
+ * @brief Retrieves the ConditionalWait object to synchronize the raw resources loading of the
+ * model with the given URI between multiple threads.
+ * @param[in] modelUri The unique model URI with its absolute path.
+ * @return The ConditionalWait object.
+ */
+ Dali::ConditionalWait& GetLoadRawResourceConditionalWaitInstance(std::string modelUri);
+
+ /**
+ * @brief Reference the cache of the model with the given URI.
+ * This will increment the reference count of the load result by 1.
+ * @param[in] modelUri The model URI.
+ */
+ void ReferenceModelCache(std::string modelUri);
+
+ /**
+ * @brief Unreference the cache of the model with the given URI.
+ * This will decrement the reference count of the load result by 1.
+ * When the reference count becomes zero, the model will be removed from the cache and all
+ * its resources will be deleted.
+ * @param[in] modelUri The model URI.
+ */
+ void UnreferenceModelCache(std::string modelUri);
+
+ /**
+ * @brief Retrieves whether the scene of the model with the given URI has been loaded.
+ * @param[in] modelUri The unique model URI with its absolute path.
+ * @return whether the scene of the model has been loaded. This will be true if the scene
+ * has been loaded for once.
+ */
+ bool IsSceneLoaded(std::string modelUri);
+
+ /**
+ * @brief Sets whether the scene of the model with the given URI has been loaded.
+ * @param[in] modelUri The unique model URI with its absolute path.
+ * @param[in] isSceneLoaded Whether the scene of the model has been loaded.
+ */
+ void SetSceneLoaded(std::string modelUri, bool isSceneLoaded);
+
+ /**
+ * @brief Retrieves whether the scene loading of the model with the given URI is in progress.
+ * @param[in] modelUri The unique model URI with its absolute path.
+ * @return whether the scene loading of the model is in progress.
+ */
+ bool IsSceneLoading(std::string modelUri);
+
+ /**
+ * @brief Sets whether the scene loading of the model with the given URI is in progress.
+ * @param[in] modelUri The unique model URI with its absolute path.
+ * @param[in] isSceneLoading Whether the scene loading of the model is in progress.
+ */
+ void SetSceneLoading(std::string modelUri, bool isSceneLoading);
+
+public:
+ // Default copy and move operator
+ ModelCacheManager(const ModelCacheManager& rhs) = default;
+ ModelCacheManager(ModelCacheManager&& rhs) = default;
+ ModelCacheManager& operator=(const ModelCacheManager& rhs) = default;
+ ModelCacheManager& operator=(ModelCacheManager&& rhs) = default;
+
+private:
+ class Impl;
+ explicit DALI_INTERNAL ModelCacheManager(ModelCacheManager::Impl* impl);
+};
+
+} // namespace Internal
+
+} // namespace Scene3D
+
+} // namespace Dali
+
+#endif // DALI_SCENE3D_MODEL_CACHE_MANAGER_H
#include <dali-scene3d/internal/common/model-load-task.h>
// EXTERNAL INCLUDES
-#include <filesystem>
#include <dali/integration-api/debug.h>
+#include <filesystem>
// INTERNAL INCLUDES
+#include <dali-scene3d/internal/common/model-cache-manager.h>
#include <dali-scene3d/public-api/loader/animation-definition.h>
#include <dali-scene3d/public-api/loader/camera-parameters.h>
#include <dali-scene3d/public-api/loader/dli-loader.h>
#include <dali-scene3d/public-api/loader/node-definition.h>
#include <dali-scene3d/public-api/loader/shader-definition-factory.h>
-
namespace Dali
{
namespace Scene3D
: AsyncTask(callback),
mModelUrl(modelUrl),
mResourceDirectoryUrl(resourceDirectoryUrl),
- mHasSucceeded(false)
+ mHasSucceeded(false),
+ mModelCacheManager(ModelCacheManager::Get()),
+ mLoadResult(mModelCacheManager.GetModelLoadResult(modelUrl))
{
}
void ModelLoadTask::Process()
{
+ uint32_t cacheRefCount = mModelCacheManager.GetModelCacheRefCount(mModelUrl);
+ Dali::ConditionalWait& loadSceneConditionalWait = mModelCacheManager.GetLoadSceneConditionalWaitInstance(mModelUrl);
+ Dali::ConditionalWait& loadRawResourceConditionalWait = mModelCacheManager.GetLoadRawResourceConditionalWaitInstance(mModelUrl);
+
std::filesystem::path modelUrl(mModelUrl);
if(mResourceDirectoryUrl.empty())
{
std::string extension = modelUrl.extension();
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
- Dali::Scene3D::Loader::ResourceBundle::PathProvider pathProvider = [&](Dali::Scene3D::Loader::ResourceType::Value type)
- {
+ Dali::Scene3D::Loader::ResourceBundle::PathProvider pathProvider = [&](Dali::Scene3D::Loader::ResourceType::Value type) {
return mResourceDirectoryUrl;
};
- mAnimations.clear();
-
- std::filesystem::path metaDataUrl = modelUrl;
- metaDataUrl.replace_extension(METADATA_EXTENSION.data());
-
- Dali::Scene3D::Loader::LoadSceneMetadata(metaDataUrl.c_str(), mMetaData);
- Dali::Scene3D::Loader::LoadResult result{mResources, mScene, mMetaData, mAnimations, mAnimGroups, mCameraParameters, mLights};
-
- if(extension == DLI_EXTENSION)
{
- Dali::Scene3D::Loader::DliLoader loader;
- Dali::Scene3D::Loader::DliLoader::InputParams input{
- pathProvider(Dali::Scene3D::Loader::ResourceType::Mesh),
- nullptr,
- {},
- {},
- nullptr,
- {}};
- Dali::Scene3D::Loader::DliLoader::LoadParams loadParams{input, result};
- if(!loader.LoadScene(mModelUrl, loadParams))
+ ConditionalWait::ScopedLock lock(loadSceneConditionalWait);
+
+ while(cacheRefCount > 1 && mModelCacheManager.IsSceneLoading(mModelUrl))
{
- DALI_LOG_ERROR("Failed to load scene from '%s': %s\n", mModelUrl.c_str(), loader.GetParseError().c_str());
- return;
+ loadSceneConditionalWait.Wait();
}
}
- else if(extension == GLTF_EXTENSION)
+
{
- Dali::Scene3D::Loader::ShaderDefinitionFactory sdf;
- sdf.SetResources(mResources);
- Dali::Scene3D::Loader::LoadGltfScene(mModelUrl, sdf, result);
+ ConditionalWait::ScopedLock lock(loadSceneConditionalWait);
+
+ if(!mModelCacheManager.IsSceneLoaded(mModelUrl))
+ {
+ mModelCacheManager.SetSceneLoading(mModelUrl, true);
+
+ std::filesystem::path metaDataUrl = modelUrl;
+ metaDataUrl.replace_extension(METADATA_EXTENSION.data());
+
+ Dali::Scene3D::Loader::LoadSceneMetadata(metaDataUrl.c_str(), mLoadResult.mSceneMetadata);
+
+ mLoadResult.mAnimationDefinitions.clear();
+
+ if(extension == DLI_EXTENSION)
+ {
+ Dali::Scene3D::Loader::DliLoader loader;
+ Dali::Scene3D::Loader::DliLoader::InputParams input{
+ pathProvider(Dali::Scene3D::Loader::ResourceType::Mesh),
+ nullptr,
+ {},
+ {},
+ nullptr,
+ {}};
+ Dali::Scene3D::Loader::DliLoader::LoadParams loadParams{input, mLoadResult};
+ if(!loader.LoadScene(mModelUrl, loadParams))
+ {
+ DALI_LOG_ERROR("Failed to load scene from '%s': %s\n", mModelUrl.c_str(), loader.GetParseError().c_str());
+
+ mModelCacheManager.SetSceneLoaded(mModelUrl, false);
+ mModelCacheManager.SetSceneLoading(mModelUrl, false);
+ mModelCacheManager.UnreferenceModelCache(mModelUrl);
+
+ return;
+ }
+ }
+ else if(extension == GLTF_EXTENSION)
+ {
+ Dali::Scene3D::Loader::ShaderDefinitionFactory sdf;
+ sdf.SetResources(mLoadResult.mResources);
+ Dali::Scene3D::Loader::LoadGltfScene(mModelUrl, sdf, mLoadResult);
+ }
+ else
+ {
+ DALI_LOG_ERROR("Unsupported model type.\n");
+
+ mModelCacheManager.SetSceneLoaded(mModelUrl, false);
+ mModelCacheManager.SetSceneLoading(mModelUrl, false);
+ mModelCacheManager.UnreferenceModelCache(mModelUrl);
+
+ return;
+ }
+
+ mModelCacheManager.SetSceneLoaded(mModelUrl, true);
+ mModelCacheManager.SetSceneLoading(mModelUrl, false);
+ }
}
- else
+
+ loadSceneConditionalWait.Notify();
+
{
- DALI_LOG_ERROR("Unsupported model type.\n");
- return;
+ ConditionalWait::ScopedLock lock(loadRawResourceConditionalWait);
+
+ while(cacheRefCount > 1 && mLoadResult.mResources.mRawResourcesLoading)
+ {
+ loadRawResourceConditionalWait.Wait();
+ }
}
- for(auto iRoot : mScene.GetRoots())
{
- mResourceRefCounts.push_back(mResources.CreateRefCounter());
- mScene.CountResourceRefs(iRoot, mResourceChoices, mResourceRefCounts.back());
- mResources.CountEnvironmentReferences(mResourceRefCounts.back());
+ ConditionalWait::ScopedLock lock(loadRawResourceConditionalWait);
+
+ mResourceRefCount = std::move(mLoadResult.mResources.CreateRefCounter());
- mResources.LoadRawResources(mResourceRefCounts.back(), pathProvider);
+ for(auto iRoot : mLoadResult.mScene.GetRoots())
+ {
+ mLoadResult.mScene.CountResourceRefs(iRoot, mResourceChoices, mResourceRefCount);
+ }
+
+ mLoadResult.mResources.CountEnvironmentReferences(mResourceRefCount);
+
+ mLoadResult.mResources.LoadRawResources(mResourceRefCount, pathProvider);
// glTF Mesh is defined in right hand coordinate system, with positive Y for Up direction.
// Because DALi uses left hand system, Y direciton will be flipped for environment map sampling.
- for(auto&& env : mResources.mEnvironmentMaps)
+ for(auto&& env : mLoadResult.mResources.mEnvironmentMaps)
{
env.first.mYDirection = Y_DIRECTION;
}
}
+
+ loadRawResourceConditionalWait.Notify();
+
mHasSucceeded = true;
}
#define DALI_SCENE3D_MODEL_LOAD_TASK_H
/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <memory>
// INTERNAL INCLUDES
+#include <dali-scene3d/internal/common/model-cache-manager.h>
#include <dali-scene3d/public-api/loader/load-result.h>
#include <dali-scene3d/public-api/loader/scene-definition.h>
+#include <dali/devel-api/threading/conditional-wait.h>
#include <dali/public-api/adaptor-framework/async-task-manager.h>
namespace Dali
public:
/**
* Constructor
- * @param[in] modelUrl model file path.(e.g., glTF, and DLI).
- * @param[in] resourceDirectoryUrl resource file path that includes binary, image etc.
+ * @param[in] modelUrl Model file path.(e.g., glTF, and DLI).
+ * @param[in] resourceDirectoryUrl Resource file path that includes binary, image etc.
* @param[in] callback The callback that is called when the operation is completed.
*/
ModelLoadTask(const std::string& modelUrl, const std::string& resourceDirectoryUrl, CallbackBase* callback);
std::string mModelUrl;
std::string mResourceDirectoryUrl;
- Dali::Scene3D::Loader::ResourceBundle mResources;
- Dali::Scene3D::Loader::SceneDefinition mScene;
- Dali::Scene3D::Loader::SceneMetadata mMetaData;
- std::vector<Dali::Scene3D::Loader::AnimationGroupDefinition> mAnimGroups;
- std::vector<Dali::Scene3D::Loader::CameraParameters> mCameraParameters;
- std::vector<Dali::Scene3D::Loader::LightParameters> mLights;
- std::vector<Dali::Scene3D::Loader::AnimationDefinition> mAnimations;
-
- Dali::Scene3D::Loader::Customization::Choices mResourceChoices;
- std::vector<Dali::Scene3D::Loader::ResourceRefCounts> mResourceRefCounts;
- bool mHasSucceeded;
+ Dali::Scene3D::Loader::Customization::Choices mResourceChoices;
+ Dali::Scene3D::Loader::ResourceRefCounts mResourceRefCount;
+ bool mHasSucceeded;
+
+ ModelCacheManager mModelCacheManager;
+ Dali::Scene3D::Loader::LoadResult mLoadResult;
};
} // namespace Internal
#include <filesystem>
// 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/public-api/controls/model/model.h>
#include <dali-scene3d/public-api/loader/animation-definition.h>
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);
Model::~Model()
{
+ if(ModelCacheManager::Get())
+ {
+ ModelCacheManager::Get().UnreferenceModelCache(mModelUrl);
+ }
+
ResetResourceTasks();
}
return animation;
}
+uint32_t Model::GetCameraCount() const
+{
+ return mCameraParameters.size();
+}
+
+Dali::CameraActor Model::GenerateCamera(uint32_t index) const
+{
+ Dali::CameraActor camera;
+ if(mCameraParameters.size() > index)
+ {
+ camera = Dali::CameraActor::New3DCamera();
+ if(!mCameraParameters[index].ConfigureCamera(camera, false))
+ {
+ DALI_LOG_ERROR("Fail to generate %u's camera actor : Some property was not defined. Please check model file.\n", index);
+ camera.Reset();
+ return camera;
+ }
+
+ ApplyCameraTransform(camera);
+ }
+ return camera;
+}
+
+bool Model::ApplyCamera(uint32_t index, Dali::CameraActor camera) const
+{
+ if(camera && mCameraParameters.size() > index)
+ {
+ if(!mCameraParameters[index].ConfigureCamera(camera, false))
+ {
+ DALI_LOG_ERROR("Fail to apply %u's camera actor : Some property was not defined. Please check model file.\n", index);
+ return false;
+ }
+
+ ApplyCameraTransform(camera);
+ return true;
+ }
+ return false;
+}
+
///////////////////////////////////////////////////////////
//
// Private methods
{
if(!mModelLoadTask && !mModelRoot)
{
+ if(ModelCacheManager::Get())
+ {
+ ModelCacheManager::Get().ReferenceModelCache(mModelUrl);
+ }
+
Scene3D::Loader::InitializeGltfLoader();
mModelLoadTask = new ModelLoadTask(mModelUrl, mResourceDirectoryUrl, MakeCallback(this, &Model::OnModelLoadComplete));
Dali::AsyncTaskManager::Get().AddTask(mModelLoadTask);
}
uint32_t textureCount = textures.GetTextureCount();
// EnvMap requires at least 2 texture, diffuse and specular
- if(textureCount > 2u)
+ if(textureCount > 2u &&
+ (textures.GetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE) != currentDiffuseTexture ||
+ textures.GetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE) != currentSpecularTexture))
{
- textures.SetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE, currentDiffuseTexture);
- textures.SetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE, currentSpecularTexture);
+ Dali::TextureSet newTextures = Dali::TextureSet::New();
+
+ for(uint32_t index = 0u; index < textureCount; ++index)
+ {
+ Dali::Texture texture = textures.GetTexture(index);
+ if(index == textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE)
+ {
+ texture = currentDiffuseTexture;
+ }
+ else if(index == textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE)
+ {
+ texture = currentSpecularTexture;
+ }
+
+ newTextures.SetTexture(index, texture);
+ newTextures.SetSampler(index, textures.GetSampler(index));
+ }
+
+ renderer.SetTextures(newTextures);
}
}
renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIblScaleFactor);
}
}
+void Model::ApplyCameraTransform(Dali::CameraActor camera) const
+{
+ Vector3 selfPosition = Self().GetProperty<Vector3>(Actor::Property::POSITION);
+ Quaternion selfOrientation = Self().GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+ Vector3 selfScale = Self().GetProperty<Vector3>(Actor::Property::SCALE);
+
+ Vector3 cameraPosition = camera.GetProperty<Vector3>(Actor::Property::POSITION);
+ Quaternion cameraOrientation = camera.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+ Vector3 cameraScale = camera.GetProperty<Vector3>(Actor::Property::SCALE);
+
+ // Models in glTF and dli are defined as right hand coordinate system.
+ // DALi uses left hand coordinate system. Scaling negative is for change winding order.
+ if(!Dali::Equals(Y_DIRECTION.Dot(Vector3::YAXIS), 1.0f))
+ {
+ // Reflect by XZ plane
+ cameraPosition.y = -cameraPosition.y;
+ Quaternion yDirectionQuaternion;
+ yDirectionQuaternion.mVector = Vector3::YAXIS;
+ // Reflect orientation
+ cameraOrientation = yDirectionQuaternion * cameraOrientation * yDirectionQuaternion;
+ }
+
+ Vector3 resultPosition;
+ Quaternion resultOrientation;
+ Vector3 resultScale;
+
+ Matrix selfMatrix(false);
+ Matrix cameraMatrix(false);
+ Matrix resultMatrix(false);
+ selfMatrix.SetTransformComponents(selfScale, selfOrientation, selfPosition);
+ cameraMatrix.SetTransformComponents(cameraScale, cameraOrientation, cameraPosition);
+ Matrix::Multiply(resultMatrix, cameraMatrix, selfMatrix);
+ resultMatrix.GetTransformComponents(resultPosition, resultOrientation, resultScale);
+
+ camera.SetProperty(Actor::Property::POSITION, resultPosition);
+ camera.SetProperty(Actor::Property::ORIENTATION, resultOrientation);
+ camera.SetProperty(Actor::Property::SCALE, resultScale);
+}
+
void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
{
if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
if(!mModelLoadTask->HasSucceeded())
{
ResetResourceTasks();
+
+ if(ModelCacheManager::Get())
+ {
+ ModelCacheManager::Get().UnreferenceModelCache(mModelUrl);
+ }
+
return;
}
mRenderableActors.clear();
CollectRenderableActor(mModelRoot);
- auto* resources = &(mModelLoadTask->mResources);
- auto* scene = &(mModelLoadTask->mScene);
- CreateAnimations(*scene);
- if(!resources->mEnvironmentMaps.empty())
+ CreateAnimations(mModelLoadTask->mLoadResult.mScene);
+ ResetCameraParameters();
+
+ if(!mModelLoadTask->mLoadResult.mResources.mEnvironmentMaps.empty())
{
- mDefaultDiffuseTexture = resources->mEnvironmentMaps.front().second.mDiffuse;
- mDefaultSpecularTexture = resources->mEnvironmentMaps.front().second.mSpecular;
+ mDefaultDiffuseTexture = mModelLoadTask->mLoadResult.mResources.mEnvironmentMaps.front().second.mDiffuse;
+ mDefaultSpecularTexture = mModelLoadTask->mLoadResult.mResources.mEnvironmentMaps.front().second.mSpecular;
}
UpdateImageBasedLightTexture();
mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
BoundingVolume AABB;
- auto* resources = &(mModelLoadTask->mResources);
- auto* scene = &(mModelLoadTask->mScene);
Dali::Scene3D::Loader::Transforms xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
- Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{*resources, xforms, {}, {}, {}};
- uint32_t rootCount = 0u;
- for(auto iRoot : scene->GetRoots())
- {
- resources->GenerateResources(mModelLoadTask->mResourceRefCounts[rootCount]);
+ Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{mModelLoadTask->mLoadResult.mResources, xforms, {}, {}, {}};
+
+ // Generate Dali handles from resource bundle. Note that we generate all scene's resouce immediatly.
+ mModelLoadTask->mLoadResult.mResources.GenerateResources(mModelLoadTask->mResourceRefCount);
- if(auto actor = scene->CreateNodes(iRoot, mModelLoadTask->mResourceChoices, nodeParams))
+ for(auto iRoot : mModelLoadTask->mLoadResult.mScene.GetRoots())
+ {
+ if(auto actor = mModelLoadTask->mLoadResult.mScene.CreateNodes(iRoot, mModelLoadTask->mResourceChoices, nodeParams))
{
- scene->ConfigureSkeletonJoints(iRoot, resources->mSkeletons, actor);
- scene->ConfigureSkinningShaders(*resources, actor, std::move(nodeParams.mSkinnables));
- ConfigureBlendShapeShaders(*resources, *scene, actor, std::move(nodeParams.mBlendshapeRequests));
+ mModelLoadTask->mLoadResult.mScene.ConfigureSkeletonJoints(iRoot, mModelLoadTask->mLoadResult.mResources.mSkeletons, actor);
+ mModelLoadTask->mLoadResult.mScene.ConfigureSkinningShaders(mModelLoadTask->mLoadResult.mResources, actor, std::move(nodeParams.mSkinnables));
+ ConfigureBlendShapeShaders(mModelLoadTask->mLoadResult.mResources, mModelLoadTask->mLoadResult.mScene, actor, std::move(nodeParams.mBlendshapeRequests));
- scene->ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
+ mModelLoadTask->mLoadResult.mScene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
mModelRoot.Add(actor);
}
- AddModelTreeToAABB(AABB, *scene, mModelLoadTask->mResourceChoices, iRoot, nodeParams, Matrix::IDENTITY);
- rootCount++;
+ AddModelTreeToAABB(AABB, mModelLoadTask->mLoadResult.mScene, mModelLoadTask->mResourceChoices, iRoot, nodeParams, Matrix::IDENTITY);
}
mNaturalSize = AABB.CalculateSize();
void Model::CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene)
{
- if(!mModelLoadTask->mAnimations.empty())
+ mAnimations.clear();
+ if(!mModelLoadTask->mLoadResult.mAnimationDefinitions.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);
return mModelRoot.FindChildById(node->mNodeId);
};
- mAnimations.clear();
- for(auto&& animation : mModelLoadTask->mAnimations)
+ for(auto&& animation : mModelLoadTask->mLoadResult.mAnimationDefinitions)
{
Dali::Animation anim = animation.ReAnimate(getActor);
mAnimations.push_back({animation.mName, anim});
}
}
+void Model::ResetCameraParameters()
+{
+ mCameraParameters.clear();
+ if(!mModelLoadTask->mLoadResult.mCameraParameters.empty())
+ {
+ // Copy camera parameters.
+ std::copy(mModelLoadTask->mLoadResult.mCameraParameters.begin(), mModelLoadTask->mLoadResult.mCameraParameters.end(), std::back_inserter(mCameraParameters));
+ }
+}
+
} // namespace Internal
} // namespace Scene3D
} // namespace Dali
#define DALI_SCENE3D_INTERNAL_MODEL_H
/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// EXTERNAL INCLUDES
#include <dali-toolkit/public-api/controls/control-impl.h>
+#include <dali/public-api/actors/camera-actor.h>
#include <dali/public-api/actors/layer.h>
#include <dali/public-api/animation/animation.h>
#include <dali/public-api/object/weak-handle.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/loader/load-result.h>
namespace Dali
{
{
public:
using AnimationData = std::pair<std::string, Dali::Animation>;
+ using CameraData = Loader::CameraParameters;
/**
- * @brief Creates a new Model.
- *
- * @return A public handle to the newly allocated Model.
+ * @copydoc Model::New()
*/
static Dali::Scene3D::Model New(const std::string& modelUrl, const std::string& resourceDirectoryUrl);
*/
Dali::Animation GetAnimation(const std::string& name) const;
+ /**
+ * @copydoc Model::GetCameraCount()
+ */
+ uint32_t GetCameraCount() const;
+
+ /**
+ * @copydoc Model::GenerateCamera()
+ */
+ Dali::CameraActor GenerateCamera(uint32_t index) const;
+
+ /**
+ * @copydoc Model::ApplyCamera()
+ */
+ bool ApplyCamera(uint32_t index, Dali::CameraActor camera) const;
+
protected:
/**
* @brief Constructs a new Model.
+ * @param[in] modelUrl model file path.(e.g., glTF, and DLI).
+ * @param[in] resourceDirectoryUrl resource file path that includes binary, image etc.
*/
Model(const std::string& modelUrl, const std::string& resourceDirectoryUrl);
*/
bool IsResourceReady() const override;
+private:
/**
* @brief Scales the model to fit the control or to return to original size.
*/
*/
void UpdateImageBasedLightScaleFactor();
+ /**
+ * @brief Apply self transform into inputed camera.
+ * Inputed camera must be configured by CameraParameter. Mean, inputed camera coordinate depend on Model.
+ * After this API finished, CameraActor coordinate system converted as DALi coordinate system.
+ *
+ * @param[in,out] camera CameraActor who need to apply model itself's transform
+ */
+ void ApplyCameraTransform(Dali::CameraActor camera) const;
+
public: // Overrides ImageBasedLightObserver Methods.
/**
* @copydoc Dali::Scene3D::Internal::ImageBasedLightObserver::NotifyImageBasedLightTexture()
*/
void CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene);
+ /**
+ * @brief Reset CameraData from loaded CameraParameters.
+ */
+ void ResetCameraParameters();
+
private:
std::string mModelUrl;
std::string mResourceDirectoryUrl;
Dali::Actor mModelRoot;
std::vector<AnimationData> mAnimations;
+ std::vector<CameraData> mCameraParameters;
std::vector<WeakHandle<Actor>> mRenderableActors;
WeakHandle<Scene3D::SceneView> mParentSceneView;
${scene3d_internal_dir}/algorithm/path-finder-spfa.cpp
${scene3d_internal_dir}/algorithm/path-finder-spfa-double-way.cpp
${scene3d_internal_dir}/common/environment-map-load-task.cpp
+ ${scene3d_internal_dir}/common/model-cache-manager.cpp
${scene3d_internal_dir}/common/model-load-task.cpp
${scene3d_internal_dir}/controls/model/model-impl.cpp
${scene3d_internal_dir}/controls/scene-view/scene-view-impl.cpp
#ifndef DALI_SCENE3D_LOADER_GLTF2_ASSET_H_
#define DALI_SCENE3D_LOADER_GLTF2_ASSET_H_
/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
// INTERNAL INCLUDES
-#include "dali-scene3d/internal/loader/json-reader.h"
-#include "dali-scene3d/public-api/loader/index.h"
+#include <dali-scene3d/internal/loader/json-reader.h>
+#include <dali-scene3d/public-api/loader/index.h>
// EXTERNAL INCLUDES
+#include <dali/devel-api/common/map-wrapper.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <dali/public-api/math/quaternion.h>
+#include <dali/public-api/math/vector4.h>
#include <cstdint>
#include <memory>
-#include "dali/devel-api/common/map-wrapper.h"
-#include "dali/public-api/common/vector-wrapper.h"
-#include "dali/public-api/math/quaternion.h"
-#include "dali/public-api/math/vector4.h"
#define ENUM_STRING_MAPPING(t, x) \
{ \
namespace gltf2
{
-using Index = Dali::Scene3D::Loader::Index;
+using Index = Dali::Scene3D::Loader::Index;
+constexpr float UNDEFINED_FLOAT_VALUE = -1.0f; ///< Special marker for some non-negative only float values.
template<typename T>
class Ref
*/
struct MaterialIor
{
- float mIor = MAXFLOAT;
+ float mIor = UNDEFINED_FLOAT_VALUE;
};
/**
{
struct Perspective
{
- float mAspectRatio;
- float mYFov;
- float mZFar;
- float mZNear;
+ float mAspectRatio = UNDEFINED_FLOAT_VALUE;
+ float mYFov = UNDEFINED_FLOAT_VALUE;
+ float mZFar = UNDEFINED_FLOAT_VALUE;
+ float mZNear = UNDEFINED_FLOAT_VALUE;
//TODO: extras
//TODO: extensions
};
struct Orthographic
{
- float mXMag;
- float mYMag;
- float mZFar;
- float mZNear;
+ float mXMag = UNDEFINED_FLOAT_VALUE;
+ float mYMag = UNDEFINED_FLOAT_VALUE;
+ float mZFar = UNDEFINED_FLOAT_VALUE;
+ float mZNear = UNDEFINED_FLOAT_VALUE;
//TODO: extras
//TODO: extensions
};
/*
- * 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.
return GetImpl(*this).GetAnimation(name);
}
+uint32_t Model::GetCameraCount() const
+{
+ return GetImpl(*this).GetCameraCount();
+}
+
+Dali::CameraActor Model::GenerateCamera(uint32_t index) const
+{
+ return GetImpl(*this).GenerateCamera(index);
+}
+
+bool Model::ApplyCamera(uint32_t index, Dali::CameraActor camera) const
+{
+ return GetImpl(*this).ApplyCamera(index, camera);
+}
+
} // namespace Scene3D
} // namespace Dali
#define DALI_SCENE3D_MODEL_H
/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// EXTERNAL INCLUDES
#include <dali-toolkit/public-api/controls/control.h>
+#include <dali/public-api/actors/camera-actor.h>
#include <dali/public-api/common/dali-common.h>
#include <dali/public-api/rendering/texture.h>
*/
Dali::Animation GetAnimation(const std::string& name) const;
+ /**
+ * @brief Gets number of camera parameters those loaded from model file.
+ *
+ * @SINCE_2_2.15
+ * @return The number of loaded camera parameters.
+ * @note This method should be called after Model load finished.
+ */
+ uint32_t GetCameraCount() const;
+
+ /**
+ * @brief Generate camera actor using camera parameters at the index.
+ * If camera parameter is valid, create new CameraActor.
+ * Camera parameter decide at initialized time and
+ * didn't apply model node's current position (like Animation).
+ *
+ * @SINCE_2_2.15
+ * @param[in] index Index of camera to be used for generation camera.
+ * @return Generated CameraActor by the index, or empty Handle if generation failed.
+ * @note This method should be called after Model load finished.
+ */
+ Dali::CameraActor GenerateCamera(uint32_t index) const;
+
+ /**
+ * @brief Apply camera parameters at the index to inputed camera actor.
+ * If camera parameter is valid and camera actor is not empty, apply parameters.
+ * It will change camera's transform and near / far / fov or orthographic size / aspect ratio (if defined)
+ * Camera parameter decide at initialized time and
+ * didn't apply model node's current position (like Animation).
+ *
+ * @SINCE_2_2.15
+ * @param[in] index Index of camera to be used for generation camera.
+ * @param[in,out] camera Index of camera to be used for generation camera.
+ * @return True if apply successed. False otherwise.
+ * @note This method should be called after Model load finished.
+ */
+ bool ApplyCamera(uint32_t index, Dali::CameraActor camera) const;
+
public: // Not intended for application developers
/// @cond internal
/**
/*
- * 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.
* limitations under the License.
*
*/
-#include "dali-scene3d/public-api/loader/camera-parameters.h"
-#include "dali-scene3d/public-api/loader/utils.h"
-#include "dali/devel-api/actors/camera-actor-devel.h"
-#include "dali/integration-api/debug.h"
-#include "dali/public-api/actors/camera-actor.h"
-#include "dali/public-api/math/quaternion.h"
+
+// CLASS HEADER
+#include <dali-scene3d/public-api/loader/camera-parameters.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/actors/camera-actor-devel.h>
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/actors/camera-actor.h>
+#include <dali/public-api/math/quaternion.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/loader/gltf2-asset.h> // for gltf2::UNDEFINED_FLOAT_VALUE
+#include <dali-scene3d/public-api/loader/utils.h>
namespace Dali
{
ViewProjection CameraParameters::GetViewProjection() const
{
ViewProjection viewProjection;
+
// The projection matrix.
if(isPerspective)
{
Perspective(viewProjection.GetProjection(),
- Radian(Degree(yFov)),
+ Radian(yFovDegree),
1.f,
zNear,
zFar,
orientation *= viewQuaternion;
}
-void CameraParameters::ConfigureCamera(CameraActor& camera) const
+bool CameraParameters::ConfigureCamera(CameraActor& camera, bool invertY) const
{
- SetActorCentered(camera);
-
if(isPerspective)
{
+ if(Dali::Equals(zNear, gltf2::UNDEFINED_FLOAT_VALUE) ||
+ Dali::Equals(yFovDegree.degree, gltf2::UNDEFINED_FLOAT_VALUE))
+ {
+ return false;
+ }
+
camera.SetProjectionMode(Camera::PERSPECTIVE_PROJECTION);
camera.SetNearClippingPlane(zNear);
- camera.SetFarClippingPlane(zFar);
- camera.SetFieldOfView(Radian(Degree(yFov)));
+ camera.SetFieldOfView(Radian(yFovDegree));
+
+ if(!Dali::Equals(zFar, gltf2::UNDEFINED_FLOAT_VALUE))
+ {
+ camera.SetFarClippingPlane(zFar);
+ }
+ else
+ {
+ // TODO : Infinite perspective projection didn't support yet. Just set big enough value now
+ camera.SetFarClippingPlane(1000.0f);
+ }
+
+ if(!Dali::Equals(aspectRatio, gltf2::UNDEFINED_FLOAT_VALUE))
+ {
+ // TODO: By gltf 2.0 spec, we should not 'crop' and 'non-uniform scaling' by viewport.
+ // If we skip to setup this value, 'non-uniform scaling' problem fixed.
+ // But we need to resolve 'crop' case in future.
+ //camera.SetAspectRatio(aspectRatio);
+ }
}
else
{
+ if(Dali::Equals(zNear, gltf2::UNDEFINED_FLOAT_VALUE) ||
+ Dali::Equals(zFar, gltf2::UNDEFINED_FLOAT_VALUE) ||
+ Dali::Equals(orthographicSize, gltf2::UNDEFINED_FLOAT_VALUE))
+ {
+ return false;
+ }
+
camera.SetProjectionMode(Camera::ORTHOGRAPHIC_PROJECTION);
camera.SetNearClippingPlane(zNear);
camera.SetFarClippingPlane(zFar);
- camera.SetAspectRatio(aspectRatio);
camera.SetProperty(Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE, orthographicSize);
+
+ if(!Dali::Equals(aspectRatio, gltf2::UNDEFINED_FLOAT_VALUE))
+ {
+ // TODO: By gltf 2.0 spec, we should not 'crop' and 'non-uniform scaling' by viewport.
+ // If we skip to setup this value, 'non-uniform scaling' problem fixed.
+ // But we need to resolve 'crop' case in future.
+ //camera.SetAspectRatio(aspectRatio);
+ }
}
+ SetActorCentered(camera);
+
// model
Vector3 camTranslation;
Vector3 camScale;
Quaternion camOrientation;
CalculateTransformComponents(camTranslation, camOrientation, camScale);
- camera.SetInvertYAxis(true);
+ camera.SetInvertYAxis(invertY);
camera.SetProperty(Actor::Property::POSITION, camTranslation);
camera.SetProperty(Actor::Property::ORIENTATION, camOrientation);
camera.SetProperty(Actor::Property::SCALE, camScale);
+
+ return true;
}
} // namespace Loader
#ifndef DALI_SCENE3D_LOADER_CAMERA_PARAMETERS_H
#define DALI_SCENE3D_LOADER_CAMERA_PARAMETERS_H
/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
// INTERNAL INCLUDES
-#include "dali-scene3d/public-api/api.h"
-#include "dali-scene3d/public-api/loader/view-projection.h"
+#include <dali-scene3d/public-api/api.h>
+#include <dali-scene3d/public-api/loader/view-projection.h>
// EXTERNAL INCLUDES
-#include "dali/public-api/math/matrix.h"
-#include "dali/public-api/math/vector3.h"
+#include <dali/public-api/math/degree.h>
+#include <dali/public-api/math/matrix.h>
+#include <dali/public-api/math/vector3.h>
namespace Dali
{
{
struct DALI_SCENE3D_API CameraParameters
{
+ // TODO : Is these default value has is meaning?
Matrix matrix = Matrix::IDENTITY;
float orthographicSize = 1.f;
float aspectRatio = 1.f;
- float yFov = 60.f;
+ Degree yFovDegree = Degree(60.f);
float zNear = 0.1f;
float zFar = 1000.f;
bool isPerspective = true;
* @brief Configures the camera in the way that it is supposed to be used with
* scene3d scenes. This means inverted Y and a rotation of 180 degrees
* along the Y axis, plus whatever the parameters define.
+ *
+ * @return True if success to generate camera. False otherwise.
*/
- void ConfigureCamera(CameraActor& camera) const;
+ bool ConfigureCamera(CameraActor& camera, bool invertY = true) const;
};
} // namespace Loader
/*
- * 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.
namespace
{
-const char* NODES = "nodes";
-const char* SCENES = "scenes";
-const char* NODE = "node";
-const char* URI = "uri";
-const char* URL = "url";
-const char* HINTS = "hints";
+const char* NODES = "nodes";
+const char* SCENES = "scenes";
+const char* NODE = "node";
+const char* URI = "uri";
+const char* URL = "url";
+const char* HINTS = "hints";
const char* NAME("name");
const char* BLEND_SHAPE_HEADER("blendShapeHeader");
const char* BLEND_SHAPES("blendShapes");
{
auto& jsonCamera = (*i0).second;
- ReadFloat(jsonCamera.GetChild("fov"), iCamera->yFov);
+ ReadFloat(jsonCamera.GetChild("fov"), iCamera->yFovDegree.degree);
ReadFloat(jsonCamera.GetChild("near"), iCamera->zNear);
ReadFloat(jsonCamera.GetChild("far"), iCamera->zFar);
if(ReadVector(jsonCamera.GetChild("orthographic"), dummyFloatArray, 4u))
return result;\r
}\r
\r
-const auto BLEND_SHAPE_READER = std::move(js::Reader<BlendShape>()\r
- .Register(*js::MakeProperty("key", ReadBlendShapeKeys, &BlendShape::mKeys))\r
- .Register(*new js::Property<BlendShape, std::string_view>("name", js::Read::StringView, &BlendShape::mNodeName))\r
- .Register(*js::MakeProperty("morphtarget", js::Read::Number<uint32_t>, &BlendShape::mNumberOfMorphTarget))\r
- .Register(*new js::Property<BlendShape, std::string_view>("blendShapeVersion", js::Read::StringView, &BlendShape::mVersion))\r
- .Register(*new js::Property<BlendShape, std::string_view>("fullName", js::Read::StringView, &BlendShape::mFullName))\r
- .Register(*js::MakeProperty("morphname", js::Read::Array<std::string_view, js::Read::StringView>, &BlendShape::mMorphNames)));\r
-\r
-const auto FACIAL_ANIMATION_READER = std::move(js::Reader<FacialAnimation>()\r
- .Register(*new js::Property<FacialAnimation, std::string_view>("name", js::Read::StringView, &FacialAnimation::mName))\r
- .Register(*js::MakeProperty("blendShapes", js::Read::Array<BlendShape, js::ObjectReader<BlendShape>::Read>, &FacialAnimation::mBlendShapes))\r
- .Register(*new js::Property<FacialAnimation, std::string_view>("version", js::Read::StringView, &FacialAnimation::mVersion))\r
- .Register(*js::MakeProperty("shapesAmount", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfShapes))\r
- .Register(*js::MakeProperty("time", js::Read::Array<uint32_t, js::Read::Number>, &FacialAnimation::mTime))\r
- .Register(*js::MakeProperty("frames", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfFrames)));\r
+const js::Reader<BlendShape>& GetBlendShapeReader()\r
+{\r
+ static const auto BLEND_SHAPE_READER = std::move(js::Reader<BlendShape>()\r
+ .Register(*js::MakeProperty("key", ReadBlendShapeKeys, &BlendShape::mKeys))\r
+ .Register(*new js::Property<BlendShape, std::string_view>("name", js::Read::StringView, &BlendShape::mNodeName))\r
+ .Register(*js::MakeProperty("morphtarget", js::Read::Number<uint32_t>, &BlendShape::mNumberOfMorphTarget))\r
+ .Register(*new js::Property<BlendShape, std::string_view>("blendShapeVersion", js::Read::StringView, &BlendShape::mVersion))\r
+ .Register(*new js::Property<BlendShape, std::string_view>("fullName", js::Read::StringView, &BlendShape::mFullName))\r
+ .Register(*js::MakeProperty("morphname", js::Read::Array<std::string_view, js::Read::StringView>, &BlendShape::mMorphNames)));\r
+ return BLEND_SHAPE_READER;\r
+}\r
+\r
+const js::Reader<FacialAnimation>& GetFacialAnimationReader()\r
+{\r
+ static const auto FACIAL_ANIMATION_READER = std::move(js::Reader<FacialAnimation>()\r
+ .Register(*new js::Property<FacialAnimation, std::string_view>("name", js::Read::StringView, &FacialAnimation::mName))\r
+ .Register(*js::MakeProperty("blendShapes", js::Read::Array<BlendShape, js::ObjectReader<BlendShape>::Read>, &FacialAnimation::mBlendShapes))\r
+ .Register(*new js::Property<FacialAnimation, std::string_view>("version", js::Read::StringView, &FacialAnimation::mVersion))\r
+ .Register(*js::MakeProperty("shapesAmount", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfShapes))\r
+ .Register(*js::MakeProperty("time", js::Read::Array<uint32_t, js::Read::Number>, &FacialAnimation::mTime))\r
+ .Register(*js::MakeProperty("frames", js::Read::Number<uint32_t>, &FacialAnimation::mNumberOfFrames)));\r
+ return FACIAL_ANIMATION_READER;\r
+}\r
\r
} // unnamed namespace\r
\r
if(setObjectReaders)\r
{\r
// NOTE: only referencing own, anonymous namespace, const objects; the pointers will never need to change.\r
- js::SetObjectReader(BLEND_SHAPE_READER);\r
+ js::SetObjectReader(GetBlendShapeReader());\r
setObjectReaders = false;\r
}\r
\r
auto& rootObj = js::Cast<json_object_s>(*root);\r
\r
FacialAnimation facialAnimation;\r
- FACIAL_ANIMATION_READER.Read(rootObj, facialAnimation);\r
+ GetFacialAnimationReader().Read(rootObj, facialAnimation);\r
\r
AnimationDefinition animationDefinition;\r
animationDefinition.mName = std::string(facialAnimation.mName.data());\r
Dali::Mutex gInitializeMutex;
Dali::Mutex gReadMutex;
-const char* POSITION_PROPERTY("position");
-const char* ORIENTATION_PROPERTY("orientation");
-const char* SCALE_PROPERTY("scale");
-const char* BLEND_SHAPE_WEIGHTS_UNIFORM("uBlendShapeWeight");
-const char* MRENDERER_MODEL_IDENTIFICATION("M-Renderer");
-const char* ROOT_NODE_NAME("RootNode");
-const Vector3 SCALE_TO_ADJUST(100.0f, 100.0f, 100.0f);
+const char* POSITION_PROPERTY("position");
+const char* ORIENTATION_PROPERTY("orientation");
+const char* SCALE_PROPERTY("scale");
+const char* BLEND_SHAPE_WEIGHTS_UNIFORM("uBlendShapeWeight");
+const char* MRENDERER_MODEL_IDENTIFICATION("M-Renderer");
+const char* ROOT_NODE_NAME("RootNode");
+const Vector3 SCALE_TO_ADJUST(100.0f, 100.0f, 100.0f);
const Geometry::Type GLTF2_TO_DALI_PRIMITIVES[]{
Geometry::POINTS,
MeshDefinition::Blob::ApplyMinMax(acc.mMin, acc.mMax, acc.mCount, values);
}
-const auto BUFFER_READER = std::move(js::Reader<gt::Buffer>()
- .Register(*js::MakeProperty("byteLength", js::Read::Number<uint32_t>, >::Buffer::mByteLength))
- .Register(*js::MakeProperty("uri", js::Read::StringView, >::Buffer::mUri)));
+const js::Reader<gt::Buffer>& GetBufferReader()
+{
+ static const auto BUFFER_READER = std::move(js::Reader<gt::Buffer>()
+ .Register(*js::MakeProperty("byteLength", js::Read::Number<uint32_t>, >::Buffer::mByteLength))
+ .Register(*js::MakeProperty("uri", js::Read::StringView, >::Buffer::mUri)));
+ return BUFFER_READER;
+}
-const auto BUFFER_VIEW_READER = std::move(js::Reader<gt::BufferView>()
- .Register(*js::MakeProperty("buffer", gt::RefReader<gt::Document>::Read<gt::Buffer, >::Document::mBuffers>, >::BufferView::mBuffer))
- .Register(*js::MakeProperty("byteOffset", js::Read::Number<uint32_t>, >::BufferView::mByteOffset))
- .Register(*js::MakeProperty("byteLength", js::Read::Number<uint32_t>, >::BufferView::mByteLength))
- .Register(*js::MakeProperty("byteStride", js::Read::Number<uint32_t>, >::BufferView::mByteStride))
- .Register(*js::MakeProperty("target", js::Read::Number<uint32_t>, >::BufferView::mTarget)));
+const js::Reader<gt::BufferView>& GetBufferViewReader()
+{
+ static const auto BUFFER_VIEW_READER = std::move(js::Reader<gt::BufferView>()
+ .Register(*js::MakeProperty("buffer", gt::RefReader<gt::Document>::Read<gt::Buffer, >::Document::mBuffers>, >::BufferView::mBuffer))
+ .Register(*js::MakeProperty("byteOffset", js::Read::Number<uint32_t>, >::BufferView::mByteOffset))
+ .Register(*js::MakeProperty("byteLength", js::Read::Number<uint32_t>, >::BufferView::mByteLength))
+ .Register(*js::MakeProperty("byteStride", js::Read::Number<uint32_t>, >::BufferView::mByteStride))
+ .Register(*js::MakeProperty("target", js::Read::Number<uint32_t>, >::BufferView::mTarget)));
+ return BUFFER_VIEW_READER;
+}
-const auto BUFFER_VIEW_CLIENT_READER = std::move(js::Reader<gt::BufferViewClient>()
+const js::Reader<gt::BufferViewClient>& GetBufferViewClientReader()
+{
+ static const auto BUFFER_VIEW_CLIENT_READER = std::move(js::Reader<gt::BufferViewClient>()
.Register(*js::MakeProperty("bufferView", gt::RefReader<gt::Document>::Read<gt::BufferView, >::Document::mBufferViews>, >::BufferViewClient::mBufferView))
.Register(*js::MakeProperty("byteOffset", js::Read::Number<uint32_t>, >::BufferViewClient::mByteOffset)));
+ return BUFFER_VIEW_CLIENT_READER;
+}
-const auto COMPONENT_TYPED_BUFFER_VIEW_CLIENT_READER = std::move(js::Reader<gt::ComponentTypedBufferViewClient>()
+const js::Reader<gt::ComponentTypedBufferViewClient>& GetComponentTypedBufferViewClientReader()
+{
+ static const auto COMPONENT_TYPED_BUFFER_VIEW_CLIENT_READER = std::move(js::Reader<gt::ComponentTypedBufferViewClient>()
.Register(*new js::Property<gt::ComponentTypedBufferViewClient, gt::Ref<gt::BufferView>>("bufferView", gt::RefReader<gt::Document>::Read<gt::BufferView, >::Document::mBufferViews>, >::ComponentTypedBufferViewClient::mBufferView))
.Register(*new js::Property<gt::ComponentTypedBufferViewClient, uint32_t>("byteOffset", js::Read::Number<uint32_t>, >::ComponentTypedBufferViewClient::mByteOffset))
.Register(*js::MakeProperty("componentType", js::Read::Enum<gt::Component::Type>, >::ComponentTypedBufferViewClient::mComponentType)));
+ return COMPONENT_TYPED_BUFFER_VIEW_CLIENT_READER;
+}
-const auto ACCESSOR_SPARSE_READER = std::move(js::Reader<gt::Accessor::Sparse>()
+const js::Reader<gt::Accessor::Sparse>& GetAccessorSparseReader()
+{
+ static const auto ACCESSOR_SPARSE_READER = std::move(js::Reader<gt::Accessor::Sparse>()
.Register(*js::MakeProperty("count", js::Read::Number<uint32_t>, >::Accessor::Sparse::mCount))
.Register(*js::MakeProperty("indices", js::ObjectReader<gt::ComponentTypedBufferViewClient>::Read, >::Accessor::Sparse::mIndices))
.Register(*js::MakeProperty("values", js::ObjectReader<gt::BufferViewClient>::Read, >::Accessor::Sparse::mValues)));
+ return ACCESSOR_SPARSE_READER;
+}
-const auto ACCESSOR_READER = std::move(js::Reader<gt::Accessor>()
+const js::Reader<gt::Accessor>& GetAccessorReader()
+{
+ static const auto ACCESSOR_READER = std::move(js::Reader<gt::Accessor>()
.Register(*new js::Property<gt::Accessor, gt::Ref<gt::BufferView>>("bufferView",
gt::RefReader<gt::Document>::Read<gt::BufferView, >::Document::mBufferViews>,
>::Accessor::mBufferView))
.Register(*js::MakeProperty("min", js::Read::Array<float, js::Read::Number>, >::Accessor::mMin))
.Register(*js::MakeProperty("max", js::Read::Array<float, js::Read::Number>, >::Accessor::mMax))
.Register(*new js::Property<gt::Accessor, gt::Accessor::Sparse>("sparse", js::ObjectReader<gt::Accessor::Sparse>::Read, >::Accessor::SetSparse)));
+ return ACCESSOR_READER;
+}
-const auto IMAGE_READER = std::move(js::Reader<gt::Image>()
+const js::Reader<gt::Image>& GetImageReader()
+{
+ static const auto IMAGE_READER = std::move(js::Reader<gt::Image>()
.Register(*new js::Property<gt::Image, std::string_view>("name", js::Read::StringView, >::Material::mName))
.Register(*js::MakeProperty("uri", js::Read::StringView, >::Image::mUri))
.Register(*js::MakeProperty("mimeType", js::Read::StringView, >::Image::mMimeType))
.Register(*js::MakeProperty("bufferView", gt::RefReader<gt::Document>::Read<gt::BufferView, >::Document::mBufferViews>, >::Image::mBufferView)));
+ return IMAGE_READER;
+}
-const auto SAMPLER_READER = std::move(js::Reader<gt::Sampler>()
+const js::Reader<gt::Sampler>& GetSamplerReader()
+{
+ static const auto SAMPLER_READER = std::move(js::Reader<gt::Sampler>()
.Register(*js::MakeProperty("minFilter", js::Read::Enum<gt::Filter::Type>, >::Sampler::mMinFilter))
.Register(*js::MakeProperty("magFilter", js::Read::Enum<gt::Filter::Type>, >::Sampler::mMagFilter))
.Register(*js::MakeProperty("wrapS", js::Read::Enum<gt::Wrap::Type>, >::Sampler::mWrapS))
.Register(*js::MakeProperty("wrapT", js::Read::Enum<gt::Wrap::Type>, >::Sampler::mWrapT)));
+ return SAMPLER_READER;
+}
-const auto TEXURE_READER = std::move(js::Reader<gt::Texture>()
+const js::Reader<gt::Texture>& GetTextureReader()
+{
+ static const auto TEXURE_READER = std::move(js::Reader<gt::Texture>()
.Register(*js::MakeProperty("source", gt::RefReader<gt::Document>::Read<gt::Image, >::Document::mImages>, >::Texture::mSource))
.Register(*js::MakeProperty("sampler", gt::RefReader<gt::Document>::Read<gt::Sampler, >::Document::mSamplers>, >::Texture::mSampler)));
+ return TEXURE_READER;
+}
-const auto TEXURE_INFO_READER = std::move(js::Reader<gt::TextureInfo>()
+const js::Reader<gt::TextureInfo>& GetTextureInfoReader()
+{
+ static const auto TEXURE_INFO_READER = std::move(js::Reader<gt::TextureInfo>()
.Register(*js::MakeProperty("index", gt::RefReader<gt::Document>::Read<gt::Texture, >::Document::mTextures>, >::TextureInfo::mTexture))
.Register(*js::MakeProperty("texCoord", js::Read::Number<uint32_t>, >::TextureInfo::mTexCoord))
.Register(*js::MakeProperty("scale", js::Read::Number<float>, >::TextureInfo::mScale))
.Register(*js::MakeProperty("strength", js::Read::Number<float>, >::TextureInfo::mStrength)));
+ return TEXURE_INFO_READER;
+}
-const auto MATERIAL_PBR_READER = std::move(js::Reader<gt::Material::Pbr>()
+const js::Reader<gt::Material::Pbr>& GetMaterialPbrReader()
+{
+ static const auto MATERIAL_PBR_READER = std::move(js::Reader<gt::Material::Pbr>()
.Register(*js::MakeProperty("baseColorFactor", gt::ReadDaliVector<Vector4>, >::Material::Pbr::mBaseColorFactor))
.Register(*js::MakeProperty("baseColorTexture", js::ObjectReader<gt::TextureInfo>::Read, >::Material::Pbr::mBaseColorTexture))
.Register(*js::MakeProperty("metallicFactor", js::Read::Number<float>, >::Material::Pbr::mMetallicFactor))
.Register(*js::MakeProperty("roughnessFactor", js::Read::Number<float>, >::Material::Pbr::mRoughnessFactor))
.Register(*js::MakeProperty("metallicRoughnessTexture", js::ObjectReader<gt::TextureInfo>::Read, >::Material::Pbr::mMetallicRoughnessTexture)));
+ return MATERIAL_PBR_READER;
+}
-const auto MATERIAL_SPECULAR_READER = std::move(js::Reader<gt::MaterialSpecular>()
+const js::Reader<gt::MaterialSpecular>& GetMaterialSpecularReader()
+{
+ static const auto MATERIAL_SPECULAR_READER = std::move(js::Reader<gt::MaterialSpecular>()
.Register(*js::MakeProperty("specularFactor", js::Read::Number<float>, >::MaterialSpecular::mSpecularFactor))
.Register(*js::MakeProperty("specularTexture", js::ObjectReader<gt::TextureInfo>::Read, >::MaterialSpecular::mSpecularTexture))
.Register(*js::MakeProperty("specularColorFactor", gt::ReadDaliVector<Vector3>, >::MaterialSpecular::mSpecularColorFactor))
.Register(*js::MakeProperty("specularColorTexture", js::ObjectReader<gt::TextureInfo>::Read, >::MaterialSpecular::mSpecularColorTexture)));
+ return MATERIAL_SPECULAR_READER;
+}
-const auto MATERIAL_IOR_READER = std::move(js::Reader<gt::MaterialIor>()
+const js::Reader<gt::MaterialIor>& GetMaterialIorReader()
+{
+ static const auto MATERIAL_IOR_READER = std::move(js::Reader<gt::MaterialIor>()
.Register(*js::MakeProperty("ior", js::Read::Number<float>, >::MaterialIor::mIor)));
+ return MATERIAL_IOR_READER;
+}
-const auto MATERIAL_EXTENSION_READER = std::move(js::Reader<gt::MaterialExtensions>()
+const js::Reader<gt::MaterialExtensions>& GetMaterialExtensionsReader()
+{
+ static const auto MATERIAL_EXTENSION_READER = std::move(js::Reader<gt::MaterialExtensions>()
.Register(*js::MakeProperty("KHR_materials_ior", js::ObjectReader<gt::MaterialIor>::Read, >::MaterialExtensions::mMaterialIor))
.Register(*js::MakeProperty("KHR_materials_specular", js::ObjectReader<gt::MaterialSpecular>::Read, >::MaterialExtensions::mMaterialSpecular)));
+ return MATERIAL_EXTENSION_READER;
+}
-const auto MATERIAL_READER = std::move(js::Reader<gt::Material>()
+const js::Reader<gt::Material>& GetMaterialReader()
+{
+ static const auto MATERIAL_READER = std::move(js::Reader<gt::Material>()
.Register(*new js::Property<gt::Material, std::string_view>("name", js::Read::StringView, >::Material::mName))
.Register(*js::MakeProperty("pbrMetallicRoughness", js::ObjectReader<gt::Material::Pbr>::Read, >::Material::mPbrMetallicRoughness))
.Register(*js::MakeProperty("normalTexture", js::ObjectReader<gt::TextureInfo>::Read, >::Material::mNormalTexture))
.Register(*js::MakeProperty("alphaCutoff", js::Read::Number<float>, >::Material::mAlphaCutoff))
.Register(*js::MakeProperty("doubleSided", js::Read::Boolean, >::Material::mDoubleSided))
.Register(*js::MakeProperty("extensions", js::ObjectReader<gt::MaterialExtensions>::Read, >::Material::mMaterialExtensions)));
+ return MATERIAL_READER;
+}
std::map<gt::Attribute::Type, gt::Ref<gt::Accessor>> ReadMeshPrimitiveAttributes(const json_value_s& j)
{
return result;
}
-const auto MESH_PRIMITIVE_READER = std::move(js::Reader<gt::Mesh::Primitive>()
+const js::Reader<gt::Mesh::Primitive>& GetMeshPrimitiveReader()
+{
+ static const auto MESH_PRIMITIVE_READER = std::move(js::Reader<gt::Mesh::Primitive>()
.Register(*js::MakeProperty("attributes", ReadMeshPrimitiveAttributes, >::Mesh::Primitive::mAttributes))
.Register(*js::MakeProperty("indices", gt::RefReader<gt::Document>::Read<gt::Accessor, >::Document::mAccessors>, >::Mesh::Primitive::mIndices))
.Register(*js::MakeProperty("material", gt::RefReader<gt::Document>::Read<gt::Material, >::Document::mMaterials>, >::Mesh::Primitive::mMaterial))
.Register(*js::MakeProperty("mode", js::Read::Enum<gt::Mesh::Primitive::Mode>, >::Mesh::Primitive::mMode))
.Register(*js::MakeProperty("targets", ReadMeshPrimitiveTargets, >::Mesh::Primitive::mTargets)));
+ return MESH_PRIMITIVE_READER;
+}
-const auto MESH_READER = std::move(js::Reader<gt::Mesh>()
+const js::Reader<gt::Mesh>& GetMeshReader()
+{
+ static const auto MESH_READER = std::move(js::Reader<gt::Mesh>()
.Register(*new js::Property<gt::Mesh, std::string_view>("name", js::Read::StringView, >::Mesh::mName))
.Register(*js::MakeProperty("primitives",
js::Read::Array<gt::Mesh::Primitive, js::ObjectReader<gt::Mesh::Primitive>::Read>,
>::Mesh::mPrimitives))
.Register(*js::MakeProperty("weights", js::Read::Array<float, js::Read::Number>, >::Mesh::mWeights)));
+ return MESH_READER;
+}
-const auto SKIN_READER = std::move(js::Reader<gt::Skin>()
+const js::Reader<gt::Skin>& GetSkinReader()
+{
+ static const auto SKIN_READER = std::move(js::Reader<gt::Skin>()
.Register(*new js::Property<gt::Skin, std::string_view>("name", js::Read::StringView, >::Skin::mName))
.Register(*js::MakeProperty("inverseBindMatrices",
gt::RefReader<gt::Document>::Read<gt::Accessor, >::Document::mAccessors>,
.Register(*js::MakeProperty("joints",
js::Read::Array<gt::Ref<gt::Node>, gt::RefReader<gt::Document>::Read<gt::Node, >::Document::mNodes>>,
>::Skin::mJoints)));
+ return SKIN_READER;
+}
-const auto CAMERA_PERSPECTIVE_READER = std::move(js::Reader<gt::Camera::Perspective>()
+const js::Reader<gt::Camera::Perspective>& GetCameraPerspectiveReader()
+{
+ static const auto CAMERA_PERSPECTIVE_READER = std::move(js::Reader<gt::Camera::Perspective>()
.Register(*js::MakeProperty("aspectRatio", js::Read::Number<float>, >::Camera::Perspective::mAspectRatio))
.Register(*js::MakeProperty("yfov", js::Read::Number<float>, >::Camera::Perspective::mYFov))
.Register(*js::MakeProperty("zfar", js::Read::Number<float>, >::Camera::Perspective::mZFar))
.Register(*js::MakeProperty("znear", js::Read::Number<float>, >::Camera::Perspective::mZNear))); // TODO: infinite perspective projection, where znear is omitted
+ return CAMERA_PERSPECTIVE_READER;
+}
-const auto CAMERA_ORTHOGRAPHIC_READER = std::move(js::Reader<gt::Camera::Orthographic>()
+const js::Reader<gt::Camera::Orthographic>& GetCameraOrthographicReader()
+{
+ static const auto CAMERA_ORTHOGRAPHIC_READER = std::move(js::Reader<gt::Camera::Orthographic>()
.Register(*js::MakeProperty("xmag", js::Read::Number<float>, >::Camera::Orthographic::mXMag))
- .Register(*js::MakeProperty("ymag", js::Read::Number<float>, >::Camera::Orthographic::mXMag))
+ .Register(*js::MakeProperty("ymag", js::Read::Number<float>, >::Camera::Orthographic::mYMag))
.Register(*js::MakeProperty("zfar", js::Read::Number<float>, >::Camera::Orthographic::mZFar))
.Register(*js::MakeProperty("znear", js::Read::Number<float>, >::Camera::Orthographic::mZNear)));
+ return CAMERA_ORTHOGRAPHIC_READER;
+}
-const auto CAMERA_READER = std::move(js::Reader<gt::Camera>()
+const js::Reader<gt::Camera>& GetCameraReader()
+{
+ static const auto CAMERA_READER = std::move(js::Reader<gt::Camera>()
.Register(*new js::Property<gt::Camera, std::string_view>("name", js::Read::StringView, >::Camera::mName))
.Register(*js::MakeProperty("type", js::Read::StringView, >::Camera::mType))
.Register(*js::MakeProperty("perspective", js::ObjectReader<gt::Camera::Perspective>::Read, >::Camera::mPerspective))
.Register(*js::MakeProperty("orthographic", js::ObjectReader<gt::Camera::Orthographic>::Read, >::Camera::mOrthographic)));
+ return CAMERA_READER;
+}
-const auto NODE_READER = std::move(js::Reader<gt::Node>()
+const js::Reader<gt::Node>& GetNodeReader()
+{
+ static const auto NODE_READER = std::move(js::Reader<gt::Node>()
.Register(*new js::Property<gt::Node, std::string_view>("name", js::Read::StringView, >::Node::mName))
.Register(*js::MakeProperty("translation", gt::ReadDaliVector<Vector3>, >::Node::mTranslation))
.Register(*js::MakeProperty("rotation", gt::ReadQuaternion, >::Node::mRotation))
.Register(*js::MakeProperty("children", js::Read::Array<gt::Ref<gt::Node>, gt::RefReader<gt::Document>::Read<gt::Node, >::Document::mNodes>>, >::Node::mChildren))
.Register(*js::MakeProperty("mesh", gt::RefReader<gt::Document>::Read<gt::Mesh, >::Document::mMeshes>, >::Node::mMesh))
.Register(*js::MakeProperty("skin", gt::RefReader<gt::Document>::Read<gt::Skin, >::Document::mSkins>, >::Node::mSkin)));
+ return NODE_READER;
+}
-const auto ANIMATION_SAMPLER_READER = std::move(js::Reader<gt::Animation::Sampler>()
+const js::Reader<gt::Animation::Sampler>& GetAnimationSamplerReader()
+{
+ static const auto ANIMATION_SAMPLER_READER = std::move(js::Reader<gt::Animation::Sampler>()
.Register(*js::MakeProperty("input", gt::RefReader<gt::Document>::Read<gt::Accessor, >::Document::mAccessors>, >::Animation::Sampler::mInput))
.Register(*js::MakeProperty("output", gt::RefReader<gt::Document>::Read<gt::Accessor, >::Document::mAccessors>, >::Animation::Sampler::mOutput))
.Register(*js::MakeProperty("interpolation", gt::ReadStringEnum<gt::Animation::Sampler::Interpolation>, >::Animation::Sampler::mInterpolation)));
+ return ANIMATION_SAMPLER_READER;
+}
-const auto ANIMATION_TARGET_READER = std::move(js::Reader<gt::Animation::Channel::Target>()
+const js::Reader<gt::Animation::Channel::Target>& GetAnimationChannelTargetReader()
+{
+ static const auto ANIMATION_TARGET_READER = std::move(js::Reader<gt::Animation::Channel::Target>()
.Register(*js::MakeProperty("node", gt::RefReader<gt::Document>::Read<gt::Node, >::Document::mNodes>, >::Animation::Channel::Target::mNode))
.Register(*js::MakeProperty("path", gt::ReadStringEnum<gt::Animation::Channel::Target>, >::Animation::Channel::Target::mPath)));
+ return ANIMATION_TARGET_READER;
+}
-const auto ANIMATION_CHANNEL_READER = std::move(js::Reader<gt::Animation::Channel>()
+const js::Reader<gt::Animation::Channel>& GetAnimationChannelReader()
+{
+ static const auto ANIMATION_CHANNEL_READER = std::move(js::Reader<gt::Animation::Channel>()
.Register(*js::MakeProperty("target", js::ObjectReader<gt::Animation::Channel::Target>::Read, >::Animation::Channel::mTarget))
.Register(*js::MakeProperty("sampler", gt::RefReader<gt::Animation>::Read<gt::Animation::Sampler, >::Animation::mSamplers>, >::Animation::Channel::mSampler)));
+ return ANIMATION_CHANNEL_READER;
+}
-const auto ANIMATION_READER = std::move(js::Reader<gt::Animation>()
+const js::Reader<gt::Animation>& GetAnimationReader()
+{
+ static const auto ANIMATION_READER = std::move(js::Reader<gt::Animation>()
.Register(*new js::Property<gt::Animation, std::string_view>("name", js::Read::StringView, >::Animation::mName))
.Register(*js::MakeProperty("samplers",
js::Read::Array<gt::Animation::Sampler, js::ObjectReader<gt::Animation::Sampler>::Read>,
.Register(*js::MakeProperty("channels",
js::Read::Array<gt::Animation::Channel, js::ObjectReader<gt::Animation::Channel>::Read>,
>::Animation::mChannels)));
+ return ANIMATION_READER;
+}
-const auto SCENE_READER = std::move(js::Reader<gt::Scene>()
+const js::Reader<gt::Scene>& GetSceneReader()
+{
+ static const auto SCENE_READER = std::move(js::Reader<gt::Scene>()
.Register(*new js::Property<gt::Scene, std::string_view>("name", js::Read::StringView, >::Scene::mName))
.Register(*js::MakeProperty("nodes",
js::Read::Array<gt::Ref<gt::Node>, gt::RefReader<gt::Document>::Read<gt::Node, >::Document::mNodes>>,
>::Scene::mNodes)));
+ return SCENE_READER;
+}
-const auto DOCUMENT_READER = std::move(js::Reader<gt::Document>()
+const js::Reader<gt::Document>& GetDocumentReader()
+{
+ static const auto DOCUMENT_READER = std::move(js::Reader<gt::Document>()
.Register(*js::MakeProperty("buffers",
js::Read::Array<gt::Buffer, js::ObjectReader<gt::Buffer>::Read>,
>::Document::mBuffers))
js::Read::Array<gt::Scene, js::ObjectReader<gt::Scene>::Read>,
>::Document::mScenes))
.Register(*js::MakeProperty("scene", gt::RefReader<gt::Document>::Read<gt::Scene, >::Document::mScenes>, >::Document::mScene)));
+ return DOCUMENT_READER;
+}
struct NodeMapping
{
matDef.mEmissiveFactor = material.mEmissiveFactor;
}
- if(material.mMaterialExtensions.mMaterialIor.mIor < MAXFLOAT)
+ if(!Dali::Equals(material.mMaterialExtensions.mMaterialIor.mIor, gltf2::UNDEFINED_FLOAT_VALUE))
{
float ior = material.mMaterialExtensions.mMaterialIor.mIor;
matDef.mDielectricSpecular = powf((ior - 1.0f) / (ior + 1.0f), 2.0f);
if(camParams.isPerspective)
{
auto& perspective = camera.mPerspective;
- camParams.yFov = Degree(Radian(perspective.mYFov)).degree;
- camParams.zNear = perspective.mZNear;
- camParams.zFar = perspective.mZFar;
+ if(!Dali::Equals(perspective.mYFov, gltf2::UNDEFINED_FLOAT_VALUE))
+ {
+ camParams.yFovDegree = Degree(Radian(perspective.mYFov));
+ }
+ else
+ {
+ camParams.yFovDegree = Degree(gltf2::UNDEFINED_FLOAT_VALUE);
+ }
+ camParams.zNear = perspective.mZNear;
+ camParams.zFar = perspective.mZFar;
// TODO: yes, we seem to ignore aspectRatio in CameraParameters.
}
else
{
- auto& ortho = camera.mOrthographic;
- camParams.orthographicSize = ortho.mYMag * .5f;
- camParams.aspectRatio = ortho.mXMag / ortho.mYMag;
- camParams.zNear = ortho.mZNear;
- camParams.zFar = ortho.mZFar;
+ auto& ortho = camera.mOrthographic;
+ if(!Dali::Equals(ortho.mYMag, gltf2::UNDEFINED_FLOAT_VALUE) && !Dali::Equals(ortho.mXMag, gltf2::UNDEFINED_FLOAT_VALUE))
+ {
+ camParams.orthographicSize = ortho.mYMag * .5f;
+ camParams.aspectRatio = ortho.mXMag / ortho.mYMag;
+ }
+ else
+ {
+ camParams.orthographicSize = gltf2::UNDEFINED_FLOAT_VALUE;
+ camParams.aspectRatio = gltf2::UNDEFINED_FLOAT_VALUE;
+ }
+ camParams.zNear = ortho.mZNear;
+ camParams.zFar = ortho.mZFar;
}
}
void SetObjectReaders()
{
- js::SetObjectReader(BUFFER_READER);
- js::SetObjectReader(BUFFER_VIEW_READER);
- js::SetObjectReader(BUFFER_VIEW_CLIENT_READER);
- js::SetObjectReader(COMPONENT_TYPED_BUFFER_VIEW_CLIENT_READER);
- js::SetObjectReader(ACCESSOR_SPARSE_READER);
- js::SetObjectReader(ACCESSOR_READER);
- js::SetObjectReader(IMAGE_READER);
- js::SetObjectReader(SAMPLER_READER);
- js::SetObjectReader(TEXURE_READER);
- js::SetObjectReader(TEXURE_INFO_READER);
- js::SetObjectReader(MATERIAL_PBR_READER);
- js::SetObjectReader(MATERIAL_SPECULAR_READER);
- js::SetObjectReader(MATERIAL_IOR_READER);
- js::SetObjectReader(MATERIAL_EXTENSION_READER);
- js::SetObjectReader(MATERIAL_READER);
- js::SetObjectReader(MESH_PRIMITIVE_READER);
- js::SetObjectReader(MESH_READER);
- js::SetObjectReader(SKIN_READER);
- js::SetObjectReader(CAMERA_PERSPECTIVE_READER);
- js::SetObjectReader(CAMERA_ORTHOGRAPHIC_READER);
- js::SetObjectReader(CAMERA_READER);
- js::SetObjectReader(NODE_READER);
- js::SetObjectReader(ANIMATION_SAMPLER_READER);
- js::SetObjectReader(ANIMATION_TARGET_READER);
- js::SetObjectReader(ANIMATION_CHANNEL_READER);
- js::SetObjectReader(ANIMATION_READER);
- js::SetObjectReader(SCENE_READER);
+ js::SetObjectReader(GetBufferReader());
+ js::SetObjectReader(GetBufferViewReader());
+ js::SetObjectReader(GetBufferViewClientReader());
+ js::SetObjectReader(GetComponentTypedBufferViewClientReader());
+ js::SetObjectReader(GetAccessorSparseReader());
+ js::SetObjectReader(GetAccessorReader());
+ js::SetObjectReader(GetImageReader());
+ js::SetObjectReader(GetSamplerReader());
+ js::SetObjectReader(GetTextureReader());
+ js::SetObjectReader(GetTextureInfoReader());
+ js::SetObjectReader(GetMaterialPbrReader());
+ js::SetObjectReader(GetMaterialSpecularReader());
+ js::SetObjectReader(GetMaterialIorReader());
+ js::SetObjectReader(GetMaterialExtensionsReader());
+ js::SetObjectReader(GetMaterialReader());
+ js::SetObjectReader(GetMeshPrimitiveReader());
+ js::SetObjectReader(GetMeshReader());
+ js::SetObjectReader(GetSkinReader());
+ js::SetObjectReader(GetCameraPerspectiveReader());
+ js::SetObjectReader(GetCameraOrthographicReader());
+ js::SetObjectReader(GetCameraReader());
+ js::SetObjectReader(GetNodeReader());
+ js::SetObjectReader(GetAnimationSamplerReader());
+ js::SetObjectReader(GetAnimationChannelTargetReader());
+ js::SetObjectReader(GetAnimationChannelReader());
+ js::SetObjectReader(GetAnimationReader());
+ js::SetObjectReader(GetSceneReader());
}
void SetDefaultEnvironmentMap(const gt::Document& doc, ConversionContext& context)
{
Mutex::ScopedLock lock(gReadMutex);
gt::SetRefReaderObject(doc);
- DOCUMENT_READER.Read(rootObj, doc);
+ GetDocumentReader().Read(rootObj, doc);
}
auto path = url.substr(0, url.rfind('/') + 1);
ImageData::SamplingMode::Type mSamplingMode{ImageData::SamplingMode::BOX_THEN_LINEAR}; ///< The sampling mode used to resize the image.\r
};\r
\r
-const std::map<std::string_view, ImageData::SamplingMode::Type> SAMPLING_MODE_TYPES{\r
- ENUM_STRING_MAPPING(ImageData::SamplingMode, BOX),\r
- ENUM_STRING_MAPPING(ImageData::SamplingMode, NEAREST),\r
- ENUM_STRING_MAPPING(ImageData::SamplingMode, LINEAR),\r
- ENUM_STRING_MAPPING(ImageData::SamplingMode, BOX_THEN_NEAREST),\r
- ENUM_STRING_MAPPING(ImageData::SamplingMode, BOX_THEN_LINEAR),\r
- ENUM_STRING_MAPPING(ImageData::SamplingMode, NO_FILTER),\r
- ENUM_STRING_MAPPING(ImageData::SamplingMode, DONT_CARE),\r
-};\r
+const std::map<std::string_view, ImageData::SamplingMode::Type>& GetStringSamplingModeTable()\r
+{\r
+ static const std::map<std::string_view, ImageData::SamplingMode::Type> SAMPLING_MODE_TYPES{\r
+ ENUM_STRING_MAPPING(ImageData::SamplingMode, BOX),\r
+ ENUM_STRING_MAPPING(ImageData::SamplingMode, NEAREST),\r
+ ENUM_STRING_MAPPING(ImageData::SamplingMode, LINEAR),\r
+ ENUM_STRING_MAPPING(ImageData::SamplingMode, BOX_THEN_NEAREST),\r
+ ENUM_STRING_MAPPING(ImageData::SamplingMode, BOX_THEN_LINEAR),\r
+ ENUM_STRING_MAPPING(ImageData::SamplingMode, NO_FILTER),\r
+ ENUM_STRING_MAPPING(ImageData::SamplingMode, DONT_CARE),\r
+ };\r
+ return SAMPLING_MODE_TYPES;\r
+}\r
\r
-ENUM_TYPE_FROM_STRING(ImageData::SamplingMode, SAMPLING_MODE_TYPES)\r
+ENUM_TYPE_FROM_STRING(ImageData::SamplingMode, GetStringSamplingModeTable())\r
\r
struct MetaData\r
{\r
std::vector<ImageData> mImageData;\r
};\r
\r
-const auto IMAGE_METADATA_READER = std::move(js::Reader<ImageData>()\r
- .Register(*js::MakeProperty("uri", js::Read::String, &ImageData::mImageUri))\r
- .Register(*js::MakeProperty("minWidth", js::Read::Number, &ImageData::mMinWidth))\r
- .Register(*js::MakeProperty("minHeight", js::Read::Number, &ImageData::mMinHeight))\r
- .Register(*js::MakeProperty("samplingMode", gt::ReadStringEnum<ImageData::SamplingMode>, &ImageData::mSamplingMode)));\r
-\r
-const auto METADATA_READER = std::move(js::Reader<MetaData>()\r
- .Register(*js::MakeProperty("images", js::Read::Array<ImageData, js::ObjectReader<ImageData>::Read>, &MetaData::mImageData)));\r
+const js::Reader<ImageData>& GetImageMetaDataReader()\r
+{\r
+ static const auto IMAGE_METADATA_READER = std::move(js::Reader<ImageData>()\r
+ .Register(*js::MakeProperty("uri", js::Read::String, &ImageData::mImageUri))\r
+ .Register(*js::MakeProperty("minWidth", js::Read::Number, &ImageData::mMinWidth))\r
+ .Register(*js::MakeProperty("minHeight", js::Read::Number, &ImageData::mMinHeight))\r
+ .Register(*js::MakeProperty("samplingMode", gt::ReadStringEnum<ImageData::SamplingMode>, &ImageData::mSamplingMode)));\r
+ return IMAGE_METADATA_READER;\r
+}\r
\r
+const js::Reader<MetaData>& GetMetaDataReader()\r
+{\r
+ static const auto METADATA_READER = std::move(js::Reader<MetaData>()\r
+ .Register(*js::MakeProperty("images", js::Read::Array<ImageData, js::ObjectReader<ImageData>::Read>, &MetaData::mImageData)));\r
+ return METADATA_READER;\r
+}\r
} // namespace\r
\r
void LoadSceneMetadata(const std::string& url, SceneMetadata& sceneMetadata)\r
static bool setObjectReaders = true;\r
if(setObjectReaders)\r
{\r
- js::SetObjectReader(IMAGE_METADATA_READER);\r
+ js::SetObjectReader(GetImageMetaDataReader());\r
\r
setObjectReaders = false;\r
}\r
\r
MetaData metaData;\r
- METADATA_READER.Read(rootObj, metaData);\r
+ GetMetaDataReader().Read(rootObj, metaData);\r
\r
sceneMetadata.mImageMetadata.reserve(metaData.mImageData.size() + metaData.mImageData.size());\r
for(auto&& data : metaData.mImageData)\r
{
namespace
{
+template<bool use32BitIndices>
class IndexProvider
{
public:
+ using IndexType = typename std::conditional_t<use32BitIndices, uint32_t, uint16_t>;
IndexProvider(const uint16_t* indices)
: mData(reinterpret_cast<uintptr_t>(indices)),
mFunc(indices ? IncrementPointer : Increment)
{
}
- uint16_t operator()()
+ IndexType operator()()
{
return mFunc(mData);
}
private:
- static uint16_t Increment(uintptr_t& data)
+ static IndexType Increment(uintptr_t& data)
{
- return static_cast<uint16_t>(data++);
+ // mData was 'zero' at construct time. Just simply return counter start with 0.
+ return static_cast<IndexType>(data++);
}
- static uint16_t IncrementPointer(uintptr_t& data)
+ static IndexType IncrementPointer(uintptr_t& data)
{
- auto iPtr = reinterpret_cast<const uint16_t*>(data);
+ auto iPtr = reinterpret_cast<const IndexType*>(data);
auto result = *iPtr;
data = reinterpret_cast<uintptr_t>(++iPtr);
return result;
}
uintptr_t mData;
- uint16_t (*mFunc)(uintptr_t&);
+ IndexType (*mFunc)(uintptr_t&);
};
const char* QUAD("quad");
raw.mAttribs.push_back({"aJoints", Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
}
-void GenerateNormals(MeshDefinition::RawData& raw)
+template<bool use32BitsIndices, typename IndexProviderType = IndexProvider<use32BitsIndices>>
+bool GenerateNormals(MeshDefinition::RawData& raw)
{
+ using IndexType = typename IndexProviderType::IndexType;
+
+ // mIndicies size must be even if we use 32bit indices.
+ if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0)))
+ {
+ return false;
+ }
+
auto& attribs = raw.mAttribs;
DALI_ASSERT_DEBUG(attribs.size() > 0); // positions
- IndexProvider getIndex(raw.mIndices.data());
- const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size());
+ IndexProviderType getIndex(raw.mIndices.data());
+
+ const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t)));
auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
for(uint32_t i = 0; i < numIndices; i += 3)
{
- uint16_t indices[]{getIndex(), getIndex(), getIndex()};
- Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
+ IndexType indices[]{getIndex(), getIndex(), getIndex()};
+ Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
Vector3 a = pos[1] - pos[0];
Vector3 b = pos[2] - pos[0];
}
attribs.push_back({"aNormal", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer)});
+
+ return true;
}
-template<bool useVec3, bool hasUvs, typename T = std::conditional_t<useVec3, Vector3, Vector4>, typename = std::enable_if_t<(std::is_same<T, Vector3>::value || std::is_same<T, Vector4>::value)>>
+template<bool use32BitsIndices, bool useVec3, bool hasUvs, typename T = std::conditional_t<useVec3, Vector3, Vector4>, typename = std::enable_if_t<(std::is_same<T, Vector3>::value || std::is_same<T, Vector4>::value)>, typename IndexProviderType = IndexProvider<use32BitsIndices>>
bool GenerateTangents(MeshDefinition::RawData& raw)
{
+ using IndexType = typename IndexProviderType::IndexType;
+
+ // mIndicies size must be even if we use 32bit indices.
+ if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0)))
+ {
+ return false;
+ }
+
auto& attribs = raw.mAttribs;
// Required positions, normals, uvs (if we have). If not, skip generation
- if(attribs.size() < (2 + static_cast<size_t>(hasUvs)))
+ if(DALI_UNLIKELY(attribs.size() < (2 + static_cast<size_t>(hasUvs))))
{
return false;
}
if constexpr(hasUvs)
{
- IndexProvider getIndex(raw.mIndices.data());
- const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size());
+ IndexProviderType getIndex(raw.mIndices.data());
+
+ const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t)));
auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
auto* uvs = reinterpret_cast<const Vector2*>(attribs[2].mData.data());
for(uint32_t i = 0; i < numIndices; i += 3)
{
- uint16_t indices[]{getIndex(), getIndex(), getIndex()};
- Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
- Vector2 uv[]{uvs[indices[0]], uvs[indices[1]], uvs[indices[2]]};
+ IndexType indices[]{getIndex(), getIndex(), getIndex()};
+ Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
+ Vector2 uv[]{uvs[indices[0]], uvs[indices[1]], uvs[indices[2]]};
float x0 = pos[1].x - pos[0].x;
float y0 = pos[1].y - pos[0].y;
{
ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
}
-
- auto u16s = raw.mIndices.data();
- auto u32s = reinterpret_cast<uint32_t*>(raw.mIndices.data());
- auto end = u32s + indexCount;
- while(u32s != end)
- {
- *u16s = static_cast<uint16_t>(*u32s);
- ++u16s;
- ++u32s;
- }
-
- raw.mIndices.resize(indexCount);
}
else if(MaskMatch(mFlags, U8_INDICES))
{
mIndices.mBlob.mStride >= sizeof(uint8_t)) &&
"Index buffer length not a multiple of element size");
const auto indexCount = mIndices.mBlob.GetBufferSize() / sizeof(uint8_t);
- raw.mIndices.resize(indexCount); // NOTE: we need space for uint32_ts initially.
+ raw.mIndices.resize(indexCount); // NOTE: we need space for uint16_ts initially.
std::string path;
auto u8s = reinterpret_cast<uint8_t*>(raw.mIndices.data()) + indexCount;
else if(mNormals.mBlob.mLength != 0 && isTriangles)
{
DALI_ASSERT_DEBUG(mNormals.mBlob.mLength == mPositions.mBlob.GetBufferSize());
- GenerateNormals(raw);
- hasNormals = true;
+ static const std::function<bool(RawData&)> GenerateNormalsFunction[2] =
+ {
+ GenerateNormals<false>,
+ GenerateNormals<true>,
+ };
+ const bool generateSuccessed = GenerateNormalsFunction[MaskMatch(mFlags, U32_INDICES)](raw);
+ if(!generateSuccessed)
+ {
+ DALI_LOG_ERROR("Failed to generate normal\n");
+ }
+ else
+ {
+ hasNormals = true;
+ }
}
const auto hasUvs = mTexCoords.IsDefined();
}
}
- mTexCoords.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector2)), reinterpret_cast<float*>(buffer.data()));
+ mTexCoords.mBlob.ApplyMinMax(static_cast<uint32_t>(uvCount), reinterpret_cast<float*>(buffer.data()));
raw.mAttribs.push_back({"aTexCoord", Property::VECTOR2, static_cast<uint32_t>(uvCount), std::move(buffer)});
}
else if(mTangents.mBlob.mLength != 0 && hasNormals && isTriangles)
{
DALI_ASSERT_DEBUG(mTangents.mBlob.mLength == mNormals.mBlob.GetBufferSize());
- static const std::function<bool(RawData&)> GenerateTangentsFunction[2][2] =
+ static const std::function<bool(RawData&)> GenerateTangentsFunction[2][2][2] =
{
{
- GenerateTangents<false, false>,
- GenerateTangents<false, true>,
+ {
+ GenerateTangents<false, false, false>,
+ GenerateTangents<false, false, true>,
+ },
+ {
+ GenerateTangents<false, true, false>,
+ GenerateTangents<false, true, true>,
+ },
},
{
- GenerateTangents<true, false>,
- GenerateTangents<true, true>,
- },
- };
- const bool generateSuccessed = GenerateTangentsFunction[mTangentType == Property::VECTOR3][hasUvs](raw);
+ {
+ GenerateTangents<true, false, false>,
+ GenerateTangents<true, false, true>,
+ },
+ {
+ GenerateTangents<true, true, false>,
+ GenerateTangents<true, true, true>,
+ },
+ }};
+ const bool generateSuccessed = GenerateTangentsFunction[MaskMatch(mFlags, U32_INDICES)][mTangentType == Property::VECTOR3][hasUvs](raw);
if(!generateSuccessed)
{
DALI_LOG_ERROR("Failed to generate tangents\n");
{
if(!raw.mIndices.empty())
{
- meshGeometry.geometry.SetIndexBuffer(raw.mIndices.data(), raw.mIndices.size());
+ if(MaskMatch(mFlags, U32_INDICES))
+ {
+ // TODO : We can only store indeces as uint16_type. Send Dali::Geometry that we use it as uint32_t actual.
+ meshGeometry.geometry.SetIndexBuffer(reinterpret_cast<const uint32_t*>(raw.mIndices.data()), raw.mIndices.size() / 2);
+ }
+ else
+ {
+ meshGeometry.geometry.SetIndexBuffer(raw.mIndices.data(), raw.mIndices.size());
+ }
}
for(auto& a : raw.mAttribs)
/*
- * 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.
*/
// FILE HEADER
-#include "dali-scene3d/public-api/loader/resource-bundle.h"
+#include <dali-scene3d/public-api/loader/resource-bundle.h>
// EXTERNAL
+#include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
+#include <dali/public-api/rendering/sampler.h>
#include <cstring>
#include <fstream>
#include <istream>
-#include "dali-toolkit/public-api/image-loader/sync-image-loader.h"
-#include "dali/public-api/rendering/sampler.h"
namespace Dali
{
return RESOURCE_TYPE_NAMES[static_cast<int>(type)];
}
+ResourceBundle::ResourceBundle()
+: mRawResourcesLoading(false),
+ mResourcesGenerating(false),
+ mRawResourcesLoaded(false),
+ mResourcesGenerated(false)
+{
+};
+
ResourceRefCounts ResourceBundle::CreateRefCounter() const
{
ResourceRefCounts refCounts(4);
void ResourceBundle::LoadResources(const ResourceRefCounts& refCounts, PathProvider pathProvider, Options::Type options)
{
+ mRawResourcesLoading = true;
+ mResourcesGenerating = true;
+
const auto kForceLoad = MaskMatch(options, Options::ForceReload);
const auto kKeepUnused = MaskMatch(options, Options::KeepUnused);
iMaterial.second = TextureSet();
}
}
+
+ mRawResourcesLoading = false;
+ mResourcesGenerating = false;
+
+ mRawResourcesLoaded = true;
+ mResourcesGenerated = true;
}
void ResourceBundle::LoadRawResources(const ResourceRefCounts& refCounts, PathProvider pathProvider, Options::Type options)
{
- const auto kForceLoad = MaskMatch(options, Options::ForceReload);
+ const auto kForceLoad = MaskMatch(options, Options::ForceReload);
- const auto& refCountEnvMaps = refCounts[ResourceType::Environment];
- auto environmentsPath = pathProvider(ResourceType::Environment);
- for(uint32_t i = 0, iEnd = refCountEnvMaps.Size(); i != iEnd; ++i)
+ if(kForceLoad || (!mRawResourcesLoaded && !mRawResourcesLoading))
{
- auto refCount = refCountEnvMaps[i];
- auto& iEnvMap = mEnvironmentMaps[i];
- if(refCount > 0 && (kForceLoad || !iEnvMap.second.IsLoaded()))
+ mRawResourcesLoading = true;
+
+ const auto& refCountEnvMaps = refCounts[ResourceType::Environment];
+ auto environmentsPath = pathProvider(ResourceType::Environment);
+ for(uint32_t i = 0, iEnd = refCountEnvMaps.Size(); i != iEnd; ++i)
{
- iEnvMap.first.mRawData = std::make_shared<EnvironmentDefinition::RawData>(iEnvMap.first.LoadRaw(environmentsPath));
+ auto refCount = refCountEnvMaps[i];
+ auto& iEnvMap = mEnvironmentMaps[i];
+ if(refCount > 0 && (kForceLoad || (!iEnvMap.first.mRawData && !iEnvMap.second.IsLoaded())))
+ {
+ iEnvMap.first.mRawData = std::make_shared<EnvironmentDefinition::RawData>(iEnvMap.first.LoadRaw(environmentsPath));
+ }
}
- }
- const auto& refCountShaders = refCounts[ResourceType::Shader];
- auto shadersPath = pathProvider(ResourceType::Shader);
- for(uint32_t i = 0, iEnd = refCountShaders.Size(); i != iEnd; ++i)
- {
- auto refCount = refCountShaders[i];
- auto& iShader = mShaders[i];
- if(refCount > 0 && (kForceLoad || !iShader.second))
+ const auto& refCountShaders = refCounts[ResourceType::Shader];
+ auto shadersPath = pathProvider(ResourceType::Shader);
+ for(uint32_t i = 0, iEnd = refCountShaders.Size(); i != iEnd; ++i)
{
- iShader.first.mRawData = std::make_shared<ShaderDefinition::RawData>(iShader.first.LoadRaw(shadersPath));
+ auto refCount = refCountShaders[i];
+ auto& iShader = mShaders[i];
+ if(refCount > 0 && (kForceLoad || !iShader.second))
+ {
+ iShader.first.mRawData = std::make_shared<ShaderDefinition::RawData>(iShader.first.LoadRaw(shadersPath));
+ }
}
- }
- const auto& refCountMeshes = refCounts[ResourceType::Mesh];
- auto modelsPath = pathProvider(ResourceType::Mesh);
- for(uint32_t i = 0, iEnd = refCountMeshes.Size(); i != iEnd; ++i)
- {
- auto refCount = refCountMeshes[i];
- auto& iMesh = mMeshes[i];
- if(refCount > 0 && (kForceLoad || !iMesh.second.geometry))
+ const auto& refCountMeshes = refCounts[ResourceType::Mesh];
+ auto modelsPath = pathProvider(ResourceType::Mesh);
+ for(uint32_t i = 0, iEnd = refCountMeshes.Size(); i != iEnd; ++i)
{
- iMesh.first.mRawData = std::make_shared<MeshDefinition::RawData>(iMesh.first.LoadRaw(modelsPath, mBuffers));
+ auto refCount = refCountMeshes[i];
+ auto& iMesh = mMeshes[i];
+ if(refCount > 0 && (kForceLoad || (!iMesh.first.mRawData && !iMesh.second.geometry)))
+ {
+ iMesh.first.mRawData = std::make_shared<MeshDefinition::RawData>(iMesh.first.LoadRaw(modelsPath, mBuffers));
+ }
}
- }
- const auto& refCountMaterials = refCounts[ResourceType::Material];
- auto imagesPath = pathProvider(ResourceType::Material);
- for(uint32_t i = 0, iEnd = refCountMaterials.Size(); i != iEnd; ++i)
- {
- auto refCount = refCountMaterials[i];
- auto& iMaterial = mMaterials[i];
- if(refCount > 0 && (kForceLoad || !iMaterial.second))
+ const auto& refCountMaterials = refCounts[ResourceType::Material];
+ auto imagesPath = pathProvider(ResourceType::Material);
+ for(uint32_t i = 0, iEnd = refCountMaterials.Size(); i != iEnd; ++i)
{
- iMaterial.first.mRawData = std::make_shared<MaterialDefinition::RawData>(iMaterial.first.LoadRaw(imagesPath));
+ auto refCount = refCountMaterials[i];
+ auto& iMaterial = mMaterials[i];
+ if(refCount > 0 && (kForceLoad || (!iMaterial.first.mRawData && !iMaterial.second)))
+ {
+ iMaterial.first.mRawData = std::make_shared<MaterialDefinition::RawData>(iMaterial.first.LoadRaw(imagesPath));
+ }
}
+
+ mRawResourcesLoading = false;
+ mRawResourcesLoaded = true;
}
}
void ResourceBundle::GenerateResources(const ResourceRefCounts& refCounts, Options::Type options)
{
- const auto& refCountEnvMaps = refCounts[ResourceType::Environment];
- for(uint32_t i = 0, iEnd = refCountEnvMaps.Size(); i != iEnd; ++i)
- {
- auto& iEnvMap = mEnvironmentMaps[i];
- if(iEnvMap.first.mRawData)
- {
- iEnvMap.second = iEnvMap.first.Load(std::move(*(iEnvMap.first.mRawData)));
- }
- else
- {
- iEnvMap.second.mDiffuse = Texture();
- iEnvMap.second.mSpecular = Texture();
- }
- }
+ const auto kForceLoad = MaskMatch(options, Options::ForceReload);
- const auto& refCountShaders = refCounts[ResourceType::Shader];
- for(uint32_t i = 0, iEnd = refCountShaders.Size(); i != iEnd; ++i)
+ if(mRawResourcesLoaded)
{
- auto& iShader = mShaders[i];
- if(iShader.first.mRawData)
+ if(kForceLoad || (!mResourcesGenerated && !mResourcesGenerating))
{
- iShader.second = iShader.first.Load(std::move(*(iShader.first.mRawData)));
- }
- else
- {
- iShader.second = Shader();
- }
- }
+ mResourcesGenerating = true;
- const auto& refCountMeshes = refCounts[ResourceType::Mesh];
- for(uint32_t i = 0, iEnd = refCountMeshes.Size(); i != iEnd; ++i)
- {
- auto& iMesh = mMeshes[i];
- if(iMesh.first.mRawData)
- {
- iMesh.second = iMesh.first.Load(std::move(*(iMesh.first.mRawData)));
- }
- else
- {
- iMesh.second.geometry = Geometry();
- }
- }
+ const auto& refCountEnvMaps = refCounts[ResourceType::Environment];
+ for(uint32_t i = 0, iEnd = refCountEnvMaps.Size(); i != iEnd; ++i)
+ {
+ auto refCount = refCountEnvMaps[i];
+ auto& iEnvMap = mEnvironmentMaps[i];
+ if(refCount > 0 && (kForceLoad || !iEnvMap.second.IsLoaded()))
+ {
+ if(iEnvMap.first.mRawData)
+ {
+ iEnvMap.second = iEnvMap.first.Load(std::move(*(iEnvMap.first.mRawData)));
+ }
+ else
+ {
+ iEnvMap.second.mDiffuse = Texture();
+ iEnvMap.second.mSpecular = Texture();
+ }
+ }
+ }
- const auto& refCountMaterials = refCounts[ResourceType::Material];
- for(uint32_t i = 0, iEnd = refCountMaterials.Size(); i != iEnd; ++i)
- {
- auto& iMaterial = mMaterials[i];
- if(iMaterial.first.mRawData)
- {
- iMaterial.second = iMaterial.first.Load(mEnvironmentMaps, std::move(*(iMaterial.first.mRawData)));
+ const auto& refCountShaders = refCounts[ResourceType::Shader];
+ for(uint32_t i = 0, iEnd = refCountShaders.Size(); i != iEnd; ++i)
+ {
+ auto refCount = refCountShaders[i];
+ auto& iShader = mShaders[i];
+ if(refCount > 0 && (kForceLoad || !iShader.second))
+ {
+ if(iShader.first.mRawData)
+ {
+ iShader.second = iShader.first.Load(std::move(*(iShader.first.mRawData)));
+ }
+ else
+ {
+ iShader.second = Shader();
+ }
+ }
+ }
+
+ const auto& refCountMeshes = refCounts[ResourceType::Mesh];
+ for(uint32_t i = 0, iEnd = refCountMeshes.Size(); i != iEnd; ++i)
+ {
+ auto refCount = refCountMeshes[i];
+ auto& iMesh = mMeshes[i];
+ if(refCount > 0 && (kForceLoad || !iMesh.second.geometry))
+ {
+ if(iMesh.first.mRawData)
+ {
+ iMesh.second = iMesh.first.Load(std::move(*(iMesh.first.mRawData)));
+ }
+ else
+ {
+ iMesh.second.geometry = Geometry();
+ }
+ }
+ }
+
+ const auto& refCountMaterials = refCounts[ResourceType::Material];
+ for(uint32_t i = 0, iEnd = refCountMaterials.Size(); i != iEnd; ++i)
+ {
+ auto refCount = refCountMaterials[i];
+ auto& iMaterial = mMaterials[i];
+ if(refCount > 0 && (kForceLoad || !iMaterial.second))
+ {
+ if(iMaterial.first.mRawData)
+ {
+ iMaterial.second = iMaterial.first.Load(mEnvironmentMaps, std::move(*(iMaterial.first.mRawData)));
+ }
+ else
+ {
+ iMaterial.second = TextureSet();
+ }
+ }
+ }
+
+ mResourcesGenerating = false;
+ mResourcesGenerated = true;
}
- else
+ else if(mResourcesGenerated && !mResourcesGenerating)
{
- iMaterial.second = TextureSet();
+ mResourcesGenerating = true;
+
+ const auto& refCountShaders = refCounts[ResourceType::Shader];
+ for(uint32_t i = 0, iEnd = refCountShaders.Size(); i != iEnd; ++i)
+ {
+ auto refCount = refCountShaders[i];
+ auto& iShader = mShaders[i];
+
+ // Always regenerating the Shader objects as they can't be shared between multiple models.
+ if(refCount > 0 || kForceLoad)
+ {
+ if(iShader.first.mRawData)
+ {
+ iShader.second = iShader.first.Load(std::move(*(iShader.first.mRawData)));
+ }
+ else
+ {
+ iShader.second = Shader();
+ }
+ }
+ }
+
+ mResourcesGenerating = false;
}
}
}
#ifndef DALI_SCENE3D_LOADERERERERER_RESOURCE_BUNDLE_H_
#define DALI_SCENE3D_LOADERERERERER_RESOURCE_BUNDLE_H_
/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
// INTERNAL
+#include <dali-scene3d/public-api/loader/buffer-definition.h>
#include <dali-scene3d/public-api/loader/environment-definition.h>
#include <dali-scene3d/public-api/loader/material-definition.h>
#include <dali-scene3d/public-api/loader/mesh-definition.h>
#include <dali-scene3d/public-api/loader/shader-definition.h>
#include <dali-scene3d/public-api/loader/skeleton-definition.h>
-#include <dali-scene3d/public-api/loader/buffer-definition.h>
// EXTERNAL
-#include <functional>
-#include <memory>
#include <dali/public-api/common/vector-wrapper.h>
#include <dali/public-api/rendering/shader.h>
#include <dali/public-api/rendering/texture-set.h>
+#include <functional>
+#include <memory>
namespace Dali
{
using PathProvider = std::function<std::string(ResourceType::Value)>;
- ResourceBundle() = default;
+ ResourceBundle();
ResourceBundle(const ResourceBundle&) = delete;
ResourceBundle& operator=(const ResourceBundle&) = delete;
SkeletonDefinition::Vector mSkeletons;
BufferDefinition::Vector mBuffers;
+
+ bool mRawResourcesLoading;
+ bool mResourcesGenerating;
+
+ bool mRawResourcesLoaded;
+ bool mResourcesGenerated;
};
} // namespace Loader
Vector<ScriptRun>::ConstIterator scriptRunEndIt = scripts.End();
bool isNewParagraphCharacter = false;
- FontId previousEmojiFontId = 0u;
- TextAbstraction::Script previousScript = TextAbstraction::UNKNOWN;
+ FontId previousEmojiFontId = 0u;
+ FontId currentFontId = 0u;
+ FontId previousFontId = 0u;
+ TextAbstraction::Script previousScript = TextAbstraction::UNKNOWN;
CharacterIndex lastCharacter = startIndex + numberOfCharacters - 1u;
for(Length index = startIndex; index <= lastCharacter; ++index)
// Get the font for the current character.
FontId fontId = fontClient.GetFontId(currentFontDescription, currentFontPointSize);
+ currentFontId = fontId;
// Get the script for the current character.
Script script = GetScript(index,
}
}
+ if(TextAbstraction::IsSpace(character) &&
+ TextAbstraction::HasLigatureMustBreak(script) &&
+ isValidCachedDefaultFont &&
+ (isDefaultFont || (currentFontId == previousFontId)))
+ {
+ fontId = cachedDefaultFontId;
+ isValidFont = true;
+ }
+
// If the given font is not valid, it means either:
// - there is no cached font for the current script yet or,
// - the user has set a different font than the default one for the current script or,
// Whether the current character is a new paragraph character.
isNewParagraphCharacter = TextAbstraction::IsNewParagraph(character);
previousScript = script;
+ previousFontId = currentFontId;
} // end traverse characters.
if(0u != currentFontRun.characterRun.numberOfCharacters)
{
const unsigned int TOOLKIT_MAJOR_VERSION = 2;
const unsigned int TOOLKIT_MINOR_VERSION = 2;
-const unsigned int TOOLKIT_MICRO_VERSION = 15;
+const unsigned int TOOLKIT_MICRO_VERSION = 16;
const char* const TOOLKIT_BUILD_DATE = __DATE__ " " __TIME__;
#ifdef DEBUG_ENABLED
Name: dali2-toolkit
Summary: Dali 3D engine Toolkit
-Version: 2.2.15
+Version: 2.2.16
Release: 1
Group: System/Libraries
License: Apache-2.0 and BSD-3-Clause and MIT