+# Common gitignore
.cproject
.project
.settings
.directory
-.idea/
Makefile.in
Makefile
+BROWSE
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
-dali.node
-dali.doxy
install_manifest.txt
-libdali2-toolkit.so*
*~
+*.pc
*.o
*.o.d
-*.pc
*.lo
*.loT
*.la
.deps
.libs
*.swp
+*.creator
+*.creator.user
/docs/generated/*
/build/tizen/doc
/build/tizen/.cov
-build/tizen/CMakeDoxyfile.in
-build/tizen/CMakeDoxygenDefaults.cmake
/build/desktop
/packaging/home*
+compile_commands.json
+.clangd
+/debugfiles.list
+/debuglinks.list
+/debugsources.list
+.clangd/
+*.vscode/
+*.cache/
+# dali-toolkit only
+.idea/
+dali.node
+dali.doxy
+libdali2-toolkit.so*
+build/tizen/CMakeDoxyfile.in
+build/tizen/CMakeDoxygenDefaults.cmake
.vscode/
core
-.clangd/
-compile_commands.json
dali-toolkit/internal/graphics/generated/*
dali-toolkit/internal/graphics/builtin-shader-extern-gen.h
dali-scene3d/internal/graphics/generated/*
utc-Dali-SceneView.cpp
utc-Dali-MatrixStack.cpp
utc-Dali-MeshDefinition.cpp
+ utc-Dali-NavigationMesh.cpp
utc-Dali-NodeDefinition.cpp
+ utc-Dali-PathFinding.cpp
utc-Dali-RendererState.cpp
utc-Dali-ResourceBundle.cpp
utc-Dali-SceneDefinition.cpp
--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "dali-scene3d/public-api/algorithm/navigation-mesh.h"
+#include "dali-scene3d/public-api/loader/navigation-mesh-factory.h"
+#include <dali-test-suite-utils.h>
+
+using namespace Dali;
+using namespace Dali::Scene3D::Algorithm;
+using namespace Dali::Scene3D::Loader;
+
+int UtcDaliNavigationMeshCreateFromFileFail1(void)
+{
+ tet_infoline("UtcDaliNavigationMeshCreateFromFileFail1: Fails to create navigation mesh from file");
+
+ // No such file, misspelled name
+ auto result = NavigationMeshFactory::CreateFromFile("notexisting.bin");
+
+ DALI_TEST_CHECK(result == nullptr);
+
+ END_TEST;
+}
+
+int UtcDaliNavigationMeshCreateFromFileOk1(void)
+{
+ tet_infoline("UtcDaliNavigationMeshCreateFromFileOk1: Creates navigation mesh using file");
+
+ auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
+
+ DALI_TEST_CHECK(result != nullptr);
+
+ END_TEST;
+}
+
+int UtcDaliNavigationMeshCreateFromBufferP(void)
+{
+ tet_infoline("UtcDaliNavigationMeshCreateFromBufferP: Creates navigation mesh using binary buffer");
+
+ auto fin = fopen("resources/navmesh-test.bin", "rb");
+ [[maybe_unused]] auto err = fseek(fin, 0, SEEK_END);
+ auto length = ftell(fin);
+ fseek( fin, 0, SEEK_SET);
+ std::vector<uint8_t> buffer;
+ buffer.resize(length);
+ fread( buffer.data(), 1, length, fin );
+ fclose(fin);
+ auto result = NavigationMeshFactory::CreateFromBuffer( buffer );
+ DALI_TEST_CHECK(result != nullptr);
+
+ END_TEST;
+}
+
+int UtcDaliNavigationMeshCountersP(void)
+{
+ tet_infoline("UtcDaliNavigationMeshCountersP: Test vertex, edge and face counts");
+
+ auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
+
+ DALI_TEST_CHECK(result != nullptr);
+
+ auto vertexCount = result->GetVertexCount();
+ auto edgeCount = result->GetEdgeCount();
+ auto faceCount = result->GetFaceCount();
+
+ DALI_TEST_EQUALS( vertexCount, 132, TEST_LOCATION );
+ DALI_TEST_EQUALS( edgeCount, 300, TEST_LOCATION );
+ DALI_TEST_EQUALS( faceCount, 165, TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliNavigationMeshGetVertexP(void)
+{
+ tet_infoline("UtcDaliNavigationMeshGetVertexP: Test vertex getters");
+
+ auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
+
+ DALI_TEST_CHECK(navmesh != nullptr);
+
+ auto vertexCount = navmesh->GetVertexCount();
+
+ DALI_TEST_EQUALS( vertexCount, 132, TEST_LOCATION );
+
+ // List of coords, must be verified with Blender exporter
+ // clang-format off
+ std::vector<float> vertexData = {
+ -7.000000f, -3.000000f, 0.000000f, -4.018748f, 3.000000f, 0.000000f,
+ 1.943754f, -1.500000f, 0.000000f, -2.541295f, -0.756627f, 0.000000f,
+ -0.277504f, -1.593252f, 0.000000f, 0.682341f, 2.316388f, 3.349901f,
+ 1.912569f, 1.240314f, 2.549901f, 2.215021f, -0.365898f, 1.749901f,
+ 1.460422f, -1.815717f, 0.949901f, -0.336699f, -2.992929f, 3.829999f,
+ -3.179410f, 0.153939f, 3.829999f, -3.664814f, 2.992929f, 3.829999f,
+ -1.384417f, 0.876845f, 3.829999f, -1.571236f, 1.101834f, 3.829999f
+ };
+ // clang-format on
+
+ auto j = 0;
+ for( auto i = 0u; i < 132; i+= 10, j+= 3)
+ {
+ const auto* vertex = navmesh->GetVertex(i);
+ Vector3 v0(vertex->co);
+ Vector3 v1(vertexData[j], vertexData[j+1], vertexData[j+2]);
+ DALI_TEST_EQUALS( v0, v1, TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliNavigationMeshGetEdgeP(void)
+{
+ tet_infoline("UtcDaliNavigationMeshGetEdgeP: Test edge getters");
+
+ auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
+
+ DALI_TEST_CHECK(navmesh != nullptr);
+
+ auto edgeCount = navmesh->GetEdgeCount();
+
+ DALI_TEST_EQUALS( edgeCount, 300, TEST_LOCATION );
+
+ // List of coords, must be verified with Blender exporter
+ // clang-format off
+ std::vector<uint16_t> edgeData = {
+ 2, 65535, 8, 1,
+ 8, 109, 124, 108,
+ 10, 158, 32, 35,
+ 78, 65535, 50, 52,
+ 54, 75, 70, 69,
+ 83, 65535, 83, 81,
+ 79, 65535, 86, 42,
+ 140, 65535, 94, 115,
+ 111, 112, 118, 111,
+ 101, 143, 106, 127
+ };
+ // clang-format on
+ auto j = 0;
+ for( auto i = 0u; i < 300; i+= 30, j+= 4)
+ {
+ const auto* edge = navmesh->GetEdge(i);
+ auto e0 = edge->face[0];
+ auto e1 = edge->face[1];
+ auto v0 = edge->vertex[0];
+ auto v1 = edge->vertex[1];
+
+ DALI_TEST_EQUALS(e0, edgeData[j+0], TEST_LOCATION);
+ DALI_TEST_EQUALS(e1, edgeData[j+1], TEST_LOCATION);
+ DALI_TEST_EQUALS(v0, edgeData[j+2], TEST_LOCATION);
+ DALI_TEST_EQUALS(v1, edgeData[j+3], TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliNavigationMeshGetFaceP(void)
+{
+ tet_infoline("UtcDaliNavigationMeshGetFaceP: Test face getters");
+
+ auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
+
+ DALI_TEST_CHECK(navmesh != nullptr);
+
+ auto faceCount = navmesh->GetFaceCount();
+
+ DALI_TEST_EQUALS( faceCount, 165, TEST_LOCATION );
+
+ // List of coords, must be verified with Blender exporter
+ // clang-format off
+
+ std::vector<NavigationMesh::Face> faceData = {
+ {{6, 10, 17}, {14, 32, 8}, {0.000000f, 0.000000f, 1.000000f}, {-3.024998f, 2.500000f, 0.000000f}},
+ {{130, 120, 44}, {228, 215, 33}, {0.000000f, 0.000000f, 1.000000f}, {-1.097451f, 1.192811f, 3.829999f}},
+ {{30, 9, 38}, {13, 291, 289}, {0.000000f, -0.000000f, 1.000000f}, {-3.029388f, -1.252209f, 0.000000f}},
+ {{55, 52, 53}, {140, 95, 96}, {0.522345f, -0.298279f, 0.798865f}, {0.743287f, 1.610713f, 3.136567f}},
+ {{69, 66, 67}, {91, 121, 122}, {0.071722f, -0.597219f, 0.798865f}, {1.632142f, 0.155658f, 2.016567f}},
+ {{41, 86, 87}, {81, 160, 80}, {-0.563316f, -0.210929f, 0.798864f}, {0.340215f, -1.799765f, 0.416567f}},
+ {{28, 19, 27}, {55, 74, 47}, {0.000000f, -0.000000f, 1.000000f}, {-0.640862f, -1.037395f, 0.000000f}},
+ {{118, 96, 111}, {213, 241, 240}, {0.000000f, 0.000000f, 1.000000f}, {-6.577459f, -0.586560f, 3.829999f}},
+ {{91, 107, 103}, {170, 258, 257}, {-0.021129f, 0.023143f, 0.999509f}, {-2.551766f, 1.007552f, 3.829145f}},
+ {{97, 120, 130}, {191, 228, 271}, {0.000000f, 0.000000f, 1.000000f}, {-1.795930f, 0.710873f, 3.829999f}},
+ {{30, 39, 31}, {290, 296, 295}, {0.000000f, 0.000000f, 1.000000f}, {-2.291577f, -0.509718f, 0.000000f}},
+ };
+ // clang-format on
+ auto j = 0;
+ for( auto i = 0u; i < 165; i+= 16, j++)
+ {
+ const auto* face = navmesh->GetFace(i);
+ Vector3 n0(face->normal);
+ Vector3 c0(face->center);
+
+ Vector3 n1(faceData[j].normal);
+ Vector3 c1(faceData[j].center);
+
+ DALI_TEST_EQUALS( n0, n1, TEST_LOCATION);
+ DALI_TEST_EQUALS( c0, c1, TEST_LOCATION);
+
+ DALI_TEST_EQUALS( faceData[j].vertex[0], face->vertex[0], TEST_LOCATION);
+ DALI_TEST_EQUALS( faceData[j].vertex[1], face->vertex[1], TEST_LOCATION);
+ DALI_TEST_EQUALS( faceData[j].vertex[2], face->vertex[2], TEST_LOCATION);
+
+ DALI_TEST_EQUALS( faceData[j].edge[0], face->edge[0], TEST_LOCATION);
+ DALI_TEST_EQUALS( faceData[j].edge[1], face->edge[1], TEST_LOCATION);
+ DALI_TEST_EQUALS( faceData[j].edge[2], face->edge[2], TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+
+int UtcDaliNavigationGetGravityP(void)
+{
+ tet_infoline("UtcDaliNavigationGetGravityP: Tests gravity vector");
+ auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
+ auto gravity = navmesh->GetGravityVector();
+
+ // navmesh-test.bin is exported in Blender and the default gravity is Z = -1
+ Dali::Vector3 expectedGravity( 0.0f, 0.0f, -1.0f );
+
+ DALI_TEST_EQUALS( gravity, expectedGravity, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliNavigationSetTransformP(void)
+{
+ tet_infoline("UtcDaliNavigationSetTransformP: Test setting transform");
+
+ auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
+
+ Matrix matrix;
+ matrix.SetIdentity();
+ Quaternion q = Quaternion( Radian(Degree(-90)), Vector3(1.0, 0.0, 0.0));
+ Matrix newMatrix;
+ matrix.Multiply( newMatrix, matrix, q); // Rotate matrix along X-axis
+
+ navmesh->SetSceneTransform(newMatrix);
+
+ auto point = Vector3(0, 1, 0);
+
+ [[maybe_unused]] Vector3 navMeshLocalSpace;
+ [[maybe_unused]] Vector3 navMeshParentSpace;
+ navMeshLocalSpace = navmesh->PointSceneToLocal(point);
+
+ // Should match gravity vector
+ auto gravityVector = navmesh->GetGravityVector();
+
+ // 'point' should be turned into the gravity vector after transforming into the local space
+ DALI_TEST_EQUALS( navMeshLocalSpace, gravityVector, TEST_LOCATION);
+
+ navMeshParentSpace = navmesh->PointLocalToScene(gravityVector);
+
+ // The gravity should be transformed back into point
+ DALI_TEST_EQUALS( navMeshParentSpace, point, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliNavigationFindFloor0P(void)
+{
+ tet_infoline("UtcDaliNavigationFindFloor0P: Finds floor with result");
+
+ auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
+
+ // All calculations in the navmesh local space
+ navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
+
+ std::vector<Vector3> inPositions;
+ std::vector<Vector3> expectedPositions;
+ std::vector<uint32_t> expectedFaceIndex;
+ std::vector<bool> expectedResult;
+
+ // Lift slightly over the floor level
+ auto upFromGravity = navmesh->GetGravityVector() * (0.05f);
+
+ auto size = navmesh->GetFaceCount();
+ for( auto i = 0u; i < size; ++i)
+ {
+ const auto* face = navmesh->GetFace(i);
+ Vector3(face->center);
+ inPositions.emplace_back(Vector3(face->center));
+ inPositions.back() -= Vector3( upFromGravity );
+ expectedResult.emplace_back(true);
+
+ expectedPositions.emplace_back(face->center);
+ expectedFaceIndex.emplace_back(i);
+ }
+
+ // Add negative results
+ // Middle 'circle' of scene
+ inPositions.emplace_back(Vector3(-0.048838f, 0.039285f, 0.013085f));
+ expectedPositions.emplace_back();
+ expectedFaceIndex.emplace_back( NavigationMesh::NULL_FACE );
+ expectedResult.emplace_back(false);
+
+ // Triangle under stairs
+ inPositions.emplace_back(Vector3(0.44365f, -1.787f, 0.13085f));
+ expectedPositions.emplace_back();
+ expectedFaceIndex.emplace_back( NavigationMesh::NULL_FACE );
+ expectedResult.emplace_back(false);
+
+ // Outside area
+ inPositions.emplace_back(Vector3(0.77197f, -3.8596f, 0.13085f));
+ expectedPositions.emplace_back();
+ expectedFaceIndex.emplace_back( NavigationMesh::NULL_FACE );
+ expectedResult.emplace_back(false);
+
+ for( auto i = 0u; i < inPositions.size(); ++i )
+ {
+ Vector3 outPosition;
+ uint32_t faceIndex {NavigationMesh::NULL_FACE};
+ auto result = navmesh->FindFloor(inPositions[i], outPosition, faceIndex);
+ DALI_TEST_EQUALS( bool(result), bool(expectedResult[i]), TEST_LOCATION);
+ DALI_TEST_EQUALS( faceIndex, expectedFaceIndex[i], TEST_LOCATION);
+ DALI_TEST_EQUALS( outPosition, expectedPositions[i], TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliNavigationFindFloorForFace1P(void)
+{
+ tet_infoline("UtcDaliNavigationFindFloorForFace1P: Finds floor for selected face");
+
+ auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
+
+ // All calculations in the navmesh local space
+ navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
+
+ {
+ auto faceIndex = 0u;
+ auto position = Vector3( 0, 0, 0);
+ auto dontCheckNeighbours = true;
+ auto outPosition = Vector3();
+ auto expectedPosition = Vector3();
+ bool result = false;
+
+ {
+ // test 1. position lies within selected triangle
+ faceIndex = 137;
+ position = Vector3(-6.0767f, -1.7268f, 4.287f);
+ expectedPosition = Vector3(-6.0767f, -1.7268f, 3.83f);
+ dontCheckNeighbours = true;
+ result = navmesh->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
+
+ DALI_TEST_EQUALS( result, true, TEST_LOCATION);
+ DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
+ }
+
+ {
+ // test 2. position lies outside selected triangle, not checking neighbours
+ faceIndex = 137;
+ position = Vector3(-5.3073f, -0.6023f, 4.287f);
+ expectedPosition = Vector3::ZERO;
+ outPosition = Vector3::ZERO;
+ dontCheckNeighbours = true;
+ result = navmesh->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
+
+ DALI_TEST_EQUALS( result, false, TEST_LOCATION);
+ DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
+ }
+
+ {
+ // test 3. position lies outside selected triangle but this time checking neighbours
+ faceIndex = 137;
+ position = Vector3(-5.3073f, -0.6023f, 4.287f);
+ expectedPosition = Vector3(-5.3073, -0.6023, 3.83);
+ outPosition = Vector3::ZERO;
+ dontCheckNeighbours = false;
+ result = navmesh->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
+
+ DALI_TEST_EQUALS( result, true, TEST_LOCATION);
+ DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
+ }
+ }
+
+ END_TEST;
+}
+
+int UtcDaliNavigationFindFloorForFace2P(void)
+{
+ tet_infoline("UtcDaliNavigationFindFloorForFace2P: Finds floor for selected face");
+
+ auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
+
+ // All calculations in the navmesh local space
+ navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
+
+ {
+ [[maybe_unused]] auto faceIndex = 0u;
+ auto position = Vector3( 0, 0, 0);
+ auto dontCheckNeighbours = true;
+ auto outPosition = Vector3();
+ auto expectedPosition = Vector3();
+ bool result = false;
+
+ {
+ // test 4. position lies within a triangle but this time full search forced,
+ // the navmesh must have no previous searches (mCurrentFace shouldn't be set)
+ faceIndex = 137;
+ position = Vector3(-6.0767f, -1.7268f, 4.287f);
+ expectedPosition = Vector3(-6.0767f, -1.7268f, 3.83f);
+ dontCheckNeighbours = true;
+ result = navmesh->FindFloorForFace(position, NavigationMesh::NULL_FACE, dontCheckNeighbours, outPosition);
+
+ DALI_TEST_EQUALS( result, true, TEST_LOCATION);
+ DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
+ }
+
+ }
+
+ END_TEST;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "dali-scene3d/public-api/algorithm/navigation-mesh.h"
+#include "dali-scene3d/public-api/algorithm/path-finder.h"
+#include "dali-scene3d/public-api/loader/navigation-mesh-factory.h"
+#include <dali-test-suite-utils.h>
+
+using namespace Dali;
+using namespace Dali::Scene3D::Algorithm;
+using namespace Dali::Scene3D::Loader;
+
+bool CompareResults( const std::vector<uint32_t>& nodes, const WayPointList& waypoints )
+{
+ if(nodes.size() != waypoints.size())
+ {
+ return false;
+ }
+ for(auto i = 0u; i < nodes.size(); ++i )
+ {
+ if(nodes[i] != waypoints[i].GetNavigationMeshFaceIndex())
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+int UtcDaliPathFinderNewP(void)
+{
+ auto navmesh = NavigationMeshFactory::CreateFromFile( "resources/navmesh-test.bin");
+
+ auto pathfinder = PathFinder::New( *navmesh, PathFinderAlgorithm::DEFAULT );
+
+ DALI_TEST_CHECK(navmesh);
+ DALI_TEST_CHECK(pathfinder);
+
+ END_TEST;
+}
+
+int UtcDaliPathFinderNewFail(void)
+{
+ auto navmesh = NavigationMeshFactory::CreateFromFile( "resources/navmesh-test.bin");
+
+ auto pathfinder = PathFinder::New( *navmesh, static_cast<PathFinderAlgorithm>(-1) );
+
+ DALI_TEST_CHECK(navmesh);
+ DALI_TEST_CHECK(!pathfinder);
+
+ END_TEST;
+}
+
+void printWaypointForPython( WayPointList& waypoints)
+{
+ tet_printf( "size: %d\n", waypoints.size());
+ tet_printf( "[");
+ for(auto& wp : waypoints)
+ {
+ auto index = wp.GetNavigationMeshFaceIndex();
+ tet_printf("%d, ", index);
+ }
+ tet_printf( "]");
+}
+
+int UtcDaliPathFinderDjikstraFindPath0(void)
+{
+ auto navmesh = NavigationMeshFactory::CreateFromFile( "resources/navmesh-test.bin");
+
+ auto pathfinder = PathFinder::New( *navmesh, PathFinderAlgorithm::DJIKSTRA_SHORTEST_PATH );
+
+ DALI_TEST_CHECK(navmesh);
+ DALI_TEST_CHECK(pathfinder);
+
+ {
+ auto waypoints = pathfinder->FindPath(18, 139);
+ DALI_TEST_NOT_EQUALS(int(waypoints.size()), 0, 0, TEST_LOCATION);
+
+ // Results are verified in the Blender
+ std::vector<uint32_t> expectedResults =
+ {18, 97, 106, 82, 50, 139};
+
+ DALI_TEST_EQUALS(CompareResults(expectedResults, waypoints), true, TEST_LOCATION);
+ }
+ //printWaypointForPython(waypoints);
+
+ {
+ // Top floor middle to the tree
+
+ auto waypoints = pathfinder->FindPath(18, 157);
+ DALI_TEST_NOT_EQUALS(int(waypoints.size()), 0, 0, TEST_LOCATION);
+
+ //printWaypointForPython(waypoints);
+
+ // Results are verified in the Blender
+ std::vector<uint32_t> expectedResults =
+ {18, 97, 106, 82, 50, 6, 89, 33, 157};
+
+ DALI_TEST_EQUALS(CompareResults(expectedResults, waypoints), true, TEST_LOCATION);
+
+ }
+
+ END_TEST;
+}
+
+int UtcDaliPathFinderDjikstraFindPath1(void)
+{
+ auto navmesh = NavigationMeshFactory::CreateFromFile( "resources/navmesh-test.bin");
+ // All coordinates in navmesh local space
+ navmesh->SetSceneTransform( Matrix(Matrix::IDENTITY));
+
+ auto pathfinder = PathFinder::New( *navmesh, PathFinderAlgorithm::DJIKSTRA_SHORTEST_PATH );
+
+ DALI_TEST_CHECK(navmesh);
+ DALI_TEST_CHECK(pathfinder);
+
+ {
+ Vector3 from(-6.0767, -1.7268, 0.1438); // ground floor
+ Vector3 to(-6.0767, -1.7268, 4.287); // first floor
+
+ auto waypoints = pathfinder->FindPath(from, to);
+ DALI_TEST_NOT_EQUALS(int(waypoints.size()), 0, 0, TEST_LOCATION);
+
+ // Results are verified in the Blender
+ std::vector<uint32_t> expectedResults =
+ {154, 58, 85, 106, 128, 132, 137};
+
+ DALI_TEST_EQUALS(CompareResults(expectedResults, waypoints), true, TEST_LOCATION);
+
+ // Verify last and first points by finding floor points
+ {
+ Vector3 verifyPos = Vector3::ZERO;
+ uint32_t verifyIndex = NavigationMesh::NULL_FACE;
+ auto result = navmesh->FindFloor(from, verifyPos, verifyIndex);
+
+ DALI_TEST_EQUALS(result, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(verifyPos, waypoints[0].GetScenePosition(), TEST_LOCATION);
+ DALI_TEST_EQUALS(verifyIndex, waypoints[0].GetNavigationMeshFaceIndex(), TEST_LOCATION);
+
+ // Verified with Blender
+ Vector2 local(1.064201f, -0.273200f);
+ DALI_TEST_EQUALS(local, waypoints[0].GetFaceLocalSpacePosition(), TEST_LOCATION);
+ }
+
+ {
+ Vector3 verifyPos = Vector3::ZERO;
+ uint32_t verifyIndex = NavigationMesh::NULL_FACE;
+ auto result = navmesh->FindFloor(to, verifyPos, verifyIndex);
+
+ DALI_TEST_EQUALS(result, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(verifyPos, waypoints.back().GetScenePosition(), TEST_LOCATION);
+ DALI_TEST_EQUALS(verifyIndex, waypoints.back().GetNavigationMeshFaceIndex(), TEST_LOCATION);
+
+ // Verified with Blender
+ Vector2 local(0.165907f, 0.142597f);
+ DALI_TEST_EQUALS(local, waypoints.back().GetFaceLocalSpacePosition(), TEST_LOCATION);
+ }
+ }
+
+ END_TEST;
+}
\ No newline at end of file
OPTION(INSTALL_DOXYGEN_DOC "Install doxygen doc" ON)
OPTION(CONFIGURE_AUTOMATED_TESTS "Configure automated tests" ON)
OPTION(USE_DEFAULT_RESOURCE_DIR "Whether to use the default resource folders. Otherwise set environment variables for DALI_IMAGE_DIR, DALI_SOUND_DIR, DALI_STYLE_DIR, DALI_STYLE_IMAGE_DIR and DALI_DATA_READ_ONLY_DIR" ON)
-OPTION(BUILD_SCENE3D "Whether to build dali-scene3d." ON)
+OPTION(BUILD_SCENE3D "Whether to build dali-scene3d." ON)
IF( ENABLE_PKG_CONFIGURE )
FIND_PACKAGE( PkgConfig REQUIRED )
SET( doxygenEnabled ON )
# 'prefix' is used by doxygen in-files.
SET( prefix ${PREFIX} )
+
+ # Some doxygen properties not allowed at low version.
+ # We need to get doxygen version, and block in doxygen in-files.
+ EXECUTE_PROCESS( COMMAND bash -c "${DOXYGEN_EXECUTABLE} --version" OUTPUT_VARIABLE DOXYGEN_VERSION )
+
+ IF(${DOXYGEN_VERSION} VERSION_LESS "1.9.1")
+ SET( DOXYGEN_VERSION_LESS_1_9_1_BLOCKED "# ")
+ ELSE()
+ SET( DOXYGEN_VERSION_LESS_1_9_1_BLOCKED "")
+ ENDIF()
+
SET( DOXYGEN_DOCS_DIR ${ROOT_SRC_DIR}/docs )
SET( DOXYGEN_ROOT_DIR ${ROOT_SRC_DIR} )
SET( DOXYGEN_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/docs )
-# Doxyfile 1.8.11
+# Doxyfile @DOXYGEN_VERSION@
+# Note : Some option need to be blocked if version is lower than 1.9.1.
+# See how "DOXYGEN_VERSION_LESS_1_9_1_BLOCKED" defined in CMakeList.txt.
+
+# TODO : Need to update this file use updated doxygen 1.9.1 format
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
ALIASES += SINCE_1_4="@since 1.4"
ALIASES += SINCE_1_9="@since 1.9"
ALIASES += SINCE_2_0="@since 2.0"
+ALIASES += SINCE_2_1="@since 2.1"
+ALIASES += SINCE_2_2="@since 2.2"
# Extra tags for Tizen 3.0
ALIASES += SINCE_1_2_2="@since 1.2.2"
ALIASES += DEPRECATED_1_3_51="@deprecated Deprecated since 1.3.51"
ALIASES += DEPRECATED_1_4="@deprecated Deprecated since 1.4"
ALIASES += DEPRECATED_2_0="@deprecated Deprecated since 2.0"
+ALIASES += DEPRECATED_2_1="@deprecated Deprecated since 2.1"
+ALIASES += DEPRECATED_2_2="@deprecated Deprecated since 2.2"
ALIASES += PLATFORM=""
ALIASES += PRIVLEVEL_PLATFORM=""
#ALIASES += SINCE_1_3="\par Since:\n 5.0, DALi version 1.3"
#ALIASES += SINCE_1_4="\par Since:\n 5.5, DALi version 1.4"
#ALIASES += SINCE_1_9="\par Since:\n 6.0, DALi version 1.9"
-#ALIASES += SINCE_2_0="\par Since:\n 6.0, DALi version 2.0"
+#ALIASES += SINCE_2_0="\par Since:\n 6.5, DALi version 2.0"
+#ALIASES += SINCE_2_1="\par Since:\n 7.0, DALi version 2.1"
+#ALIASES += SINCE_2_2="\par Since:\n 7.5, DALi version 2.2"
## Extra tags for Tizen 3.0
#ALIASES += SINCE_1_2_2="\par Since:\n 3.0, DALi version 1.2.2"
#ALIASES += DEPRECATED_1_3_39="@deprecated Deprecated since 5.5, DALi version 1.3.39"
#ALIASES += DEPRECATED_1_3_51="@deprecated Deprecated since 5.5, DALi version 1.3.51"
#ALIASES += DEPRECATED_1_4="@deprecated Deprecated since 5.5, DALi version 1.4"
+#ALIASES += DEPRECATED_2_0="@deprecated Deprecated since 6.5, DALi version 2.0"
+#ALIASES += DEPRECATED_2_1="@deprecated Deprecated since 7.0, DALi version 2.1"
+#ALIASES += DEPRECATED_2_2="@deprecated Deprecated since 7.5, DALi version 2.2"
#ALIASES += PLATFORM="@platform"
#ALIASES += PRIVLEVEL_PLATFORM="\par Privilege Level:\n platform"
-# Doxyfile 1.8.11
-# Note : If you want to upgrade Doxyfile into 1.9.1
-# Find "###1.9.1###" and remove these comments.
-# For example,
-# ###1.9.1###PYTHON_DOCSTRING = YES -->
-# PYTHON_DOCSTRING = YES
+# Doxyfile @DOXYGEN_VERSION@
+# Note : Some option need to be blocked if version is lower than 1.9.1.
+# See how "DOXYGEN_VERSION_LESS_1_9_1_BLOCKED" defined in CMakeList.txt.
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
# documentation blocks is shown as doxygen documentation.
# The default value is: YES.
-###1.9.1###PYTHON_DOCSTRING = YES
+@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ PYTHON_DOCSTRING = YES
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.
# DOT_NUM_THREADS setting.
# Minimum value: 0, maximum value: 32, default value: 1.
-###1.9.1###NUM_PROC_THREADS = 1
+@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ NUM_PROC_THREADS = 1
#---------------------------------------------------------------------------
# Build related configuration options
# parameters remain unnamed in the output.
# The default value is: YES.
-###1.9.1###RESOLVE_UNNAMED_PARAMS = YES
+@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ RESOLVE_UNNAMED_PARAMS = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
# YES then doxygen will add the directory of each input to the include path.
# The default value is: YES.
-###1.9.1###CLANG_ADD_INC_PATHS = YES
+@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ CLANG_ADD_INC_PATHS = YES
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# The default value is: png.
# This tag requires that the tag GENERATE_HTML is set to YES.
-###1.9.1###HTML_FORMULA_FORMAT = png
+@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ HTML_FORMULA_FORMAT = png
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# The default value is: NO.
# This tag requires that the tag UML_LOOK is set to YES.
-###1.9.1###DOT_UML_DETAILS = NO
+@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ DOT_UML_DETAILS = NO
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold
# Minimum value: 0, maximum value: 1000, default value: 17.
# This tag requires that the tag HAVE_DOT is set to YES.
-###1.9.1###DOT_WRAP_THRESHOLD = 17
+@DOXYGEN_VERSION_LESS_1_9_1_BLOCKED@ DOT_WRAP_THRESHOLD = 17
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
# collaboration graphs will show the relations between templates and their
--- /dev/null
+#ifndef DALI_SCENE3D_NAVIGATION_MESH_HEADER_H
+#define DALI_SCENE3D_NAVIGATION_MESH_HEADER_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 <inttypes.h>
+
+namespace Dali::Scene3D::Internal::Algorithm
+{
+/**
+ * @struct NavigationMeshHeader
+ *
+ * Base header structure contains only the version field. It will allow adding changes to the
+ * exporter while maintaining backward compatibility.
+ */
+struct NavigationMeshHeader
+{
+ uint32_t checksum; ///< Checksum (used to test for endianness, tested by reader )
+ uint32_t version; ///< Version of the API
+};
+
+/**
+ * @struct NavigationMeshHeader_V10
+ *
+ * Extension of header for version 1.0 of NavigationMesh binary file
+ */
+struct NavigationMeshHeader_V10 : public NavigationMeshHeader
+{
+ uint32_t dataOffset; ///< Offset where data starts (depends on endianness)
+
+ uint32_t vertexCount; ///< total count of vertices
+ uint32_t vertexDataOffset; ///< offset into data array where vertices start
+
+ uint32_t edgeCount; ///< total count of edges
+ uint32_t edgeDataOffset; ///< offset into data array where edges are stored
+
+ uint32_t polyCount; ///< total number of polys
+ uint32_t polyDataOffset; ///< offset into data array where edges are stored
+
+ float gravityVector[3]; /// Gravity vector for the data (down vector)
+};
+
+}
+#endif // DALI_SCENE3D_NAVIGATION_MESH_HEADER_H
--- /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.
+ */
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/algorithm/navigation-mesh-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+
+#include <filesystem>
+#include <algorithm>
+#include <cerrno>
+#include <cstdio>
+#include <cstring>
+
+using Dali::Vector3;
+
+namespace Dali::Scene3D::Internal::Algorithm
+{
+
+using Poly = Dali::Scene3D::Algorithm::NavigationMesh::Face;
+using Edge = Dali::Scene3D::Algorithm::NavigationMesh::Edge;
+using Vertex = Dali::Scene3D::Algorithm::NavigationMesh::Vertex;
+
+// Internal Navigation ray structure
+struct NavigationRay
+{
+ Dali::Vector3 origin; // Origin of ray
+ Dali::Vector3 direction; // Direction of ray
+};
+
+/**
+ * Helper function calculating intersection point between triangle and ray
+ */
+static bool RayTriangleIntersect(
+ const Vector3& origin, const Vector3& direction, const Vector3& vertex0, const Vector3& vertex1, const Vector3& vertex2, const Vector3& normal, float& outDistance, Vector3& position)
+{
+ auto N = normal;
+ N.Normalize();
+ // Step 1: finding P
+
+ // check if ray and plane are parallel ?
+ float NdotRayDirection = N.Dot(direction);
+ if(Dali::Equals(NdotRayDirection, 0.0f))
+ {
+ return false; // they are parallel so they don'outDistance intersect !
+ }
+
+ // compute d parameter using equation 2
+ float d = -N.Dot(vertex0);
+
+ // compute outDistance (equation 3)
+ outDistance = -(N.Dot(origin) + d) / NdotRayDirection;
+
+ // check if the triangle is in behind the ray
+ if(outDistance < 0) return false; // the triangle is behind
+
+ // compute the intersection point using equation 1
+ auto P = origin + (direction * outDistance);
+
+ position = P;
+
+ auto edge0 = vertex1 - vertex0;
+ auto edge1 = vertex2 - vertex1;
+ auto edge2 = vertex0 - vertex2;
+ auto C0 = P - vertex0;
+ auto C1 = P - vertex1;
+ auto C2 = P - vertex2;
+
+ auto r0 = N.Dot(edge0.Cross(C0));
+ auto r1 = N.Dot(edge1.Cross(C1));
+ auto r2 = N.Dot(edge2.Cross(C2));
+ if(r0 > 0 &&
+ r1 > 0 &&
+ r2 > 0) return true; // P is inside the triangle
+
+ return false; // this ray hits the triangle
+}
+
+NavigationMesh::NavigationMesh(const std::vector<uint8_t>& buffer)
+{
+ mBuffer.resize(buffer.size());
+ std::copy(buffer.begin(), buffer.end(), mBuffer.begin());
+
+ // Setup header from the buffer
+ mHeader = *reinterpret_cast<NavigationMeshHeader_V10*>(mBuffer.data());
+ mCurrentFace = Scene3D::Algorithm::NavigationMesh::NULL_FACE;
+}
+
+[[nodiscard]] uint32_t NavigationMesh::GetFaceCount() const
+{
+ return mHeader.polyCount;
+}
+
+[[nodiscard]] uint32_t NavigationMesh::GetEdgeCount() const
+{
+ return mHeader.edgeCount;
+}
+
+[[nodiscard]] uint32_t NavigationMesh::GetVertexCount() const
+{
+ return mHeader.vertexCount;
+}
+
+bool NavigationMesh::FindFloorForFace(const Dali::Vector3& position, uint32_t faceIndex, bool dontCheckNeighbours, Dali::Vector3& outPosition)
+{
+ if(faceIndex == ::Dali::Scene3D::Algorithm::NavigationMesh::NULL_FACE)
+ {
+ faceIndex = mCurrentFace;
+ }
+
+ // No current face, do full check
+ if(mCurrentFace == ::Dali::Scene3D::Algorithm::NavigationMesh::NULL_FACE)
+ {
+ return FindFloor(position, outPosition);
+ }
+
+ NavigationRay ray;
+ ray.origin = PointSceneToLocal(Dali::Vector3(position)); // origin is equal position
+
+ // Ray direction matches gravity direction
+ ray.direction = Vector3(mHeader.gravityVector);
+
+ IntersectResult result = NavigationRayFaceIntersection(ray, *GetFace(uint16_t(faceIndex)));
+ if(result.result)
+ {
+ outPosition = PointLocalToScene(result.point);
+ return true;
+ }
+ else
+ {
+ if(dontCheckNeighbours)
+ {
+ return false;
+ }
+
+ // Test neighbouring by edges
+ const auto& poly = GetFace(uint16_t(faceIndex));
+
+ // collect faces sharing edges
+ std::vector<uint32_t> faces;
+
+ for(unsigned short i : poly->edge)
+ {
+ const auto& edge = *GetEdge(i);
+
+ // store faces
+ for(unsigned short j : edge.face)
+ {
+ if(j != ::Dali::Scene3D::Algorithm::NavigationMesh::NULL_FACE && j != faceIndex)
+ {
+ faces.emplace_back(j);
+ }
+ }
+ }
+
+ if(faces.empty())
+ {
+ return false;
+ }
+
+ for(const auto& p : faces)
+ {
+ if(FindFloorForFace(position, p, true, outPosition))
+ {
+ mCurrentFace = p;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Drops observer onto the floor
+ *
+ * When dropping observer, the nearest floor face is found
+ */
+bool NavigationMesh::FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition)
+{
+ uint32_t polyIndex = 0;
+ return FindFloor(position, outPosition, polyIndex);
+}
+
+bool NavigationMesh::FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition, uint32_t& faceIndex)
+{
+ [[maybe_unused]] auto newPos = PointSceneToLocal(Dali::Vector3(position));
+
+ NavigationRay ray;
+
+ ray.origin = PointSceneToLocal(Dali::Vector3(position)); // origin is equal position
+
+ // Ray direction matches gravity direction
+ ray.direction = Vector3(mHeader.gravityVector);
+
+ const auto POLY_COUNT = GetFaceCount();
+ std::vector<IntersectResult> results;
+ for(auto i = 0u; i < POLY_COUNT; ++i)
+ {
+ auto result = NavigationRayFaceIntersection(ray, *GetFace(i));
+ if(result.result)
+ {
+ result.faceIndex = i;
+ results.emplace_back(result);
+ }
+ }
+
+ // find minimal distance to the floor and return that position and distance
+ if(results.empty())
+ {
+ return false;
+ }
+
+ std::sort(results.begin(), results.end(), [](const IntersectResult& lhs, const IntersectResult& rhs)
+ { return lhs.distance < rhs.distance; });
+
+ outPosition = PointLocalToScene(results.front().point);
+ faceIndex = results.front().faceIndex;
+ mCurrentFace = results.front().faceIndex;
+
+ return true;
+}
+
+const Poly* NavigationMesh::GetFace(int index) const
+{
+ auto* basePtr = reinterpret_cast<const Poly*>(mBuffer.data() + mHeader.dataOffset + mHeader.polyDataOffset);
+ return &basePtr[index];
+}
+
+const Edge* NavigationMesh::GetEdge(int index) const
+{
+ auto* basePtr = reinterpret_cast<const Edge*>(mBuffer.data() + mHeader.dataOffset + mHeader.edgeDataOffset);
+ return &basePtr[index];
+}
+
+const Vertex* NavigationMesh::GetVertex(int index) const
+{
+ auto* basePtr = reinterpret_cast<const Vertex*>(mBuffer.data() + mHeader.dataOffset + mHeader.vertexDataOffset);
+ return &basePtr[index];
+}
+
+NavigationMesh::IntersectResult NavigationMesh::NavigationRayFaceIntersection(NavigationRay& ray, const Face& face)
+{
+ auto vi0 = *GetVertex(face.vertex[0]);
+ auto vi1 = *GetVertex(face.vertex[1]);
+ auto vi2 = *GetVertex(face.vertex[2]);
+
+ IntersectResult result{Vector3::ZERO, 0.0f, 0u, false};
+
+ result.result = RayTriangleIntersect(ray.origin, ray.direction, Vector3(vi0.x, vi0.y, vi0.z), Vector3(vi1.x, vi1.y, vi1.z), Vector3(vi2.x, vi2.y, vi2.z), Vector3(face.normal), result.distance, result.point);
+ return result;
+}
+
+void NavigationMesh::SetTransform(const Dali::Matrix& transform)
+{
+ mTransform = transform;
+ transform.InvertTransform(mTransformInverse);
+}
+
+Dali::Vector3 NavigationMesh::PointSceneToLocal(const Dali::Vector3& point)
+{
+ // Transform point into navmesh space
+ Dali::Vector4 invNewPos = mTransformInverse * Dali::Vector4(point.x, -point.y, point.z, 1.0f);
+ invNewPos.y *= -1.0f;
+
+ return Dali::Vector3(invNewPos.AsFloat());
+}
+
+Dali::Vector3 NavigationMesh::PointLocalToScene(const Dali::Vector3& point)
+{
+ // Transform point into scene transform space
+ Dali::Vector4 newPos = mTransform * Dali::Vector4(point.x, -point.y, point.z, 1.0f);
+ newPos.y *= -1.0f;
+
+ return Dali::Vector3(newPos.AsFloat());
+}
+
+Dali::Vector3 NavigationMesh::GetGravityVector() const
+{
+ return Dali::Vector3( mHeader.gravityVector );
+}
+
+}
\ No newline at end of file
--- /dev/null
+#ifndef DALI_SCENE3D_INTERNAL_NAVIGATION_MESH_H
+#define DALI_SCENE3D_INTERNAL_NAVIGATION_MESH_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/algorithm/navigation-mesh.h>
+#include <dali-scene3d/public-api/algorithm/path-finder.h>
+
+// INTERNAL EXTERNAL
+#include <dali/public-api/actors/actor.h>
+#include <dali/public-api/math/matrix.h>
+#include <dali/public-api/math/vector3.h>
+#include <dali/public-api/math/vector4.h>
+
+#include <cinttypes>
+#include <cstdio>
+#include <mutex>
+#include <vector>
+#include "navigation-mesh-header.h"
+
+namespace Dali::Scene3D::Loader
+{
+class NavigationMeshFactory;
+}
+
+namespace Dali::Scene3D::Internal::Algorithm
+{
+
+class NavigationRay;
+
+/**
+ * @class NavigationMesh
+ */
+class NavigationMesh
+{
+public:
+ using Face = Dali::Scene3D::Algorithm::NavigationMesh::Face;
+ using Edge = Dali::Scene3D::Algorithm::NavigationMesh::Edge;
+ using Vertex = Dali::Scene3D::Algorithm::NavigationMesh::Vertex;
+
+private:
+
+ friend class Scene3D::Loader::NavigationMeshFactory;
+
+ /**
+ * Constructor
+ */
+ NavigationMesh(const std::vector<uint8_t>& buffer);
+
+public:
+
+ /**
+ * Destructor
+ */
+ ~NavigationMesh() = default;
+
+ /**
+ * Result of Ray/Polygon intersection
+ */
+ struct IntersectResult
+ {
+ Dali::Vector3 point;
+ float distance;
+ uint16_t faceIndex;
+ bool result;
+ };
+
+ /**
+ * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::GetFaceCount()
+ */
+ [[nodiscard]] uint32_t GetFaceCount() const;
+
+ /**
+ * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::GetEdgeCount()
+ */
+ [[nodiscard]] uint32_t GetEdgeCount() const;
+
+ /**
+ * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::GetVertexCount()
+ */
+ [[nodiscard]] uint32_t GetVertexCount() const;
+
+ /**
+ * Looks for floor only within the face
+ * @param[in] position Position to be projected onto the face
+ * @param[in] faceIndex Face index
+ * @param[in] dontCheckNeighbours states whether to traverse onto neighbouring faces
+ * @param[out] outPosition Output position
+ *
+ * @return true if success
+ */
+ bool FindFloorForFace(const Dali::Vector3& position, uint32_t faceIndex, bool dontCheckNeighbours, Dali::Vector3& outPosition);
+
+ /**
+ * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::FindFloor()
+ */
+ bool FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition);
+
+ /**
+ * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::FindFloor()
+ */
+ bool FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition, uint32_t& faceIndex);
+
+ /**
+ * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::GetFace()
+ */
+ [[nodiscard]] const Face* GetFace(int index) const;
+
+ /**
+ * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::GetEdge()
+ */
+ [[nodiscard]] const Edge* GetEdge(int index) const;
+
+ /**
+ * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::GetVertex()
+ */
+ [[nodiscard]] const Vertex* GetVertex(int index) const;
+
+ /**
+ * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::SetSceneTransform()
+ */
+ void SetTransform(const Dali::Matrix& transform);
+
+ /**
+ * Tests intersection between navigation ray and face
+ */
+ IntersectResult NavigationRayFaceIntersection(NavigationRay& ray, const Face& face);
+
+ /**
+ * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::PointSceneToLocal()
+ */
+ Dali::Vector3 PointSceneToLocal(const Dali::Vector3& point);
+
+ /**
+ * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::PointLocalToScene()
+ */
+ Dali::Vector3 PointLocalToScene(const Dali::Vector3& point);
+
+ /**
+ * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::GetGravityVector()
+ */
+ [[nodiscard]] Dali::Vector3 GetGravityVector() const;
+
+private:
+ std::vector<uint8_t> mBuffer; //< Data buffer
+ NavigationMeshHeader_V10 mHeader; //< Navigation mesh header
+ uint16_t mCurrentFace; //< Current face (last floor position)
+ Dali::Matrix mTransform; //< Transform matrix
+ Dali::Matrix mTransformInverse; //< Inverse of the transform matrix
+};
+
+inline Internal::Algorithm::NavigationMesh& GetImplementation(Dali::Scene3D::Algorithm::NavigationMesh& navigationMesh)
+{
+ return *navigationMesh.mImpl;
+}
+
+} // namespace Dali::Scene3D::Internal::Algorithm
+
+#endif // DALI_SCENE3D_INTERNAL_NAVIGATION_MESH_H
--- /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.
+ */
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/algorithm/path-finder-waypoint.h>
+#include <dali-scene3d/internal/algorithm/path-finder-djikstra.h>
+#include <dali-scene3d/internal/algorithm/path-finder-waypoint-data.h>
+
+// EXTERNAL INCLUDES
+#include <limits>
+#include <vector>
+
+using WayPointList = Dali::Scene3D::Algorithm::WayPointList;
+
+namespace Dali::Scene3D::Internal::Algorithm
+{
+
+PathFinderAlgorithmDjikstra::PathFinderAlgorithmDjikstra(Dali::Scene3D::Algorithm::NavigationMesh& navMesh)
+: mNavigationMesh(&GetImplementation(navMesh))
+{
+ PrepareData();
+}
+
+PathFinderAlgorithmDjikstra::~PathFinderAlgorithmDjikstra() = default;
+
+Scene3D::Algorithm::WayPointList PathFinderAlgorithmDjikstra::FindPath(const Dali::Vector3& positionFrom, const Dali::Vector3& positionTo)
+{
+ Dali::Vector3 outPosFrom;
+ uint32_t polyIndexFrom;
+ auto result = mNavigationMesh->FindFloor(positionFrom, outPosFrom, polyIndexFrom);
+
+ Scene3D::Algorithm::WayPointList waypoints;
+
+ if(result)
+ {
+ Dali::Vector3 outPosTo;
+ uint32_t polyIndexTo;
+ result = mNavigationMesh->FindFloor(positionTo, outPosTo, polyIndexTo);
+
+ if(result)
+ {
+ // Get waypoints
+ waypoints = FindPath(polyIndexFrom, polyIndexTo);
+
+ // replace first and last waypoint
+ auto& wpFrom = static_cast<WayPointData&>(waypoints[0]);
+ auto& wpTo = static_cast<WayPointData&>(waypoints.back());
+
+ Vector2 fromCenter(wpFrom.point3d.x, wpFrom.point3d.y);
+ wpFrom.point3d = outPosFrom;
+ wpFrom.point2d = fromCenter - Vector2(outPosFrom.x, outPosFrom.y);
+
+ Vector2 toCenter(wpTo.point3d.x, wpTo.point3d.y);
+ wpTo.point3d = outPosTo;
+ wpTo.point2d = toCenter - Vector2(outPosTo.x, outPosTo.y);
+ wpTo.point3d = outPosTo;
+ }
+ }
+
+ // Returns waypoints with non-zero size of empty vector in case of failure (no path to be found)
+ return waypoints;
+}
+
+Scene3D::Algorithm::WayPointList PathFinderAlgorithmDjikstra::FindPath(uint32_t sourcePolyIndex, uint32_t targetPolyIndex)
+{
+ auto nodeCount = uint32_t(mNodes.size());
+ std::vector<float> dist;
+ std::vector<uint32_t> prev;
+
+ dist.resize(mNodes.size());
+ prev.resize(mNodes.size());
+
+ std::vector<FaceNode*> nodeQueue;
+ nodeQueue.reserve(nodeCount);
+
+ [[maybe_unused]] auto sourcePos = Dali::Vector3(Face(sourcePolyIndex)->center);
+
+ for(auto i = 0u; i < nodeCount; ++i)
+ {
+ dist[i] = std::numeric_limits<float>::infinity();
+ prev[i] = Scene3D::Algorithm::NavigationMesh::NULL_FACE; // set prev to null polygon
+ nodeQueue.emplace_back(&mNodes[i]);
+ }
+ auto removeCount = 0u;
+
+ // Set distance of source
+ dist[sourcePolyIndex] = 0.0f;
+
+ // TO OPTIMIZE WITH PRIORITY QUEUE
+ auto FindMinDistance = [&nodeQueue](decltype(dist)& dist)
+ {
+ float w = std::numeric_limits<float>::max();
+ int index = -1;
+ for(auto i = 0u; i < dist.size(); ++i)
+ {
+ // skip infinity
+ if(nodeQueue[i] && dist[i] != std::numeric_limits<float>::infinity() && dist[i] < w)
+ {
+ w = dist[i];
+ index = int(i);
+ }
+ }
+ return index;
+ };
+
+ do
+ {
+ // find minimum distance
+ [[maybe_unused]] auto minDistIndex = FindMinDistance(dist);
+
+ // remove from queue by assigning infinity to distance
+ removeCount++;
+ if(removeCount == nodeCount)
+ {
+ continue;
+ }
+
+ nodeQueue[minDistIndex] = nullptr;
+
+ [[maybe_unused]] auto sizeTmp = nodeQueue.size() - removeCount;
+
+ // check the neighbours
+ for(auto i = 0u; i < 3; ++i)
+ {
+ auto nIndex = mNodes[minDistIndex].faces[i];
+ if(nIndex != Scene3D::Algorithm::NavigationMesh::NULL_FACE && nodeQueue[nIndex] != nullptr)
+ {
+ auto alt = dist[minDistIndex] + mNodes[minDistIndex].weight[i];
+ if(alt < dist[nIndex])
+ {
+ dist[nIndex] = alt;
+ prev[nIndex] = minDistIndex;
+ }
+ }
+ }
+ } while(removeCount != nodeCount);
+
+ // Compute distances for each node back to the source
+ auto u = targetPolyIndex;
+ std::list<uint32_t> q;
+ if(prev[u] != Scene3D::Algorithm::NavigationMesh::NULL_FACE || u == sourcePolyIndex)
+ {
+ while(u != Scene3D::Algorithm::NavigationMesh::NULL_FACE)
+ {
+ q.push_front(u);
+ u = prev[u];
+ }
+ }
+
+ WayPointList waypoints;
+ waypoints.resize(q.size());
+
+ auto index = 0u;
+ auto prevN = 0u;
+ for(auto n : q)
+ {
+ auto& wp = static_cast<WayPointData&>(waypoints[index]);
+ wp.face = mNavigationMesh->GetFace(n);
+ wp.nodeIndex = n;
+
+ wp.edge = nullptr;
+ // set the common edge with previous node
+ if(index > 0)
+ {
+ const auto& node = mNodes[prevN];
+ for(auto i = 0u; i < 3; ++i)
+ {
+ if(node.faces[i] == wp.nodeIndex)
+ {
+ wp.edge = mNavigationMesh->GetEdge(node.edges[i]);
+ break;
+ }
+ }
+ }
+
+ prevN = n;
+ index++;
+ }
+
+ return OptimizeWaypoints(waypoints);
+}
+
+void PathFinderAlgorithmDjikstra::PrepareData()
+{
+ // Build the list structure connecting the nodes
+ auto faceCount = mNavigationMesh->GetFaceCount();
+
+ mNodes.resize(faceCount);
+
+ // for each face build the list
+ for(auto i = 0u; i < faceCount; ++i)
+ {
+ auto& node = mNodes[i];
+ const auto* face = mNavigationMesh->GetFace(i);
+ auto c0 = Dali::Vector3(face->center);
+
+ // for each edge add neighbouring face and compute distance to set the weight of node
+ for(auto edgeIndex = 0u; edgeIndex < 3; ++edgeIndex)
+ {
+ const auto* edge = mNavigationMesh->GetEdge(face->edge[edgeIndex]);
+ auto p1 = edge->face[0];
+ auto p2 = edge->face[1];
+
+ // One of faces is current face so ignore it
+ auto p = ((p1 != i) ? p1 : p2);
+ node.faces[edgeIndex] = p;
+ if(p != ::Dali::Scene3D::Algorithm::NavigationMesh::NULL_FACE)
+ {
+ node.edges[edgeIndex] = face->edge[edgeIndex];
+ auto c1 = Dali::Vector3(mNavigationMesh->GetFace(p)->center);
+ node.weight[edgeIndex] = (c1 - c0).Length();
+ }
+ }
+ }
+}
+
+[[maybe_unused]] static float ccw(const Dali::Vector2& A, const Dali::Vector2& B, const Dali::Vector2& C)
+{
+ return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x);
+}
+
+[[maybe_unused]] static bool intersect(const Dali::Vector2& A, const Dali::Vector2& B, const Dali::Vector2& C, const Dali::Vector2& D)
+{
+ return ccw(A, C, D) != ccw(B, C, D) && ccw(A, B, C) != ccw(A, B, D);
+}
+
+Scene3D::Algorithm::WayPointList PathFinderAlgorithmDjikstra::OptimizeWaypoints(WayPointList& waypoints) const
+{
+ WayPointList optimizedWaypoints;
+ optimizedWaypoints.emplace_back(waypoints[0]);
+ optimizedWaypoints.reserve(waypoints.size());
+
+ auto startIndex = 1u;
+
+ bool finished = false;
+ for(auto j = 0; !finished; ++j)
+ {
+ auto& startWaypoint = optimizedWaypoints.back();
+ const auto& startWaypointData = static_cast<const WayPointData&>(startWaypoint);
+
+ // add new-last waypoint which will be overriden as long as intersection takes place
+ optimizedWaypoints.emplace_back();
+ for(auto wpIndex = startIndex; wpIndex < waypoints.size(); ++wpIndex)
+ {
+ if(wpIndex == waypoints.size() - 1)
+ {
+ optimizedWaypoints.back() = waypoints.back();
+ finished = true;
+ continue;
+ }
+ // Points between centres of faces
+
+ const auto& wpData = static_cast<const WayPointData&>(waypoints[wpIndex]);
+
+ auto Pa0 = Dali::Vector2(startWaypointData.face->center[0], startWaypointData.face->center[1]);
+ auto Pa1 = Dali::Vector2(wpData.face->center[0], wpData.face->center[1]);
+
+ bool doesIntersect = true;
+ for(auto i = startIndex; i < wpIndex; ++i)
+ {
+ const auto& wp = static_cast<WayPointData&>(waypoints[i]);
+ // Skip starting waypoint
+ if(wp.face == startWaypointData.face)
+ {
+ continue;
+ }
+ auto Pb0 = mNavigationMesh->GetVertex(wp.edge->vertex[0]);
+ auto Pb1 = mNavigationMesh->GetVertex(wp.edge->vertex[1]);
+ auto vPb0 = Dali::Vector2(Pb0->x, Pb0->y);
+ auto vPb1 = Dali::Vector2(Pb1->x, Pb1->y);
+
+ doesIntersect = intersect(Pa0, Pa1, vPb0, vPb1);
+ if(!doesIntersect)
+ {
+ break;
+ }
+ }
+
+ if(!doesIntersect)
+ {
+ optimizedWaypoints.back() = waypoints[wpIndex - 1];
+ startIndex = wpIndex - 1;
+ break;
+ }
+ }
+ }
+
+ for(auto& wp : optimizedWaypoints)
+ {
+ auto& wpData = static_cast<WayPointData&>(wp);
+ wpData.point3d = mNavigationMesh->PointLocalToScene(Dali::Vector3(wpData.face->center));
+ wpData.point2d = Vector2::ZERO;
+ }
+
+ return optimizedWaypoints;
+}
+}
--- /dev/null
+#ifndef DALI_SCENE3D_INTERNAL_PATH_FINDER_DJIKSTRA_H
+#define DALI_SCENE3D_INTERNAL_PATH_FINDER_DJIKSTRA_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/algorithm/navigation-mesh-impl.h>
+#include <dali-scene3d/public-api/algorithm/path-finder.h>
+
+namespace Dali::Scene3D::Internal::Algorithm
+{
+class PathFinderAlgorithmDjikstra : public Dali::Scene3D::Algorithm::PathFinderBase
+{
+public:
+ /**
+ * @brief Constructor
+ *
+ * @param[in] navMesh Navigation mesh to associate with the algorithm
+ */
+ explicit PathFinderAlgorithmDjikstra(Dali::Scene3D::Algorithm::NavigationMesh& navMesh);
+
+ /**
+ * @brief Destructor
+ */
+ ~PathFinderAlgorithmDjikstra() override;
+
+ /**
+ * @brief Looks for a path from point A to point B.
+ *
+ * @param[in] positionFrom source position in NavigationMesh parent space
+ * @param[in] positionTo target position in NavigationMesh parent space
+ * @return List of waypoints for path
+ */
+ Scene3D::Algorithm::WayPointList FindPath(const Dali::Vector3& PositionFrom, const Dali::Vector3& PositionTo) override;
+
+ /**
+ * @brief Finds path between NavigationMesh faces
+ *
+ * @param[in] polyIndexFrom Index of start polygon
+ * @param[in] polyIndexTo Index of end polygon
+ * @return List of waypoints for path
+ */
+ Scene3D::Algorithm::WayPointList FindPath(uint32_t sourcePolyIndex, uint32_t targetPolyIndex) override;
+
+ [[nodiscard]] inline const NavigationMesh::Face* Face(uint32_t i) const
+ {
+ return mNavigationMesh->GetFace(i);
+ }
+
+ /**
+ * Build the graph of nodes
+ * distance between nodes is weight of node
+ */
+ void PrepareData();
+
+ Scene3D::Algorithm::WayPointList OptimizeWaypoints(Scene3D::Algorithm::WayPointList& waypoints) const;
+
+ /**
+ * Structure describes single node of pathfinding algorithm
+ */
+ struct FaceNode
+ {
+ uint32_t faceIndex; ///< Index of face which is associated with node
+
+ // neighbours
+ uint32_t faces[3]; ///< List of neighbouring faces (max 3 for a triangle)
+ uint32_t edges[3]; ///< List of edges (max 3 for a triangle)
+ float weight[3]; ///< List of weights (by distance) to each neighbour
+ };
+
+ NavigationMesh* mNavigationMesh; ///< Pointer to a valid NavigationMesh
+ std::vector<FaceNode> mNodes; ///< List of nodes
+};
+} // namespace Dali::Scene3D::Internal::Algorithm
+#endif // DALI_SCENE3D_INTERNAL_PATH_FINDER_DJIKSTRA_H
--- /dev/null
+#ifndef DALI_SCENE3D_INTERNAL_PATH_FINDER_WAYPOINT_DATA_H
+#define DALI_SCENE3D_INTERNAL_PATH_FINDER_WAYPOINT_DATA_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.
+ */
+
+// CLASS HEADER
+#include <dali-scene3d/public-api/algorithm/path-finder-waypoint.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/algorithm/navigation-mesh-impl.h>
+
+// EXTERNAL INCLUDES
+#include <cinttypes>
+
+namespace Dali::Scene3D::Internal::Algorithm
+{
+class NavigationMesh;
+
+/**
+ * Structure containing data describing single waypoint
+ *
+ * The structure stores:
+ * - node of NavigationMesh within which the waypoint is placed
+ * - a point within NavigationMesh face space (2D)
+ * - a point within NavigationMesh parent transform space (3D)
+ * Additionally, it stores data used by algorithm:
+ * - face - pointer to the face of NavigationMesh
+ * - edge - Edge between this waypoint and next on the path
+ */
+struct WayPointData
+{
+ uint32_t nodeIndex; ///< Index of the node/face
+ const NavigationMesh::Face* face; ///< Polygon containing waypoint
+ Dali::Vector2 point2d; ///< waypoint point in the polygon space, origin is a polygon centre
+ Dali::Vector3 point3d; ///< point in the 3D space in the navmesh parent space
+
+ // internal data needed for processing
+ const NavigationMesh::Edge* edge; ///< Edge between this face and next face
+};
+}
+
+#endif // DALI_SCENE3D_INTERNAL_PATH_FINDER_WAYPOINT_DATA_H
set(scene3d_internal_dir "${scene3d_dir}/internal")
set(scene3d_src_files ${scene3d_src_files}
+ ${scene3d_internal_dir}/algorithm/navigation-mesh-impl.cpp
+ ${scene3d_internal_dir}/algorithm/path-finder-djikstra.cpp
${scene3d_internal_dir}/common/environment-map-load-task.cpp
${scene3d_internal_dir}/common/model-load-task.cpp
${scene3d_internal_dir}/controls/model/model-impl.cpp
--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali-scene3d/public-api/algorithm/navigation-mesh.h>
+
+// INTERNAL HEADERS
+#include <dali-scene3d/internal/algorithm/navigation-mesh-impl.h>
+
+using Dali::Vector3;
+
+namespace Dali::Scene3D::Algorithm
+{
+
+NavigationMesh::NavigationMesh( NavigationMeshImpl* impl )
+{
+ mImpl.reset( impl );
+}
+
+NavigationMesh::~NavigationMesh() = default;
+
+[[nodiscard]] uint32_t NavigationMesh::GetFaceCount() const
+{
+ return mImpl->GetFaceCount();
+}
+
+[[nodiscard]] uint32_t NavigationMesh::GetEdgeCount() const
+{
+ return mImpl->GetEdgeCount();
+}
+
+[[nodiscard]] uint32_t NavigationMesh::GetVertexCount() const
+{
+ return mImpl->GetVertexCount();
+}
+
+bool NavigationMesh::FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition, uint32_t& polyIndex)
+{
+ return mImpl->FindFloor(position, outPosition, polyIndex);
+}
+
+bool NavigationMesh::FindFloorForFace(const Dali::Vector3& position, uint32_t faceIndex, bool dontCheckNeighbours, Dali::Vector3& outPosition)
+{
+ return mImpl->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
+}
+
+[[nodiscard]] const NavigationMesh::Face* NavigationMesh::GetFace(int index) const
+{
+ return mImpl->GetFace(index);
+}
+
+[[nodiscard]] const NavigationMesh::Edge* NavigationMesh::GetEdge(int index) const
+{
+ return mImpl->GetEdge(index);
+}
+
+[[nodiscard]] const NavigationMesh::Vertex* NavigationMesh::GetVertex(int index) const
+{
+ return mImpl->GetVertex(index);
+}
+
+void NavigationMesh::SetSceneTransform(const Dali::Matrix& transform)
+{
+ mImpl->SetTransform(transform);
+}
+
+Dali::Vector3 NavigationMesh::PointSceneToLocal(const Dali::Vector3& point)
+{
+ return mImpl->PointSceneToLocal(point);
+}
+
+Dali::Vector3 NavigationMesh::PointLocalToScene(const Dali::Vector3& point)
+{
+ return mImpl->PointLocalToScene(point);
+}
+
+Dali::Vector3 NavigationMesh::GetGravityVector() const
+{
+ return mImpl->GetGravityVector();
+}
+
+}
\ No newline at end of file
--- /dev/null
+#ifndef DALI_SCENE3D_NAVIGATION_MESH_H
+#define DALI_SCENE3D_NAVIGATION_MESH_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/api.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/math/matrix.h>
+#include <dali/public-api/math/vector3.h>
+#include <dali/public-api/math/vector4.h>
+
+#include <cinttypes>
+#include <cstdio>
+#include <vector>
+#include <memory>
+
+namespace Dali::Scene3D::Internal::Algorithm
+{
+class NavigationMesh;
+}
+
+namespace Dali::Scene3D::Loader
+{
+class NavigationMeshFactory;
+}
+
+constexpr auto NAVIGATION_MESH_MAX_VERTICES_PER_FACE = 3u;
+constexpr auto NAVIGATION_MESH_MAX_EDGES_PER_FACE = 3u;
+constexpr auto NAVIGATION_MESH_MAX_COMPONENTS_3D = 3u;
+constexpr auto NAVIGATION_MESH_MAX_COMPONENTS_2D = 2u;
+
+namespace Dali::Scene3D::Algorithm
+{
+// Using PImpling but not usual DALi handles as this object isn't supposed to be refcounted
+using NavigationMeshImpl = Dali::Scene3D::Internal::Algorithm::NavigationMesh;
+
+/**
+ * @class NavigationMesh
+ *
+ * NavigationMesh is a set of connected faces. The data contains
+ * Polygons (Polys), Edges and Vertices and describes relations
+ * between (for example, edge knows which polys are on each side).
+ *
+ * NavigationMesh uses any coordinate system that it has been exported with.
+ *
+ * The mesh is exported with gravity direction. This is because various editors
+ * may define UP vector differently. Note, the Gravity vector points DOWN.
+ *
+ * - All calculation take place in the navigation mesh local space
+ * - The NavigationMesh should use a correct transformation matrix (SetSceneTransform())
+ * - Without transform, the NavigationMesh space stays local (compatible with exporter tool)
+ * - The NavigationMesh defines Gravity vector (down)
+ * - The finding floor results are returned back into the scene space (set with SetSceneTransform()).
+ *
+ */
+class DALI_SCENE3D_API NavigationMesh
+{
+public:
+
+ /**
+ * @struct Face
+ *
+ * Describes a single polygon
+ */
+ struct Face
+ {
+ uint16_t vertex[NAVIGATION_MESH_MAX_VERTICES_PER_FACE]; ///< Vertices per face
+ uint16_t edge[NAVIGATION_MESH_MAX_EDGES_PER_FACE]; ///< Edges per face
+ float normal[NAVIGATION_MESH_MAX_COMPONENTS_3D]; ///< Normal vector
+ float center[NAVIGATION_MESH_MAX_COMPONENTS_3D]; ///< Barycentric coordinates
+ };
+
+ /**
+ * @struct Edge
+ *
+ * Describes a single edge
+ */
+ struct Edge
+ {
+ uint16_t vertex[NAVIGATION_MESH_MAX_COMPONENTS_2D]; ///< Vertices making the edge
+ uint16_t face[NAVIGATION_MESH_MAX_COMPONENTS_2D]; ///< Faces on both sides of edge
+ };
+
+ /**
+ * @struct Vertex
+ *
+ * Describes a single Vertex
+ *
+ */
+ struct Vertex
+ {
+ union
+ {
+ float co[NAVIGATION_MESH_MAX_COMPONENTS_3D]; ///< Coordinates of vertex
+ struct
+ {
+ float x, y, z;
+ };
+ };
+ };
+
+ NavigationMesh() = delete;
+
+public:
+
+ /**
+ * @brief Destructor
+ */
+ ~NavigationMesh();
+
+ /**
+ * @brief Returns total number of faces
+ *
+ * @return number of faces
+ */
+ [[nodiscard]] uint32_t GetFaceCount() const;
+
+ /**
+ * @brief Returns total number of edges
+ *
+ * @return number of edges
+ */
+ [[nodiscard]] uint32_t GetEdgeCount() const;
+
+ /**
+ * @brief Returns total number of vertices
+ *
+ * @return number of vertices
+ */
+ [[nodiscard]] uint32_t GetVertexCount() const;
+
+ /**
+ * @brief Looks for the floor under specified position
+ * @param[in] position Position to investigate
+ * @param[in] outPosition Position on the floor in found
+ * @param[in] faceIndex Index of NavigationMesh face associated with floor
+ *
+ * @return True if floor has been found, False otherwise
+ */
+ bool FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition, uint32_t& faceIndex);
+
+ /**
+ * @brief Looks for a floor starting from specified face
+ *
+ * The function performs lookup starting from the specified face. If 'dontCheckNeighbours' is 'true'
+ * then function will fail if 'position' falls outside boundries of face. If 'dontCheckNeighbours'
+ * is 'false' the function will continue search expanding onto neighbouring faces.
+ *
+ * @param[in] position Position to investigate
+ * @param[in] faceIndex Face index to start lookup
+ * @param[in] dontCheckNeighbours If true, the neighbouring faces won't be tested
+ * @param[out] outPosition Result of lookup
+ *
+ * @return True on success, false otherwise
+ */
+ bool FindFloorForFace(const Dali::Vector3& position, uint32_t faceIndex, bool dontCheckNeighbours, Dali::Vector3& outPosition);
+
+
+ /**
+ * @brief Returns pointer to Face structure
+ * @param[in] index Index of face to retrieve
+ * @return Pointer to valid Face structure or nullptr
+ */
+ [[nodiscard]] const Face* GetFace(int index) const;
+
+ /**
+ * @brief Returns edge structure
+ * @param[in] index Index of edge to retrieve
+ * @return Pointer to valid Edge structure or nullptr
+ */
+ [[nodiscard]] const Edge* GetEdge(int index) const;
+
+ /**
+ * @brief Returns vertex structure
+ * @param[in] index Index of vertex to retrieve
+ * @return Pointer to valid Vertex structure or nullptr
+ */
+ [[nodiscard]] const Vertex* GetVertex(int index) const;
+
+ /**
+ * @brief Sets static transform for the navigation mesh object
+ *
+ * The NavigationMesh may require to be transformed into the coordinates
+ * of the scene object. The exporter exports navigation geometry in a local
+ * space. The transform must be set in order to use the navigation mesh
+ * in the scene space (most likely DALi coordinate space).
+ *
+ * The scene transform matrix can be set in the DALi event thread using
+ * Dali::DevelActor::GetWorldTransform(sceneActor)
+ *
+ * For example:
+ * @code
+ * Actor parentActorOfNavigationMesh; // non-null object
+ * Dali::DevelActor::GetWorldTransform(parentActorOfNavigationMesh);
+ * navigationMesh->SetSceneTransform(parentActorOfNavigationMesh);
+ * @endcode
+ *
+ * The transform remains static until changed by calling SetSceneTransform() again.
+ * It means that if the matrix is obtained from the actor and actor transform will
+ * change the navigation mesh won't be aligned anymore.
+ *
+ * @param[in] transform Valid transform 4x4 matrix
+ */
+ void SetSceneTransform(const Dali::Matrix& transform);
+
+ /**
+ * @brief transforms point into the NavigationMesh local space
+ *
+ * Transforms a 3D point into navigation mesh space (space used when
+ * NavigationMesh has been created, most likely 3D editor space).
+ *
+ * @param[in] point Point to transform
+ * @return Point transformed to the local space
+ */
+ Dali::Vector3 PointSceneToLocal(const Dali::Vector3& point);
+
+ /**
+ * @brief Transforms point into the parent transform space
+ *
+ * Transforms the given point into the parent space (set with SetSceneTransform()).
+ *
+ * @param[in] point Point to transform
+ * @return Point transformed into the parent space
+ */
+ Dali::Vector3 PointLocalToScene(const Dali::Vector3& point);
+
+ /**
+ * @brief Returns direction of the gravity vector
+ *
+ * Gravity vector points down.
+ *
+ * @return Gravity vector 3D
+ */
+ Dali::Vector3 GetGravityVector() const;
+
+ static constexpr uint16_t NULL_FACE{0xffff}; ///< Represents null polygon
+ static constexpr uint16_t NULL_EDGE{0xffff}; ///< represents null edge
+
+public:
+
+ DALI_INTERNAL explicit NavigationMesh( NavigationMeshImpl* impl );
+
+ std::unique_ptr<NavigationMeshImpl> mImpl;
+};
+} // namespace Dali::Scene3D::Algorithm
+#endif // DALI_SCENE3D_NAVIGATION_MESH_H
--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali-scene3d/public-api/algorithm/path-finder-waypoint.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/algorithm/navigation-mesh-impl.h>
+#include <dali-scene3d/internal/algorithm/path-finder-waypoint-data.h>
+
+// EXTERNAL INCLUDES
+#include <cinttypes>
+
+namespace Dali::Scene3D::Algorithm
+{
+
+WayPoint::WayPoint()
+{
+ mImpl = std::make_unique<Scene3D::Internal::Algorithm::WayPointData>();
+}
+
+WayPoint::~WayPoint() = default;
+
+uint32_t WayPoint::GetNavigationMeshFaceIndex() const
+{
+ return mImpl->nodeIndex;
+}
+
+Dali::Vector2 WayPoint::GetFaceLocalSpacePosition() const
+{
+ return mImpl->point2d;
+}
+
+Dali::Vector3 WayPoint::GetScenePosition() const
+{
+ return mImpl->point3d;
+}
+
+WayPoint::WayPoint(const WayPoint& wp)
+{
+ mImpl = std::make_unique<Internal::Algorithm::WayPointData>();
+ *mImpl = *wp.mImpl;
+}
+
+WayPoint& WayPoint::operator=(const WayPoint& wp)
+{
+ mImpl = std::make_unique<Internal::Algorithm::WayPointData>();
+ *mImpl = *wp.mImpl;
+ return *this;
+}
+
+WayPoint::operator Internal::Algorithm::WayPointData&()
+{
+ return *mImpl;
+}
+
+}
--- /dev/null
+#ifndef DALI_SCENE3D_PATH_FINDER_WAYPOINT_H
+#define DALI_SCENE3D_PATH_FINDER_WAYPOINT_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/api.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/math/vector2.h>
+#include <dali/public-api/math/vector3.h>
+
+#include <cinttypes>
+#include <memory>
+
+namespace Dali::Scene3D::Internal::Algorithm
+{
+class WayPointData;
+}
+
+namespace Dali::Scene3D::Algorithm
+{
+/**
+ * @class WayPoint
+ *
+ * The class represents a public interface to the WayPoint object
+ */
+class DALI_SCENE3D_API WayPoint
+{
+public:
+
+ /**
+ * @brief Constructor
+ */
+ WayPoint();
+
+ /**
+ * @brief Destructor
+ */
+ ~WayPoint();
+
+ /**
+ * @brief Returns index of bounding face within the NavigationMesh
+ *
+ * Function returns index of face withing the NavigationMesh
+ * that the waypoint is associated with.
+ *
+ * @return Valid index of the face
+ */
+ [[nodiscard]] uint32_t GetNavigationMeshFaceIndex() const;
+
+ /**
+ * @brief Returns local 2D position in face space
+ *
+ * The face space uses the face barycentre as an origin. The x-axis is
+ * aligned with x-axis of the NavigationMesh.
+ *
+ * @return Valid 2D location vector
+ */
+ [[nodiscard]] Dali::Vector2 GetFaceLocalSpacePosition() const;
+
+ /**
+ * @brief Returns waypoint 3D position in scene space
+ *
+ * Returns the 3D position of the waypoint in the scene space
+ * of associated NavigationMesh object (using transformation set with
+ * NavigationMesh::SetSceneTransform()).
+ *
+ * @return Valid 3D location vector
+ */
+ [[nodiscard]] Dali::Vector3 GetScenePosition() const;
+
+ /**
+ * @brief Copy constructor
+ *
+ * Only copy semantics is allowed on the WayPoint object
+ */
+ WayPoint(const WayPoint&);
+
+ /**
+ * @brief Copy assignment operator
+ *
+ * Only copy semantics is allowed on the WayPoint object
+ *
+ * @return Copy of source object
+ */
+ WayPoint& operator=(const WayPoint&);
+
+private:
+
+ std::unique_ptr<Internal::Algorithm::WayPointData> mImpl;
+
+public:
+
+ DALI_INTERNAL operator Internal::Algorithm::WayPointData&();
+
+};
+}
+
+#endif // DALI_SCENE3D_PATH_FINDER_WAYPOINT_H
--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali-scene3d/public-api/algorithm/path-finder.h>
+
+// default algorithm
+#include <dali-scene3d/internal/algorithm/path-finder-djikstra.h>
+
+namespace Dali::Scene3D::Algorithm
+{
+std::unique_ptr<PathFinder> PathFinder::New(NavigationMesh& navigationMesh, PathFinderAlgorithm algorithm)
+{
+ PathFinderBase* impl = nullptr;
+
+ if(algorithm == PathFinderAlgorithm::DJIKSTRA_SHORTEST_PATH)
+ {
+ impl = new Dali::Scene3D::Internal::Algorithm::PathFinderAlgorithmDjikstra(navigationMesh);
+ }
+
+ if(!impl)
+ {
+ return {};
+ }
+
+ auto retval = std::unique_ptr<PathFinderBase>();
+ retval.reset(impl);
+ return std::unique_ptr<Algorithm::PathFinder>(new Algorithm::PathFinder(std::move(retval)));
+}
+
+WayPointList PathFinder::FindPath(const Dali::Vector3& positionFrom, const Dali::Vector3& positionTo)
+{
+ return mImpl->FindPath( positionFrom, positionTo );
+}
+
+WayPointList PathFinder::FindPath(uint32_t polyIndexFrom, uint32_t polyIndexTo)
+{
+ return mImpl->FindPath( polyIndexFrom, polyIndexTo );
+}
+
+
+PathFinder::PathFinder(std::unique_ptr<PathFinderBase>&& baseImpl)
+{
+ mImpl = std::move(baseImpl);
+}
+
+
+} // namespace Dali::Scene3D::Algorithm
--- /dev/null
+#ifndef DALI_SCENE3D_PATH_FINDER_H
+#define DALI_SCENE3D_PATH_FINDER_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/api.h>
+#include <dali-scene3d/public-api/algorithm/navigation-mesh.h>
+#include <dali-scene3d/public-api/algorithm/path-finder-waypoint.h>
+
+namespace Dali::Scene3D::Algorithm
+{
+
+using WayPointList = std::vector<Scene3D::Algorithm::WayPoint>;
+
+/**
+ * List of enums to be used when not using custom implementation
+ * of path finding.
+ */
+enum class PathFinderAlgorithm
+{
+ DJIKSTRA_SHORTEST_PATH, ///< Using A* variant (Djikstra) finding a shortest path
+ DEFAULT = DJIKSTRA_SHORTEST_PATH, ///< Default algorithm to use
+};
+
+/**
+ * @class PathFinderBase
+ *
+ * Base class for implementation of pathfinding algorithms.
+ */
+class DALI_SCENE3D_API PathFinderBase
+{
+public:
+
+ /**
+ * @brief Destructor
+ */
+ virtual ~PathFinderBase() = default;
+
+ /**
+ * @brief Looks for a path from point A to point B.
+ *
+ * @param[in] positionFrom source position in NavigationMesh parent space
+ * @param[in] positionTo target position in NavigationMesh parent space
+ * @return List of waypoints for path or empty vector if no success
+ */
+ virtual WayPointList FindPath(const Dali::Vector3& positionFrom, const Dali::Vector3& positionTo) = 0;
+
+ /**
+ * @brief Finds path between NavigationMesh faces
+ *
+ * @param[in] polyIndexFrom Index of start polygon
+ * @param[in] polyIndexTo Index of end polygon
+ * @return List of waypoints for path or empty vector if no success
+ */
+ virtual WayPointList FindPath(uint32_t polyIndexFrom, uint32_t polyIndexTo) = 0;
+};
+
+/**
+ * @class PathFinder
+ *
+ * PathFinder runs path finding algorithm on associated NavigationMesh
+ * and returns a list of waypoints.
+ */
+class DALI_SCENE3D_API PathFinder
+{
+public:
+
+ /**
+ * @brief Creates new instance of path finder
+ * @param[in] navigationMesh Navigation mesh to associate with
+ * @param[in] algorithm algorithm to use
+ * @return Valid pointer to PathFinder object or nullptr
+ */
+ static std::unique_ptr<PathFinder> New( NavigationMesh& navigationMesh, PathFinderAlgorithm algorithm );
+
+ /**
+ * @brief Looks for a path from point A to point B.
+ *
+ * The function looks for the path between point A (positionFrom) and B (positionTo). It runs
+ * the algorithm on the associated NavigationMesh and automatically looks for the floor point.
+ *
+ * It will fail if:
+ * - Any point is outside the navigation mesh
+ * - The path doesn't exist
+ *
+ * Both points should be defined in the same space as is used by the NavigationMesh.
+ *
+ * @param[in] positionFrom Source position
+ * @param[in] positionTo Target position
+ * @return List of waypoints for path or empty list on failure
+ */
+ WayPointList FindPath(const Dali::Vector3& positionFrom, const Dali::Vector3& positionTo);
+
+ /**
+ * @brief Looks for a path between specified NavigationMesh faces
+ *
+ * The function looks for the path between given faces (provided as indices).
+ *
+ * It will fail if:
+ * - index < 0 or index > NavigationMesh::GetFaceCount()
+ * - The path doesn't exist
+ *
+ * @param[in] faceIndexFrom Source face index
+ * @param[in] faceIndexTo Target face index
+ * @return List of waypoints for path or empty list on failure
+ */
+ WayPointList FindPath(uint32_t faceIndexFrom, uint32_t faceIndexTo);
+
+private:
+
+ PathFinder() = delete;
+
+ DALI_INTERNAL explicit PathFinder(std::unique_ptr<PathFinderBase>&& baseImpl);
+
+ std::unique_ptr<PathFinderBase> mImpl;
+};
+
+}
+
+#endif // DALI_SCENE3D_PATH_FINDER_H
set(scene3d_public_api_dir "${scene3d_dir}/public-api")
set(scene3d_src_files ${scene3d_src_files}
+ ${scene3d_public_api_dir}/algorithm/navigation-mesh.cpp
+ ${scene3d_public_api_dir}/algorithm/path-finder.cpp
+ ${scene3d_public_api_dir}/algorithm/path-finder-waypoint.cpp
${scene3d_public_api_dir}/controls/model/model.cpp
${scene3d_public_api_dir}/controls/scene-view/scene-view.cpp
${scene3d_public_api_dir}/loader/alpha-function-helper.cpp
${scene3d_public_api_dir}/loader/material-definition.cpp
${scene3d_public_api_dir}/loader/matrix-stack.cpp
${scene3d_public_api_dir}/loader/mesh-definition.cpp
+ ${scene3d_public_api_dir}/loader/navigation-mesh-factory.cpp
${scene3d_public_api_dir}/loader/node-definition.cpp
${scene3d_public_api_dir}/loader/parse-renderer-state.cpp
${scene3d_public_api_dir}/loader/renderer-state.cpp
--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali-scene3d/public-api/loader/navigation-mesh-factory.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/algorithm/navigation-mesh-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+
+namespace Dali::Scene3D::Loader
+{
+
+std::unique_ptr<Algorithm::NavigationMesh> NavigationMeshFactory::CreateFromFile(std::string filename)
+{
+ std::vector<uint8_t> buffer;
+ auto fin = fopen(filename.c_str(), "rb");
+ if(!fin)
+ {
+ DALI_LOG_ERROR("NavigationMesh: Can't open %s for reading: %s", filename.c_str(), strerror(errno));
+ return nullptr;
+ }
+ else
+ {
+ fseek(fin, 0, SEEK_END);
+ auto size = ftell(fin);
+ fseek(fin, 0, SEEK_SET);
+ buffer.resize(size);
+ auto count = fread(buffer.data(), 1, size, fin);
+ if(!count)
+ {
+ DALI_LOG_ERROR("NavigationMesh: Error reading file: %s\n", filename.c_str());
+ fclose(fin);
+ return nullptr;
+ }
+ fclose(fin);
+
+ return CreateFromBuffer( buffer );
+ }
+}
+
+std::unique_ptr<Algorithm::NavigationMesh> NavigationMeshFactory::CreateFromBuffer(const std::vector<uint8_t>& buffer)
+{
+ auto impl = new Scene3D::Internal::Algorithm::NavigationMesh(buffer);
+ return std::unique_ptr<Algorithm::NavigationMesh>( new Algorithm::NavigationMesh(impl));
+}
+
+}
\ No newline at end of file
--- /dev/null
+#ifndef DALI_SCENE3D_LOADER_NAVIGATION_MESH_FACTORY_H
+#define DALI_SCENE3D_LOADER_NAVIGATION_MESH_FACTORY_H
+
+/*
+* Copyright (c) 2023 Samsung Electronics Co., Ltd.
+
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+// INTERNAL INCLUDES
+#include "dali-scene3d/public-api/api.h"
+#include <dali-scene3d/public-api/algorithm/navigation-mesh.h>
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/rendering/geometry.h"
+#include "dali/public-api/rendering/texture.h"
+
+namespace Dali
+{
+namespace Scene3D
+{
+namespace Loader
+{
+struct DALI_SCENE3D_API NavigationMeshFactory
+{
+public:
+ /**
+ * @brief Creates NavigationMesh object from file
+ *
+ * @param[in] filename file to load
+ * @return Valid NavigationMesh or nullptr
+ */
+ static std::unique_ptr<Algorithm::NavigationMesh> CreateFromFile(std::string filename);
+
+ /**
+ * @brief Creates NavigationMesh object from binary buffer
+ *
+ * @param[in] buffer buffer with data
+ * @return Valid NavigationMesh or nullptr
+ */
+ static std::unique_ptr<Algorithm::NavigationMesh> CreateFromBuffer(const std::vector<uint8_t>& buffer);
+
+};
+}
+}
+}
+
+#endif // DALI_SCENE3D_INTERNAL_LOADER_NAVIGATION_MESH_FACTORY_H