Merge "Coverity issue fixes" into devel/master
[platform/core/uifw/dali-toolkit.git] / automated-tests / src / dali-scene3d / utc-Dali-NavigationMesh.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
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>
21 #include <dlfcn.h>
22 using namespace Dali;
23 using namespace Dali::Scene3D::Algorithm;
24 using namespace Dali::Scene3D::Loader;
25
26 /**
27  * SysOverride allows overriding a system symbol and
28  * set the return value for n-th call of it.
29  *
30  * After invoking the symbol override is disabled.
31  */
32 template<class R, class F>
33 struct SysOverride
34 {
35   SysOverride(const char* funcName)
36   {
37     funcNameStr = funcName;
38     if(!func)
39     {
40       func = decltype(func)(dlsym(RTLD_NEXT, funcName));
41     }
42   }
43
44   void SetReturnValue( R value, uint32_t n )
45   {
46     if(overrideEnabled)
47     {
48       tet_infoline("Warning! Overriding return value is already enabled! Ignoring!\n");
49       return;
50     }
51     result = value;
52     overrideCounter = n;
53     overrideEnabled = true;
54   }
55
56   template<class... Args>
57   R Invoke(Args&&... args)
58   {
59     auto retval = func(args...);
60     if(overrideEnabled)
61     {
62       if(!overrideCounter)
63       {
64         overrideEnabled = false;
65         return result;
66       }
67       overrideCounter--;
68     }
69     return retval;
70   }
71
72   std::string funcNameStr;
73   R result{R{}};
74   F* func{nullptr};
75   uint32_t overrideCounter = 0;
76   bool overrideEnabled = false;
77 };
78
79 // Override fseek()
80 static thread_local SysOverride<int, decltype(fseek)> call_fseek("fseek");
81 extern "C" int fseek (FILE *s, long int o, int w)
82 {
83   return call_fseek.Invoke( s, o, w );
84 }
85
86 // Override ftell()
87 static thread_local SysOverride<int, decltype(ftell)> call_ftell("ftell");
88 extern "C" long int ftell(FILE *s)
89 {
90   return call_ftell.Invoke( s );
91 }
92
93 // Override fread()
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)
96 {
97   return call_fread.Invoke(p, s, n, st);
98 }
99
100 int UtcDaliNavigationMeshCreateFromFileFail1(void)
101 {
102   tet_infoline("UtcDaliNavigationMeshCreateFromFileFail1: Fails to create navigation mesh from file");
103
104   // No such file, misspelled name
105   auto result = NavigationMeshFactory::CreateFromFile("notexisting.bin");
106
107   DALI_TEST_CHECK(result == nullptr);
108
109   END_TEST;
110 }
111
112 int UtcDaliNavigationMeshCreateFromFileFail2(void)
113 {
114   tet_infoline("UtcDaliNavigationMeshCreateFromFileFail2: Fails to create navigation mesh using file");
115
116   // Override next fseek to fail
117   call_fseek.SetReturnValue(-1, 0);
118   auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
119
120   DALI_TEST_CHECK(result == nullptr);
121
122   END_TEST;
123 }
124
125 int UtcDaliNavigationMeshCreateFromFileFail3(void)
126 {
127   tet_infoline("UtcDaliNavigationMeshCreateFromFileFail3: Fails to create navigation mesh using file");
128
129   // Override next ftell to fail
130   call_ftell.SetReturnValue(-1, 0);
131   auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
132
133   DALI_TEST_CHECK(result == nullptr);
134
135   END_TEST;
136 }
137
138 int UtcDaliNavigationMeshCreateFromFileFail4(void)
139 {
140   tet_infoline("UtcDaliNavigationMeshCreateFromFileFail4: Fails to create navigation mesh using file");
141
142   // Override 2nd fseek to fail
143   call_fseek.SetReturnValue(-1, 1);
144   auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
145
146   DALI_TEST_CHECK(result == nullptr);
147
148   END_TEST;
149 }
150
151 int UtcDaliNavigationMeshCreateFromFileFail5(void)
152 {
153   tet_infoline("UtcDaliNavigationMeshCreateFromFileFail5: Fails to create navigation mesh using file");
154
155   // Override fread() to fail reading file
156   call_fread.SetReturnValue(-1, 0);
157   auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
158
159   DALI_TEST_CHECK(result == nullptr);
160
161   END_TEST;
162 }
163
164 int UtcDaliNavigationMeshCreateFromFileOk1(void)
165 {
166   tet_infoline("UtcDaliNavigationMeshCreateFromFileOk1: Creates navigation mesh using file");
167
168   auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
169
170   DALI_TEST_CHECK(result != nullptr);
171
172   END_TEST;
173 }
174
175 int UtcDaliNavigationMeshCreateFromBufferP(void)
176 {
177   tet_infoline("UtcDaliNavigationMeshCreateFromBufferP: Creates navigation mesh using binary buffer");
178
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 );
186   fclose(fin);
187   auto result = NavigationMeshFactory::CreateFromBuffer( buffer );
188   DALI_TEST_CHECK(result != nullptr);
189
190   END_TEST;
191 }
192
193 int UtcDaliNavigationMeshCountersP(void)
194 {
195   tet_infoline("UtcDaliNavigationMeshCountersP: Test vertex, edge and face counts");
196
197   auto result = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
198
199   DALI_TEST_CHECK(result != nullptr);
200
201   auto vertexCount = result->GetVertexCount();
202   auto edgeCount = result->GetEdgeCount();
203   auto faceCount = result->GetFaceCount();
204
205   DALI_TEST_EQUALS( vertexCount, 132, TEST_LOCATION );
206   DALI_TEST_EQUALS( edgeCount, 300, TEST_LOCATION );
207   DALI_TEST_EQUALS( faceCount, 165, TEST_LOCATION );
208
209   END_TEST;
210 }
211
212 int UtcDaliNavigationMeshGetVertexP(void)
213 {
214   tet_infoline("UtcDaliNavigationMeshGetVertexP: Test vertex getters");
215
216   auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
217
218   DALI_TEST_CHECK(navmesh != nullptr);
219
220   auto vertexCount = navmesh->GetVertexCount();
221
222   DALI_TEST_EQUALS( vertexCount, 132, TEST_LOCATION );
223
224   // List of coords, must be verified with Blender exporter
225   // clang-format off
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
234   };
235   // clang-format on
236
237   auto j = 0;
238   for( auto i = 0u; i < 132; i+= 10, j+= 3)
239   {
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);
244   }
245
246   END_TEST;
247 }
248
249 int UtcDaliNavigationMeshGetEdgeP(void)
250 {
251   tet_infoline("UtcDaliNavigationMeshGetEdgeP: Test edge getters");
252
253   auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
254
255   DALI_TEST_CHECK(navmesh != nullptr);
256
257   auto edgeCount = navmesh->GetEdgeCount();
258
259   DALI_TEST_EQUALS( edgeCount, 300, TEST_LOCATION );
260
261   // List of coords, must be verified with Blender exporter
262   // clang-format off
263   std::vector<uint16_t> edgeData = {
264     2, 65535, 8, 1,
265     8, 109, 124, 108,
266     10, 158, 32, 35,
267     78, 65535, 50, 52,
268     54, 75, 70, 69,
269     83, 65535, 83, 81,
270     79, 65535, 86, 42,
271     140, 65535, 94, 115,
272     111, 112, 118, 111,
273     101, 143, 106, 127
274   };
275   // clang-format on
276   auto j = 0;
277   for( auto i = 0u; i < 300; i+= 30, j+= 4)
278   {
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];
284
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);
289   }
290
291   END_TEST;
292 }
293
294 int UtcDaliNavigationMeshGetFaceP(void)
295 {
296   tet_infoline("UtcDaliNavigationMeshGetFaceP: Test face getters");
297
298   auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
299
300   DALI_TEST_CHECK(navmesh != nullptr);
301
302   auto faceCount = navmesh->GetFaceCount();
303
304   DALI_TEST_EQUALS( faceCount, 165, TEST_LOCATION );
305
306   // List of coords, must be verified with Blender exporter
307   // clang-format off
308
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}},
321   };
322   // clang-format on
323   auto j = 0;
324   for( auto i = 0u; i < 165; i+= 16, j++)
325   {
326     const auto* face = navmesh->GetFace(i);
327     Vector3 n0(face->normal);
328     Vector3 c0(face->center);
329
330     Vector3 n1(faceData[j].normal);
331     Vector3 c1(faceData[j].center);
332
333     DALI_TEST_EQUALS( n0, n1, TEST_LOCATION);
334     DALI_TEST_EQUALS( c0, c1, TEST_LOCATION);
335
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);
339
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);
343   }
344
345   END_TEST;
346 }
347
348
349 int UtcDaliNavigationGetGravityP(void)
350 {
351   tet_infoline("UtcDaliNavigationGetGravityP: Tests gravity vector");
352   auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
353   auto gravity = navmesh->GetGravityVector();
354
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 );
357
358   DALI_TEST_EQUALS( gravity, expectedGravity, TEST_LOCATION);
359
360   END_TEST;
361 }
362
363 int UtcDaliNavigationSetTransformP(void)
364 {
365   tet_infoline("UtcDaliNavigationSetTransformP: Test setting transform");
366
367   auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
368
369   Matrix matrix;
370   matrix.SetIdentity();
371   Quaternion q = Quaternion( Radian(Degree(-90)), Vector3(1.0, 0.0, 0.0));
372   Matrix newMatrix;
373   matrix.Multiply( newMatrix, matrix, q); // Rotate matrix along X-axis
374
375   navmesh->SetSceneTransform(newMatrix);
376
377   auto point = Vector3(0, 1, 0);
378
379   [[maybe_unused]] Vector3 navMeshLocalSpace;
380   [[maybe_unused]] Vector3 navMeshParentSpace;
381   navMeshLocalSpace = navmesh->PointSceneToLocal(point);
382
383   // Should match gravity vector
384   auto gravityVector = navmesh->GetGravityVector();
385
386   // 'point' should be turned into the gravity vector after transforming into the local space
387   DALI_TEST_EQUALS( navMeshLocalSpace, gravityVector, TEST_LOCATION);
388
389   navMeshParentSpace = navmesh->PointLocalToScene(gravityVector);
390
391   // The gravity should be transformed back into point
392   DALI_TEST_EQUALS( navMeshParentSpace, point, TEST_LOCATION);
393
394   END_TEST;
395 }
396
397 int UtcDaliNavigationFindFloor0P(void)
398 {
399   tet_infoline("UtcDaliNavigationFindFloor0P: Finds floor with result");
400
401   auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
402
403   // All calculations in the navmesh local space
404   navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
405
406   std::vector<Vector3> inPositions;
407   std::vector<Vector3> expectedPositions;
408   std::vector<uint32_t> expectedFaceIndex;
409   std::vector<bool> expectedResult;
410
411   // Lift slightly over the floor level
412   auto upFromGravity = navmesh->GetGravityVector() * (0.05f);
413
414   auto size = navmesh->GetFaceCount();
415   for( auto i = 0u; i < size; ++i)
416   {
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);
422
423     expectedPositions.emplace_back(face->center);
424     expectedFaceIndex.emplace_back(i);
425   }
426
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);
433
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);
439
440   // Outside area
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);
445
446   for( auto i = 0u; i < inPositions.size(); ++i )
447   {
448     Vector3  outPosition;
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);
454   }
455
456   END_TEST;
457 }
458
459 int UtcDaliNavigationFindFloorForFace1P(void)
460 {
461   tet_infoline("UtcDaliNavigationFindFloorForFace1P: Finds floor for selected face");
462
463   auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
464
465   // All calculations in the navmesh local space
466   navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
467
468   {
469     auto faceIndex = 0u;
470     auto position = Vector3( 0, 0, 0);
471     auto dontCheckNeighbours = true;
472     auto outPosition = Vector3();
473     auto expectedPosition = Vector3();
474     bool result = false;
475
476     {
477       // test 1. position lies within selected triangle
478       faceIndex = 137;
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);
483
484       DALI_TEST_EQUALS( result, true, TEST_LOCATION);
485       DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
486     }
487
488     {
489       // test 2. position lies outside selected triangle, not checking neighbours
490       faceIndex = 137;
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);
496
497       DALI_TEST_EQUALS( result, false, TEST_LOCATION);
498       DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
499     }
500
501     {
502       // test 3. position lies outside selected triangle but this time checking neighbours
503       faceIndex = 137;
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);
509
510       DALI_TEST_EQUALS( result, true, TEST_LOCATION);
511       DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
512     }
513   }
514
515   END_TEST;
516 }
517
518 int UtcDaliNavigationFindFloorForFace2P(void)
519 {
520   tet_infoline("UtcDaliNavigationFindFloorForFace2P: Finds floor for selected face");
521
522   auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
523
524   // All calculations in the navmesh local space
525   navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
526
527   {
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();
533     bool result = false;
534
535     {
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)
538       faceIndex = 137;
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);
543
544       DALI_TEST_EQUALS( result, true, TEST_LOCATION);
545       DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
546     }
547
548   }
549
550   END_TEST;
551 }