From: Eunki Hong Date: Mon, 30 Jan 2023 09:58:08 +0000 (+0000) Subject: Merge "Remove useless warning message when multiline TextLabel SceneOff" into devel... X-Git-Tag: dali_2.2.12~5 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=21f77bcfb81c2249df4ba0b1f9b716fbd198d6c9;hp=0cdb6b8ce5b68d39841d2d25a2bccb8687d6ae8f Merge "Remove useless warning message when multiline TextLabel SceneOff" into devel/master --- diff --git a/.gitignore b/.gitignore index e4e5fe8..27436e2 100644 --- a/.gitignore +++ b/.gitignore @@ -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 index 0000000..f58f46b Binary files /dev/null and b/automated-tests/resources/navmesh-test.bin differ diff --git a/automated-tests/src/dali-scene3d/CMakeLists.txt b/automated-tests/src/dali-scene3d/CMakeLists.txt index 1f80def..707bdd5 100755 --- a/automated-tests/src/dali-scene3d/CMakeLists.txt +++ b/automated-tests/src/dali-scene3d/CMakeLists.txt @@ -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 index 0000000..ec9b0bc --- /dev/null +++ b/automated-tests/src/dali-scene3d/utc-Dali-NavigationMesh.cpp @@ -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 + +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 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 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 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 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 inPositions; + std::vector expectedPositions; + std::vector expectedFaceIndex; + std::vector 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 index 0000000..c75074a --- /dev/null +++ b/automated-tests/src/dali-scene3d/utc-Dali-PathFinding.cpp @@ -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 + +using namespace Dali; +using namespace Dali::Scene3D::Algorithm; +using namespace Dali::Scene3D::Loader; + +bool CompareResults( const std::vector& 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(-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 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 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 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 diff --git a/build/tizen/CMakeLists.txt b/build/tizen/CMakeLists.txt index 6313fc9..a1f4efd 100644 --- a/build/tizen/CMakeLists.txt +++ b/build/tizen/CMakeLists.txt @@ -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 ) diff --git a/build/tizen/docs-internal/dali-internal.doxy.in b/build/tizen/docs-internal/dali-internal.doxy.in index 1807df0..cfb7335 100644 --- a/build/tizen/docs-internal/dali-internal.doxy.in +++ b/build/tizen/docs-internal/dali-internal.doxy.in @@ -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" diff --git a/build/tizen/docs/dali.doxy.in b/build/tizen/docs/dali.doxy.in index 921ee6e..99eb3e4 100644 --- a/build/tizen/docs/dali.doxy.in +++ b/build/tizen/docs/dali.doxy.in @@ -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 index 0000000..812cdb1 --- /dev/null +++ b/dali-scene3d/internal/algorithm/navigation-mesh-header.h @@ -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 + +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 index 0000000..7e78094 --- /dev/null +++ b/dali-scene3d/internal/algorithm/navigation-mesh-impl.cpp @@ -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 + +// EXTERNAL INCLUDES +#include + +#include +#include +#include +#include +#include + +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& buffer) +{ + mBuffer.resize(buffer.size()); + std::copy(buffer.begin(), buffer.end(), mBuffer.begin()); + + // Setup header from the buffer + mHeader = *reinterpret_cast(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 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 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(mBuffer.data() + mHeader.dataOffset + mHeader.polyDataOffset); + return &basePtr[index]; +} + +const Edge* NavigationMesh::GetEdge(int index) const +{ + auto* basePtr = reinterpret_cast(mBuffer.data() + mHeader.dataOffset + mHeader.edgeDataOffset); + return &basePtr[index]; +} + +const Vertex* NavigationMesh::GetVertex(int index) const +{ + auto* basePtr = reinterpret_cast(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 index 0000000..2ba1b6d --- /dev/null +++ b/dali-scene3d/internal/algorithm/navigation-mesh-impl.h @@ -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 +#include + +// INTERNAL EXTERNAL +#include +#include +#include +#include + +#include +#include +#include +#include +#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& 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 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 index 0000000..0da6568 --- /dev/null +++ b/dali-scene3d/internal/algorithm/path-finder-djikstra.cpp @@ -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 +#include +#include + +// EXTERNAL INCLUDES +#include +#include + +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(waypoints[0]); + auto& wpTo = static_cast(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 dist; + std::vector prev; + + dist.resize(mNodes.size()); + prev.resize(mNodes.size()); + + std::vector 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::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::max(); + int index = -1; + for(auto i = 0u; i < dist.size(); ++i) + { + // skip infinity + if(nodeQueue[i] && dist[i] != std::numeric_limits::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 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(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(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(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(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(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 index 0000000..21bd316 --- /dev/null +++ b/dali-scene3d/internal/algorithm/path-finder-djikstra.h @@ -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 +#include + +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 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 index 0000000..53df123 --- /dev/null +++ b/dali-scene3d/internal/algorithm/path-finder-waypoint-data.h @@ -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 + +// INTERNAL INCLUDES +#include + +// EXTERNAL INCLUDES +#include + +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 diff --git a/dali-scene3d/internal/file.list b/dali-scene3d/internal/file.list index d264857..30ebe95 100644 --- a/dali-scene3d/internal/file.list +++ b/dali-scene3d/internal/file.list @@ -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 index 0000000..45225ae --- /dev/null +++ b/dali-scene3d/public-api/algorithm/navigation-mesh.cpp @@ -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 + +// INTERNAL HEADERS +#include + +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 index 0000000..12f3387 --- /dev/null +++ b/dali-scene3d/public-api/algorithm/navigation-mesh.h @@ -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 + +// EXTERNAL INCLUDES +#include +#include +#include + +#include +#include +#include +#include + +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 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 index 0000000..9254e04 --- /dev/null +++ b/dali-scene3d/public-api/algorithm/path-finder-waypoint.cpp @@ -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 + +// INTERNAL INCLUDES +#include +#include + +// EXTERNAL INCLUDES +#include + +namespace Dali::Scene3D::Algorithm +{ + +WayPoint::WayPoint() +{ + mImpl = std::make_unique(); +} + +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(); + *mImpl = *wp.mImpl; +} + +WayPoint& WayPoint::operator=(const WayPoint& wp) +{ + mImpl = std::make_unique(); + *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 index 0000000..201783c --- /dev/null +++ b/dali-scene3d/public-api/algorithm/path-finder-waypoint.h @@ -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 + +// EXTERNAL INCLUDES +#include +#include + +#include +#include + +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 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 index 0000000..42d227f --- /dev/null +++ b/dali-scene3d/public-api/algorithm/path-finder.cpp @@ -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 + +// default algorithm +#include + +namespace Dali::Scene3D::Algorithm +{ +std::unique_ptr 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(); + retval.reset(impl); + return std::unique_ptr(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&& 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 index 0000000..e1791bc --- /dev/null +++ b/dali-scene3d/public-api/algorithm/path-finder.h @@ -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 +#include +#include + +namespace Dali::Scene3D::Algorithm +{ + +using WayPointList = std::vector; + +/** + * 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 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&& baseImpl); + + std::unique_ptr mImpl; +}; + +} + +#endif // DALI_SCENE3D_PATH_FINDER_H diff --git a/dali-scene3d/public-api/file.list b/dali-scene3d/public-api/file.list index 3fc4638..a6a24cb 100644 --- a/dali-scene3d/public-api/file.list +++ b/dali-scene3d/public-api/file.list @@ -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 index 0000000..9d03f0d --- /dev/null +++ b/dali-scene3d/public-api/loader/navigation-mesh-factory.cpp @@ -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 + +// INTERNAL INCLUDES +#include + +// EXTERNAL INCLUDES +#include + +namespace Dali::Scene3D::Loader +{ + +std::unique_ptr NavigationMeshFactory::CreateFromFile(std::string filename) +{ + std::vector 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 NavigationMeshFactory::CreateFromBuffer(const std::vector& buffer) +{ + auto impl = new Scene3D::Internal::Algorithm::NavigationMesh(buffer); + return std::unique_ptr( 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 index 0000000..0bbe8a9 --- /dev/null +++ b/dali-scene3d/public-api/loader/navigation-mesh-factory.h @@ -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 + +// 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 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 CreateFromBuffer(const std::vector& buffer); + +}; +} +} +} + +#endif // DALI_SCENE3D_INTERNAL_LOADER_NAVIGATION_MESH_FACTORY_H