Merge "Remove useless warning message when multiline TextLabel SceneOff" into devel...
authorEunki Hong <eunkiki.hong@samsung.com>
Mon, 30 Jan 2023 09:58:08 +0000 (09:58 +0000)
committerGerrit Code Review <gerrit@review>
Mon, 30 Jan 2023 09:58:08 +0000 (09:58 +0000)
24 files changed:
.gitignore
automated-tests/resources/navmesh-test.bin [new file with mode: 0755]
automated-tests/src/dali-scene3d/CMakeLists.txt
automated-tests/src/dali-scene3d/utc-Dali-NavigationMesh.cpp [new file with mode: 0644]
automated-tests/src/dali-scene3d/utc-Dali-PathFinding.cpp [new file with mode: 0644]
build/tizen/CMakeLists.txt
build/tizen/docs-internal/dali-internal.doxy.in
build/tizen/docs/dali.doxy.in
dali-scene3d/internal/algorithm/navigation-mesh-header.h [new file with mode: 0644]
dali-scene3d/internal/algorithm/navigation-mesh-impl.cpp [new file with mode: 0644]
dali-scene3d/internal/algorithm/navigation-mesh-impl.h [new file with mode: 0644]
dali-scene3d/internal/algorithm/path-finder-djikstra.cpp [new file with mode: 0644]
dali-scene3d/internal/algorithm/path-finder-djikstra.h [new file with mode: 0644]
dali-scene3d/internal/algorithm/path-finder-waypoint-data.h [new file with mode: 0644]
dali-scene3d/internal/file.list
dali-scene3d/public-api/algorithm/navigation-mesh.cpp [new file with mode: 0644]
dali-scene3d/public-api/algorithm/navigation-mesh.h [new file with mode: 0644]
dali-scene3d/public-api/algorithm/path-finder-waypoint.cpp [new file with mode: 0644]
dali-scene3d/public-api/algorithm/path-finder-waypoint.h [new file with mode: 0644]
dali-scene3d/public-api/algorithm/path-finder.cpp [new file with mode: 0644]
dali-scene3d/public-api/algorithm/path-finder.h [new file with mode: 0644]
dali-scene3d/public-api/file.list
dali-scene3d/public-api/loader/navigation-mesh-factory.cpp [new file with mode: 0644]
dali-scene3d/public-api/loader/navigation-mesh-factory.h [new file with mode: 0644]

