2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include "dali-scene3d/public-api/algorithm/navigation-mesh.h"
19 #include "dali-scene3d/public-api/loader/navigation-mesh-factory.h"
20 #include <dali-test-suite-utils.h>
23 using namespace Dali::Scene3D::Algorithm;
24 using namespace Dali::Scene3D::Loader;
27 * SysOverride allows overriding a system symbol and
28 * set the return value for n-th call of it.
30 * After invoking the symbol override is disabled.
32 template<class R, class F>
35 SysOverride(const char* funcName)
37 funcNameStr = funcName;
40 func = decltype(func)(dlsym(RTLD_NEXT, funcName));
44 void SetReturnValue( R value, uint32_t n )
48 tet_infoline("Warning! Overriding return value is already enabled! Ignoring!\n");
53 overrideEnabled = true;
56 template<class... Args>
57 R Invoke(Args&&... args)
59 auto retval = func(args...);
64 overrideEnabled = false;
72 std::string funcNameStr;
75 uint32_t overrideCounter = 0;
76 bool overrideEnabled = false;
80 static thread_local SysOverride<int, decltype(fseek)> call_fseek("fseek");
81 extern "C" int fseek (FILE *s, long int o, int w)
83 return call_fseek.Invoke( s, o, w );
87 static thread_local SysOverride<int, decltype(ftell)> call_ftell("ftell");
88 extern "C" long int ftell(FILE *s)
90 return call_ftell.Invoke( s );
94 static thread_local SysOverride<int, decltype(fread)> call_fread("fread");
95 extern "C" size_t fread (void *__restrict p, size_t s, size_t n, FILE *__restrict st)
97 return call_fread.Invoke(p, s, n, st);
100 int UtcDaliNavigationMeshCreateFromFileFail1(void)
102 tet_infoline("UtcDaliNavigationMeshCreateFromFileFail1: Fails to create navigation mesh from file");
104 // No such file, misspelled name
105 auto result = NavigationMeshFactory::CreateFromFile("notexisting.bin");
107 DALI_TEST_CHECK(result == nullptr);
112 int UtcDaliNavigationMeshCreateFromFileFail2(void)
114 tet_infoline("UtcDaliNavigationMeshCreateFromFileFail2: Fails to create navigation mesh using file");
116 // Override next fseek to fail
117 call_fseek.SetReturnValue(-1, 0);
118 auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
120 DALI_TEST_CHECK(result == nullptr);
125 int UtcDaliNavigationMeshCreateFromFileFail3(void)
127 tet_infoline("UtcDaliNavigationMeshCreateFromFileFail3: Fails to create navigation mesh using file");
129 // Override next ftell to fail
130 call_ftell.SetReturnValue(-1, 0);
131 auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
133 DALI_TEST_CHECK(result == nullptr);
138 int UtcDaliNavigationMeshCreateFromFileFail4(void)
140 tet_infoline("UtcDaliNavigationMeshCreateFromFileFail4: Fails to create navigation mesh using file");
142 // Override 2nd fseek to fail
143 call_fseek.SetReturnValue(-1, 1);
144 auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
146 DALI_TEST_CHECK(result == nullptr);
151 int UtcDaliNavigationMeshCreateFromFileFail5(void)
153 tet_infoline("UtcDaliNavigationMeshCreateFromFileFail5: Fails to create navigation mesh using file");
155 // Override fread() to fail reading file
156 call_fread.SetReturnValue(-1, 0);
157 auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
159 DALI_TEST_CHECK(result == nullptr);
164 int UtcDaliNavigationMeshCreateFromFileOk1(void)
166 tet_infoline("UtcDaliNavigationMeshCreateFromFileOk1: Creates navigation mesh using file");
168 auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
170 DALI_TEST_CHECK(result != nullptr);
175 int UtcDaliNavigationMeshCreateFromBufferP(void)
177 tet_infoline("UtcDaliNavigationMeshCreateFromBufferP: Creates navigation mesh using binary buffer");
179 auto fin = fopen("resources/navmesh-test.bin", "rb");
180 [[maybe_unused]] auto err = fseek(fin, 0, SEEK_END);
181 auto length = ftell(fin);
182 fseek( fin, 0, SEEK_SET);
183 std::vector<uint8_t> buffer;
184 buffer.resize(length);
185 fread( buffer.data(), 1, length, fin );
187 auto result = NavigationMeshFactory::CreateFromBuffer( buffer );
188 DALI_TEST_CHECK(result != nullptr);
193 int UtcDaliNavigationMeshCountersP(void)
195 tet_infoline("UtcDaliNavigationMeshCountersP: Test vertex, edge and face counts");
197 auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
199 DALI_TEST_CHECK(result != nullptr);
201 auto vertexCount = result->GetVertexCount();
202 auto edgeCount = result->GetEdgeCount();
203 auto faceCount = result->GetFaceCount();
205 DALI_TEST_EQUALS( vertexCount, 132, TEST_LOCATION );
206 DALI_TEST_EQUALS( edgeCount, 300, TEST_LOCATION );
207 DALI_TEST_EQUALS( faceCount, 165, TEST_LOCATION );
212 int UtcDaliNavigationMeshGetVertexP(void)
214 tet_infoline("UtcDaliNavigationMeshGetVertexP: Test vertex getters");
216 auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
218 DALI_TEST_CHECK(navmesh != nullptr);
220 auto vertexCount = navmesh->GetVertexCount();
222 DALI_TEST_EQUALS( vertexCount, 132, TEST_LOCATION );
224 // List of coords, must be verified with Blender exporter
226 std::vector<float> vertexData = {
227 -7.000000f, -3.000000f, 0.000000f, -4.018748f, 3.000000f, 0.000000f,
228 1.943754f, -1.500000f, 0.000000f, -2.541295f, -0.756627f, 0.000000f,
229 -0.277504f, -1.593252f, 0.000000f, 0.682341f, 2.316388f, 3.349901f,
230 1.912569f, 1.240314f, 2.549901f, 2.215021f, -0.365898f, 1.749901f,
231 1.460422f, -1.815717f, 0.949901f, -0.336699f, -2.992929f, 3.829999f,
232 -3.179410f, 0.153939f, 3.829999f, -3.664814f, 2.992929f, 3.829999f,
233 -1.384417f, 0.876845f, 3.829999f, -1.571236f, 1.101834f, 3.829999f
238 for( auto i = 0u; i < 132; i+= 10, j+= 3)
240 const auto* vertex = navmesh->GetVertex(i);
241 Vector3 v0(vertex->co);
242 Vector3 v1(vertexData[j], vertexData[j+1], vertexData[j+2]);
243 DALI_TEST_EQUALS( v0, v1, TEST_LOCATION);
249 int UtcDaliNavigationMeshGetEdgeP(void)
251 tet_infoline("UtcDaliNavigationMeshGetEdgeP: Test edge getters");
253 auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
255 DALI_TEST_CHECK(navmesh != nullptr);
257 auto edgeCount = navmesh->GetEdgeCount();
259 DALI_TEST_EQUALS( edgeCount, 300, TEST_LOCATION );
261 // List of coords, must be verified with Blender exporter
263 std::vector<uint16_t> edgeData = {
277 for( auto i = 0u; i < 300; i+= 30, j+= 4)
279 const auto* edge = navmesh->GetEdge(i);
280 auto e0 = edge->face[0];
281 auto e1 = edge->face[1];
282 auto v0 = edge->vertex[0];
283 auto v1 = edge->vertex[1];
285 DALI_TEST_EQUALS(e0, edgeData[j+0], TEST_LOCATION);
286 DALI_TEST_EQUALS(e1, edgeData[j+1], TEST_LOCATION);
287 DALI_TEST_EQUALS(v0, edgeData[j+2], TEST_LOCATION);
288 DALI_TEST_EQUALS(v1, edgeData[j+3], TEST_LOCATION);
294 int UtcDaliNavigationMeshGetFaceP(void)
296 tet_infoline("UtcDaliNavigationMeshGetFaceP: Test face getters");
298 auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
300 DALI_TEST_CHECK(navmesh != nullptr);
302 auto faceCount = navmesh->GetFaceCount();
304 DALI_TEST_EQUALS( faceCount, 165, TEST_LOCATION );
306 // List of coords, must be verified with Blender exporter
309 std::vector<NavigationMesh::Face> faceData = {
310 {{6, 10, 17}, {14, 32, 8}, {0.000000f, 0.000000f, 1.000000f}, {-3.024998f, 2.500000f, 0.000000f}},
311 {{130, 120, 44}, {228, 215, 33}, {0.000000f, 0.000000f, 1.000000f}, {-1.097451f, 1.192811f, 3.829999f}},
312 {{30, 9, 38}, {13, 291, 289}, {0.000000f, -0.000000f, 1.000000f}, {-3.029388f, -1.252209f, 0.000000f}},
313 {{55, 52, 53}, {140, 95, 96}, {0.522345f, -0.298279f, 0.798865f}, {0.743287f, 1.610713f, 3.136567f}},
314 {{69, 66, 67}, {91, 121, 122}, {0.071722f, -0.597219f, 0.798865f}, {1.632142f, 0.155658f, 2.016567f}},
315 {{41, 86, 87}, {81, 160, 80}, {-0.563316f, -0.210929f, 0.798864f}, {0.340215f, -1.799765f, 0.416567f}},
316 {{28, 19, 27}, {55, 74, 47}, {0.000000f, -0.000000f, 1.000000f}, {-0.640862f, -1.037395f, 0.000000f}},
317 {{118, 96, 111}, {213, 241, 240}, {0.000000f, 0.000000f, 1.000000f}, {-6.577459f, -0.586560f, 3.829999f}},
318 {{91, 107, 103}, {170, 258, 257}, {-0.021129f, 0.023143f, 0.999509f}, {-2.551766f, 1.007552f, 3.829145f}},
319 {{97, 120, 130}, {191, 228, 271}, {0.000000f, 0.000000f, 1.000000f}, {-1.795930f, 0.710873f, 3.829999f}},
320 {{30, 39, 31}, {290, 296, 295}, {0.000000f, 0.000000f, 1.000000f}, {-2.291577f, -0.509718f, 0.000000f}},
324 for( auto i = 0u; i < 165; i+= 16, j++)
326 const auto* face = navmesh->GetFace(i);
327 Vector3 n0(face->normal);
328 Vector3 c0(face->center);
330 Vector3 n1(faceData[j].normal);
331 Vector3 c1(faceData[j].center);
333 DALI_TEST_EQUALS( n0, n1, TEST_LOCATION);
334 DALI_TEST_EQUALS( c0, c1, TEST_LOCATION);
336 DALI_TEST_EQUALS( faceData[j].vertex[0], face->vertex[0], TEST_LOCATION);
337 DALI_TEST_EQUALS( faceData[j].vertex[1], face->vertex[1], TEST_LOCATION);
338 DALI_TEST_EQUALS( faceData[j].vertex[2], face->vertex[2], TEST_LOCATION);
340 DALI_TEST_EQUALS( faceData[j].edge[0], face->edge[0], TEST_LOCATION);
341 DALI_TEST_EQUALS( faceData[j].edge[1], face->edge[1], TEST_LOCATION);
342 DALI_TEST_EQUALS( faceData[j].edge[2], face->edge[2], TEST_LOCATION);
349 int UtcDaliNavigationGetGravityP(void)
351 tet_infoline("UtcDaliNavigationGetGravityP: Tests gravity vector");
352 auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
353 auto gravity = navmesh->GetGravityVector();
355 // navmesh-test.bin is exported in Blender and the default gravity is Z = -1
356 Dali::Vector3 expectedGravity( 0.0f, 0.0f, -1.0f );
358 DALI_TEST_EQUALS( gravity, expectedGravity, TEST_LOCATION);
363 int UtcDaliNavigationSetTransformP(void)
365 tet_infoline("UtcDaliNavigationSetTransformP: Test setting transform");
367 auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
370 matrix.SetIdentity();
371 Quaternion q = Quaternion( Radian(Degree(-90)), Vector3(1.0, 0.0, 0.0));
373 matrix.Multiply( newMatrix, matrix, q); // Rotate matrix along X-axis
375 navmesh->SetSceneTransform(newMatrix);
377 auto point = Vector3(0, 1, 0);
379 [[maybe_unused]] Vector3 navMeshLocalSpace;
380 [[maybe_unused]] Vector3 navMeshParentSpace;
381 navMeshLocalSpace = navmesh->PointSceneToLocal(point);
383 // Should match gravity vector
384 auto gravityVector = navmesh->GetGravityVector();
386 // 'point' should be turned into the gravity vector after transforming into the local space
387 DALI_TEST_EQUALS( navMeshLocalSpace, gravityVector, TEST_LOCATION);
389 navMeshParentSpace = navmesh->PointLocalToScene(gravityVector);
391 // The gravity should be transformed back into point
392 DALI_TEST_EQUALS( navMeshParentSpace, point, TEST_LOCATION);
397 int UtcDaliNavigationFindFloor0P(void)
399 tet_infoline("UtcDaliNavigationFindFloor0P: Finds floor with result");
401 auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
403 // All calculations in the navmesh local space
404 navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
406 std::vector<Vector3> inPositions;
407 std::vector<Vector3> expectedPositions;
408 std::vector<uint32_t> expectedFaceIndex;
409 std::vector<bool> expectedResult;
411 // Lift slightly over the floor level
412 auto upFromGravity = navmesh->GetGravityVector() * (0.05f);
414 auto size = navmesh->GetFaceCount();
415 for( auto i = 0u; i < size; ++i)
417 const auto* face = navmesh->GetFace(i);
418 Vector3(face->center);
419 inPositions.emplace_back(Vector3(face->center));
420 inPositions.back() -= Vector3( upFromGravity );
421 expectedResult.emplace_back(true);
423 expectedPositions.emplace_back(face->center);
424 expectedFaceIndex.emplace_back(i);
427 // Add negative results
428 // Middle 'circle' of scene
429 inPositions.emplace_back(Vector3(-0.048838f, 0.039285f, 0.013085f));
430 expectedPositions.emplace_back();
431 expectedFaceIndex.emplace_back( NavigationMesh::NULL_FACE );
432 expectedResult.emplace_back(false);
434 // Triangle under stairs
435 inPositions.emplace_back(Vector3(0.44365f, -1.787f, 0.13085f));
436 expectedPositions.emplace_back();
437 expectedFaceIndex.emplace_back( NavigationMesh::NULL_FACE );
438 expectedResult.emplace_back(false);
441 inPositions.emplace_back(Vector3(0.77197f, -3.8596f, 0.13085f));
442 expectedPositions.emplace_back();
443 expectedFaceIndex.emplace_back( NavigationMesh::NULL_FACE );
444 expectedResult.emplace_back(false);
446 for( auto i = 0u; i < inPositions.size(); ++i )
449 uint32_t faceIndex {NavigationMesh::NULL_FACE};
450 auto result = navmesh->FindFloor(inPositions[i], outPosition, faceIndex);
451 DALI_TEST_EQUALS( bool(result), bool(expectedResult[i]), TEST_LOCATION);
452 DALI_TEST_EQUALS( faceIndex, expectedFaceIndex[i], TEST_LOCATION);
453 DALI_TEST_EQUALS( outPosition, expectedPositions[i], TEST_LOCATION);
459 int UtcDaliNavigationFindFloorForFace1P(void)
461 tet_infoline("UtcDaliNavigationFindFloorForFace1P: Finds floor for selected face");
463 auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
465 // All calculations in the navmesh local space
466 navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
470 auto position = Vector3( 0, 0, 0);
471 auto dontCheckNeighbours = true;
472 auto outPosition = Vector3();
473 auto expectedPosition = Vector3();
477 // test 1. position lies within selected triangle
479 position = Vector3(-6.0767f, -1.7268f, 4.287f);
480 expectedPosition = Vector3(-6.0767f, -1.7268f, 3.83f);
481 dontCheckNeighbours = true;
482 result = navmesh->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
484 DALI_TEST_EQUALS( result, true, TEST_LOCATION);
485 DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
489 // test 2. position lies outside selected triangle, not checking neighbours
491 position = Vector3(-5.3073f, -0.6023f, 4.287f);
492 expectedPosition = Vector3::ZERO;
493 outPosition = Vector3::ZERO;
494 dontCheckNeighbours = true;
495 result = navmesh->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
497 DALI_TEST_EQUALS( result, false, TEST_LOCATION);
498 DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
502 // test 3. position lies outside selected triangle but this time checking neighbours
504 position = Vector3(-5.3073f, -0.6023f, 4.287f);
505 expectedPosition = Vector3(-5.3073, -0.6023, 3.83);
506 outPosition = Vector3::ZERO;
507 dontCheckNeighbours = false;
508 result = navmesh->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
510 DALI_TEST_EQUALS( result, true, TEST_LOCATION);
511 DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
518 int UtcDaliNavigationFindFloorForFace2P(void)
520 tet_infoline("UtcDaliNavigationFindFloorForFace2P: Finds floor for selected face");
522 auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
524 // All calculations in the navmesh local space
525 navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
528 [[maybe_unused]] auto faceIndex = 0u;
529 auto position = Vector3( 0, 0, 0);
530 auto dontCheckNeighbours = true;
531 auto outPosition = Vector3();
532 auto expectedPosition = Vector3();
536 // test 4. position lies within a triangle but this time full search forced,
537 // the navmesh must have no previous searches (mCurrentFace shouldn't be set)
539 position = Vector3(-6.0767f, -1.7268f, 4.287f);
540 expectedPosition = Vector3(-6.0767f, -1.7268f, 3.83f);
541 dontCheckNeighbours = true;
542 result = navmesh->FindFloorForFace(position, NavigationMesh::NULL_FACE, dontCheckNeighbours, outPosition);
544 DALI_TEST_EQUALS( result, true, TEST_LOCATION);
545 DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);