index e4e5fe8..27436e2 100644 (file)
@@ -1,21 +1,19 @@
+# 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
@@ -37,17 +35,30 @@ libdali2-toolkit.so*
 .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/*
diff --git a/automated-tests/resources/navmesh-test.bin b/automated-tests/resources/navmesh-test.bin
new file mode 100755 (executable)
index 0000000..f58f46b
Binary files /dev/null and b/automated-tests/resources/navmesh-test.bin differ
index 1f80def..707bdd5 100755 (executable)
@@ -22,7 +22,9 @@ SET(TC_SOURCES
   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
diff --git a/automated-tests/src/dali-scene3d/utc-Dali-NavigationMesh.cpp b/automated-tests/src/dali-scene3d/utc-Dali-NavigationMesh.cpp
new file mode 100644 (file)
index 0000000..ec9b0bc
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * 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
diff --git a/automated-tests/src/dali-scene3d/utc-Dali-PathFinding.cpp b/automated-tests/src/dali-scene3d/utc-Dali-PathFinding.cpp
new file mode 100644 (file)
index 0000000..c75074a
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * 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
index 6313fc9..a1f4efd 100644 (file)
@@ -34,7 +34,7 @@ OPTION(ENABLE_LINK_TEST          "Enable the link test" ON)
 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 )
@@ -565,6 +565,17 @@ IF( DOXYGEN_FOUND )
   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 )
index 1807df0..cfb7335 100644 (file)
@@ -1,4 +1,8 @@
-# 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.
@@ -357,6 +361,8 @@ ALIASES += SINCE_1_3="@since 1.3"
 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"
@@ -385,6 +391,8 @@ ALIASES += DEPRECATED_1_3_39="@deprecated Deprecated since 1.3.39"
 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=""
@@ -404,7 +412,9 @@ ALIASES += REMARK_RAWVIDEO=""
 #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"
@@ -434,6 +444,9 @@ ALIASES += REMARK_RAWVIDEO=""
 #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"
index 921ee6e..99eb3e4 100644 (file)
@@ -1,9 +1,6 @@
-# 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.
@@ -238,7 +235,7 @@ MULTILINE_CPP_IS_BRIEF = NO
 # 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.
@@ -690,7 +687,7 @@ LOOKUP_CACHE_SIZE      = 0
 # 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
@@ -760,7 +757,7 @@ EXTRACT_ANON_NSPACES   = NO
 # 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
@@ -1353,7 +1350,7 @@ CLANG_ASSISTED_PARSING = NO
 # 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
@@ -1806,7 +1803,7 @@ EXT_LINKS_IN_WINDOW    = NO
 # 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
@@ -2639,7 +2636,7 @@ UML_LIMIT_NUM_FIELDS = 10
 # 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
@@ -2648,7 +2645,7 @@ UML_LIMIT_NUM_FIELDS = 10
 # 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
diff --git a/dali-scene3d/internal/algorithm/navigation-mesh-header.h b/dali-scene3d/internal/algorithm/navigation-mesh-header.h
new file mode 100644 (file)
index 0000000..812cdb1
--- /dev/null
@@ -0,0 +1,58 @@
+#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
diff --git a/dali-scene3d/internal/algorithm/navigation-mesh-impl.cpp b/dali-scene3d/internal/algorithm/navigation-mesh-impl.cpp
new file mode 100644 (file)
index 0000000..7e78094
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * 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
diff --git a/dali-scene3d/internal/algorithm/navigation-mesh-impl.h b/dali-scene3d/internal/algorithm/navigation-mesh-impl.h
new file mode 100644 (file)
index 0000000..2ba1b6d
--- /dev/null
@@ -0,0 +1,174 @@
+#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
diff --git a/dali-scene3d/internal/algorithm/path-finder-djikstra.cpp b/dali-scene3d/internal/algorithm/path-finder-djikstra.cpp
new file mode 100644 (file)
index 0000000..0da6568
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// 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;
+}
+}
diff --git a/dali-scene3d/internal/algorithm/path-finder-djikstra.h b/dali-scene3d/internal/algorithm/path-finder-djikstra.h
new file mode 100644 (file)
index 0000000..21bd316
--- /dev/null
@@ -0,0 +1,89 @@
+#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
diff --git a/dali-scene3d/internal/algorithm/path-finder-waypoint-data.h b/dali-scene3d/internal/algorithm/path-finder-waypoint-data.h
new file mode 100644 (file)
index 0000000..53df123
--- /dev/null
@@ -0,0 +1,56 @@
+#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
index d264857..30ebe95 100644 (file)
@@ -1,6 +1,8 @@
 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
diff --git a/dali-scene3d/public-api/algorithm/navigation-mesh.cpp b/dali-scene3d/public-api/algorithm/navigation-mesh.cpp
new file mode 100644 (file)
index 0000000..45225ae
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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
diff --git a/dali-scene3d/public-api/algorithm/navigation-mesh.h b/dali-scene3d/public-api/algorithm/navigation-mesh.h
new file mode 100644 (file)
index 0000000..12f3387
--- /dev/null
@@ -0,0 +1,262 @@
+#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
diff --git a/dali-scene3d/public-api/algorithm/path-finder-waypoint.cpp b/dali-scene3d/public-api/algorithm/path-finder-waypoint.cpp
new file mode 100644 (file)
index 0000000..9254e04
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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;
+}
+
+}
diff --git a/dali-scene3d/public-api/algorithm/path-finder-waypoint.h b/dali-scene3d/public-api/algorithm/path-finder-waypoint.h
new file mode 100644 (file)
index 0000000..201783c
--- /dev/null
@@ -0,0 +1,114 @@
+#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
diff --git a/dali-scene3d/public-api/algorithm/path-finder.cpp b/dali-scene3d/public-api/algorithm/path-finder.cpp
new file mode 100644 (file)
index 0000000..42d227f
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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
diff --git a/dali-scene3d/public-api/algorithm/path-finder.h b/dali-scene3d/public-api/algorithm/path-finder.h
new file mode 100644 (file)
index 0000000..e1791bc
--- /dev/null
@@ -0,0 +1,135 @@
+#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
index 3fc4638..a6a24cb 100644 (file)
@@ -1,6 +1,9 @@
 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
@@ -21,6 +24,7 @@ set(scene3d_src_files ${scene3d_src_files}
        ${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
diff --git a/dali-scene3d/public-api/loader/navigation-mesh-factory.cpp b/dali-scene3d/public-api/loader/navigation-mesh-factory.cpp
new file mode 100644 (file)
index 0000000..9d03f0d
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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
diff --git a/dali-scene3d/public-api/loader/navigation-mesh-factory.h b/dali-scene3d/public-api/loader/navigation-mesh-factory.h
new file mode 100644 (file)
index 0000000..0bbe8a9
--- /dev/null
@@ -0,0 +1,58 @@
+#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