3D Scene Hit Testing 13/299713/18
authorAdam Bialogonski <adam.b@samsung.com>
Mon, 13 Nov 2023 07:34:01 +0000 (07:34 +0000)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Thu, 16 Nov 2023 09:07:15 +0000 (09:07 +0000)
- New functions creates mesh using list of vertices and face indices to work with NUI.
- Added the functionality to detect which mesh is hit & emit signal

Requires C# binding and NUI Model.cs update

Change-Id: Ib5bfd8ad6e7c35017db2c1173805f5c863156530
Signed-off-by: Adam Bialogonski <adam.b@samsung.com>
21 files changed:
automated-tests/src/dali-scene3d/collider-mesh-data.h [new file with mode: 0644]
automated-tests/src/dali-scene3d/utc-Dali-NavigationMesh.cpp
dali-scene3d/internal/algorithm/navigation-mesh-impl.cpp
dali-scene3d/internal/algorithm/navigation-mesh-impl.h
dali-scene3d/internal/controls/model/model-impl.cpp
dali-scene3d/internal/controls/model/model-impl.h
dali-scene3d/internal/event/collider-mesh-processor-impl.cpp [new file with mode: 0644]
dali-scene3d/internal/event/collider-mesh-processor-impl.h [new file with mode: 0644]
dali-scene3d/internal/event/collider-mesh-processor.cpp [new file with mode: 0644]
dali-scene3d/internal/event/collider-mesh-processor.h [new file with mode: 0644]
dali-scene3d/internal/file.list
dali-scene3d/internal/model-components/model-node-impl.cpp
dali-scene3d/internal/model-components/model-node-impl.h
dali-scene3d/public-api/algorithm/navigation-mesh.cpp
dali-scene3d/public-api/algorithm/navigation-mesh.h
dali-scene3d/public-api/controls/model/model.cpp
dali-scene3d/public-api/controls/model/model.h
dali-scene3d/public-api/loader/navigation-mesh-factory.cpp
dali-scene3d/public-api/loader/navigation-mesh-factory.h
dali-scene3d/public-api/model-components/model-node.cpp
dali-scene3d/public-api/model-components/model-node.h

diff --git a/automated-tests/src/dali-scene3d/collider-mesh-data.h b/automated-tests/src/dali-scene3d/collider-mesh-data.h
new file mode 100644 (file)
index 0000000..5e97c51
--- /dev/null
@@ -0,0 +1,75 @@
+#include <algorithm>
+#include <vector>
+
+unsigned char col_test_0[] = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x42, 0x00, 0x00, 0x35, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x42, 0x00, 0x40, 0x20, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_0_len = 216;
+unsigned char col_test_1[]   = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x41, 0x00, 0x00, 0x09, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x42, 0x00, 0x40, 0x20, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x42, 0x00, 0x00, 0x09, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x41, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_1_len = 216;
+unsigned char col_test_10[]  = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xa6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x13, 0x44, 0x00, 0x80, 0x93, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1b, 0x44, 0x00, 0x80, 0xa6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1b, 0x44, 0x00, 0x00, 0xa0, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x13, 0x44, 0x00, 0x00, 0xa0, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x44, 0x00, 0x80, 0x93, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x44, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xff, 0xff, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0xff, 0xff, 0x03, 0x00, 0x04, 0x00, 0x02, 0x00, 0xff, 0xff, 0x01, 0x00, 0x04, 0x00, 0x03, 0x00, 0xff, 0xff, 0x04, 0x00, 0x05, 0x00, 0x03, 0x00, 0xff, 0xff, 0x05, 0x00, 0x01, 0x00, 0x03, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_10_len = 360;
+unsigned char col_test_11[]   = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x16, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0x44, 0x00, 0x80, 0x0f, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x1e, 0x44, 0x00, 0xc0, 0x16, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x1e, 0x44, 0x00, 0x80, 0x0f, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0x44, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_11_len = 216;
+unsigned char col_test_12[]   = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc4, 0x43, 0x00, 0x40, 0x20, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x43, 0x00, 0x80, 0x0f, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc4, 0x43, 0x00, 0x80, 0x0f, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x44, 0x00, 0x00, 0xd6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x44, 0x00, 0x00, 0xd6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x44, 0x00, 0x00, 0xd6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc6, 0x43, 0x00, 0x00, 0xc0, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x44, 0x00, 0x00, 0x8c, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc6, 0x43, 0x00, 0x00, 0xc0, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc6, 0x43, 0x00, 0x00, 0x28, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc6, 0x43, 0x00, 0x00, 0x09, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x43, 0x00, 0x80, 0xa6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x43, 0x00, 0x00, 0x28, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x43, 0x00, 0x00, 0x09, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x42, 0x00, 0x80, 0xa6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x42, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x04, 0x00, 0x01, 0x00, 0xff, 0xff, 0x04, 0x00, 0x05, 0x00, 0x01, 0x00, 0xff, 0xff, 0x05, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x04, 0x00, 0x06, 0x00, 0x02, 0x00, 0xff, 0xff, 0x06, 0x00, 0x07, 0x00, 0x02, 0x00, 0xff, 0xff, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0xff, 0xff, 0x03, 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0xff, 0x06, 0x00, 0x04, 0x00, 0x03, 0x00, 0xff, 0xff, 0x04, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0xff, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0xff, 0xff, 0x08, 0x00, 0x09, 0x00, 0x04, 0x00, 0xff, 0xff, 0x09, 0x00, 0x06, 0x00, 0x04, 0x00, 0xff, 0xff, 0x06, 0x00, 0x0a, 0x00, 0x05, 0x00, 0xff, 0xff, 0x0a, 0x00, 0x08, 0x00, 0x05, 0x00, 0xff, 0xff, 0x08, 0x00, 0x06, 0x00, 0x05, 0x00, 0xff, 0xff, 0x02, 0x00, 0x06, 0x00, 0x06, 0x00, 0xff, 0xff, 0x06, 0x00, 0x03, 0x00, 0x06, 0x00, 0xff, 0xff, 0x03, 0x00, 0x02, 0x00, 0x06, 0x00, 0xff, 0xff, 0x02, 0x00, 0x0a, 0x00, 0x07, 0x00, 0xff, 0xff, 0x0a, 0x00, 0x06, 0x00, 0x07, 0x00, 0xff, 0xff, 0x06, 0x00, 0x02, 0x00, 0x07, 0x00, 0xff, 0xff, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, 0xff, 0xff, 0x0a, 0x00, 0x02, 0x00, 0x08, 0x00, 0xff, 0xff, 0x02, 0x00, 0x01, 0x00, 0x08, 0x00, 0xff, 0xff, 0x0b, 0x00, 0x0a, 0x00, 0x09, 0x00, 0xff, 0xff, 0x0a, 0x00, 0x01, 0x00, 0x09, 0x00, 0xff, 0xff, 0x01, 0x00, 0x0b, 0x00, 0x09, 0x00, 0xff, 0xff, 0x0c, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xff, 0xff, 0x0a, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0xff, 0xff, 0x0b, 0x00, 0x0c, 0x00, 0x0a, 0x00, 0xff, 0xff, 0x0a, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0xff, 0xff, 0x0c, 0x00, 0x0d, 0x00, 0x0b, 0x00, 0xff, 0xff, 0x0d, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0xff, 0xff, 0x07, 0x00, 0x06, 0x00, 0x0c, 0x00, 0xff, 0xff, 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0xff, 0xff, 0x09, 0x00, 0x07, 0x00, 0x0c, 0x00, 0xff, 0xff, 0x0e, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0xff, 0xff, 0x0c, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0xff, 0xff, 0x0b, 0x00, 0x0e, 0x00, 0x0d, 0x00, 0xff, 0xff, 0x0c, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0xff, 0xff, 0x0e, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xff, 0xff, 0x0f, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_12_len = 1140;
+unsigned char col_test_13[]   = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc6, 0x43, 0x00, 0x00, 0x28, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xd4, 0x43, 0x00, 0x00, 0x8c, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xd4, 0x43, 0x00, 0x00, 0x28, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc6, 0x43, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_13_len = 216;
+unsigned char col_test_14[]   = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x44, 0x00, 0x00, 0xd6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x1e, 0x44, 0x00, 0x80, 0x0f, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x1e, 0x44, 0x00, 0x00, 0xd6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x44, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_14_len = 216;
+unsigned char col_test_2[]    = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x36, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x84, 0x43, 0x00, 0x80, 0x36, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x42, 0x00, 0x40, 0x20, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x84, 0x43, 0x00, 0x00, 0x09, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x43, 0x00, 0x40, 0x20, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x43, 0x00, 0x00, 0x09, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x42, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x03, 0x00, 0x04, 0x00, 0x01, 0x00, 0xff, 0xff, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0xff, 0xff, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0xff, 0xff, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0xff, 0xff, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x03, 0x00, 0xff, 0xff, 0x01, 0x00, 0x05, 0x00, 0x03, 0x00, 0xff, 0xff, 0x05, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_2_len = 360;
+unsigned char col_test_3[]   = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xa6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x43, 0x00, 0x00, 0x28, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x43, 0x00, 0x80, 0xa6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x43, 0x00, 0x00, 0x28, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x43, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_3_len = 216;
+unsigned char col_test_4[]   = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x36, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0x43, 0x00, 0x40, 0x2f, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc4, 0x43, 0x00, 0x80, 0x36, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc4, 0x43, 0x00, 0x40, 0x2f, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0x43, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_4_len = 216;
+unsigned char col_test_5[]   = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x36, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0x43, 0x00, 0x80, 0x36, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x84, 0x43, 0x00, 0x40, 0x2f, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0x43, 0x00, 0x40, 0x20, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc4, 0x43, 0x00, 0x40, 0x2f, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc4, 0x43, 0x00, 0x40, 0x20, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x84, 0x43, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x03, 0x00, 0x04, 0x00, 0x01, 0x00, 0xff, 0xff, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0xff, 0xff, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0xff, 0xff, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0xff, 0xff, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x03, 0x00, 0xff, 0xff, 0x01, 0x00, 0x05, 0x00, 0x03, 0x00, 0xff, 0xff, 0x05, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_5_len = 360;
+unsigned char col_test_6[]   = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x93, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc4, 0x43, 0x00, 0x00, 0x8c, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xd3, 0x43, 0x00, 0x80, 0x93, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xd3, 0x43, 0x00, 0x00, 0x8c, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc4, 0x43, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_6_len = 216;
+unsigned char col_test_7[]   = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc6, 0x43, 0x00, 0x80, 0x93, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x44, 0x00, 0x00, 0xc0, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x44, 0x00, 0x80, 0x93, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc6, 0x43, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_7_len = 216;
+unsigned char col_test_8[]   = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x36, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc4, 0x43, 0x00, 0x80, 0x0f, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0x44, 0x00, 0x80, 0x36, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0x44, 0x00, 0x80, 0x0f, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc4, 0x43, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int  col_test_8_len = 216;
+unsigned char col_test_9[]   = {
+  0x4e, 0x41, 0x56, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1b, 0x44, 0x00, 0x80, 0xa6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x13, 0x44, 0x00, 0x80, 0xa6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1b, 0x44, 0x00, 0x00, 0xd6, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x44, 0x00, 0x00, 0xa0, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x13, 0x44, 0x00, 0x00, 0xa0, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x44, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 0x03, 0x00, 0x04, 0x00, 0x02, 0x00, 0xff, 0xff, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xff, 0xff, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0xff, 0xff, 0x04, 0x00, 0x03, 0x00, 0x03, 0x00, 0xff, 0xff, 0x03, 0x00, 0x05, 0x00, 0x03, 0x00, 0xff, 0xff, 0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int col_test_9_len = 360;
+
+static const std::vector<std::pair<uint8_t*, uint32_t>> TEST_COLLIDER_MESH = {
+  {col_test_0, col_test_0_len},
+  {col_test_1, col_test_1_len},
+  {col_test_2, col_test_2_len},
+  {col_test_3, col_test_3_len},
+  {col_test_4, col_test_4_len},
+  {col_test_5, col_test_5_len},
+  {col_test_6, col_test_6_len},
+  {col_test_7, col_test_7_len},
+  {col_test_8, col_test_8_len},
+  {col_test_9, col_test_9_len},
+};
+
+#define COLLIDER_BUFFER(N)                                              \
+  std::invoke([]() {                                                    \
+    auto v = std::vector<uint8_t>();                                    \
+    v.insert(v.end(), col_test_##N, col_test_##N + col_test_##N##_len); \
+    return v;                                                           \
+  })
+
+static std::vector<uint8_t> GetTestColliderMesh(int n)
+{
+  auto v = std::vector<uint8_t>();
+  v.insert(v.end(), TEST_COLLIDER_MESH[n].first, TEST_COLLIDER_MESH[n].first + TEST_COLLIDER_MESH[n].second);
+  return v;
+}
index 957332d..5df30be 100644 (file)
  *
  */
 
-#include <dali-test-suite-utils.h>
+#include <dali-scene3d/public-api/controls/model/model.h>
+#include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali/devel-api/actors/camera-actor-devel.h>
+#include <dali/integration-api/events/touch-event-integ.h>
 #include <dlfcn.h>
 #include "dali-scene3d/public-api/algorithm/navigation-mesh.h"
 #include "dali-scene3d/public-api/loader/navigation-mesh-factory.h"
+
+// include collider mesh data
+#include "collider-mesh-data.h"
+
 using namespace Dali;
 using namespace Dali::Scene3D::Algorithm;
 using namespace Dali::Scene3D::Loader;
@@ -97,6 +105,86 @@ extern "C" size_t                                     fread(void* __restrict p,
   return call_fread.Invoke(p, s, n, st);
 }
 
+// Data to test factory
+static std::vector<Dali::Vector3> COLLIDER_0_VERTS = {
+  Dali::Vector3(-1.000000, -1.556106, 0.000000),
+  Dali::Vector3(1.000000, -1.556106, 0.000000),
+  Dali::Vector3(-1.000000, 1.000000, 0.000000),
+  Dali::Vector3(1.000000, 1.000000, 0.000000),
+  Dali::Vector3(3.026269, -1.556106, 0.000000),
+  Dali::Vector3(3.026269, 1.000000, 0.000000),
+  Dali::Vector3(-1.000000, 2.491248, 0.000000),
+  Dali::Vector3(1.000000, 2.491248, 0.000000),
+};
+static std::vector<uint32_t> COLLIDER_0_IDX = {
+  1,
+  2,
+  0,
+  1,
+  5,
+  3,
+  3,
+  6,
+  2,
+  1,
+  3,
+  2,
+  1,
+  4,
+  5,
+  3,
+  7,
+  6,
+};
+static std::vector<Dali::Vector3> COLLIDER_1_VERTS = {
+  Dali::Vector3(-1.000000, -3.386207, 0.000000),
+  Dali::Vector3(1.000000, -3.386207, 0.000000),
+  Dali::Vector3(-1.000000, 1.000000, 0.000000),
+  Dali::Vector3(1.000000, 1.000000, 0.000000),
+  Dali::Vector3(-3.393266, -3.386207, 0.000000),
+  Dali::Vector3(-3.393266, 1.000000, 0.000000),
+};
+static std::vector<uint32_t> COLLIDER_1_IDX = {
+  1,
+  2,
+  0,
+  2,
+  4,
+  0,
+  1,
+  3,
+  2,
+  2,
+  5,
+  4,
+};
+static std::vector<Dali::Vector3> COLLIDER_2_VERTS = {
+  Dali::Vector3(-3.393266, -1.000000, 0.000000),
+  Dali::Vector3(1.000000, -1.000000, 0.000000),
+  Dali::Vector3(-3.393266, 0.491248, 0.000000),
+  Dali::Vector3(1.000000, 0.491248, 0.000000),
+};
+static std::vector<uint32_t> COLLIDER_2_IDX = {
+  1,
+  2,
+  0,
+  1,
+  3,
+  2,
+};
+
+Integration::TouchEvent GenerateSingleTouch(PointState::Type state, const Vector2& screenPosition)
+{
+  Integration::TouchEvent touchEvent;
+  Integration::Point      point;
+  point.SetState(state);
+  point.SetScreenPosition(screenPosition);
+  point.SetDeviceClass(Device::Class::TOUCH);
+  point.SetDeviceSubclass(Device::Subclass::NONE);
+  touchEvent.points.push_back(point);
+  return touchEvent;
+}
+
 int UtcDaliNavigationMeshCreateFromFileFail1(void)
 {
   tet_infoline("UtcDaliNavigationMeshCreateFromFileFail1: Fails to create navigation mesh from file");
@@ -373,7 +461,7 @@ int UtcDaliNavigationSetTransformP(void)
 
   navmesh->SetSceneTransform(newMatrix);
 
-  auto point = Vector3(0, 1, 0);
+  auto point = Vector3(0, -1, 0);
 
   [[maybe_unused]] Vector3 navMeshLocalSpace;
   [[maybe_unused]] Vector3 navMeshParentSpace;
@@ -383,12 +471,12 @@ int UtcDaliNavigationSetTransformP(void)
   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);
+  DALI_TEST_EQUALS(navMeshLocalSpace, gravityVector, std::numeric_limits<float>::epsilon(), TEST_LOCATION);
 
   navMeshParentSpace = navmesh->PointLocalToScene(gravityVector);
 
   // The gravity should be transformed back into point
-  DALI_TEST_EQUALS(navMeshParentSpace, point, TEST_LOCATION);
+  DALI_TEST_EQUALS(navMeshParentSpace, point, std::numeric_limits<float>::epsilon(), TEST_LOCATION);
 
   END_TEST;
 }
@@ -546,4 +634,182 @@ int UtcDaliNavigationFindFloorForFace2P(void)
   }
 
   END_TEST;
-}
\ No newline at end of file
+}
+
+int UtcDaliNavigationMeshCreateFromVerticesAndFaces(void)
+{
+  tet_infoline("UtcDaliNavigationMeshCreateFromVerticesAndFaces: Creates NavigationMesh using vertices and faces");
+
+  auto buffer0 = COLLIDER_BUFFER(0);
+
+  // All calculations in the navmesh local space
+  auto fn = [&](const auto& vertices, const auto& normals, const auto& indices) {
+    auto navmesh = NavigationMeshFactory::CreateFromVertexFaceList(vertices, normals, indices);
+    navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
+    DALI_TEST_EQUALS(navmesh->GetVertexCount(), vertices.size(), TEST_LOCATION);
+    DALI_TEST_EQUALS(navmesh->GetFaceCount(), indices.size() / 3, TEST_LOCATION);
+    DALI_TEST_EQUALS(navmesh->GetEdgeCount(), indices.size(), TEST_LOCATION);
+
+    // compare data
+    for(auto i = 0u; i < navmesh->GetVertexCount(); ++i)
+    {
+      Dali::Vector3 v(navmesh->GetVertex(i)->coordinates);
+      DALI_TEST_EQUALS(vertices[i], v, TEST_LOCATION);
+    }
+
+    for(auto i = 0u; i < navmesh->GetFaceCount() * 3; i += 3)
+    {
+      const auto& v = navmesh->GetFace(i / 3)->vertex;
+      DALI_TEST_EQUALS(indices[i], v[0], TEST_LOCATION);
+      DALI_TEST_EQUALS(indices[i + 1], v[1], TEST_LOCATION);
+      DALI_TEST_EQUALS(indices[i + 2], v[2], TEST_LOCATION);
+    }
+  };
+
+  std::vector<Vector3> normals;
+  normals.resize(COLLIDER_0_VERTS.size());
+  std::fill(normals.begin(), normals.end(), Vector3(0.0, 1.0, 0.0));
+  fn(COLLIDER_0_VERTS, normals, COLLIDER_0_IDX);
+
+  normals.resize(COLLIDER_1_VERTS.size());
+  std::fill(normals.begin(), normals.end(), Vector3(0.0, 1.0, 0.0));
+  fn(COLLIDER_1_VERTS, normals, COLLIDER_1_IDX);
+
+  normals.resize(COLLIDER_2_VERTS.size());
+  std::fill(normals.begin(), normals.end(), Vector3(0.0, 1.0, 0.0));
+  fn(COLLIDER_2_VERTS, normals, COLLIDER_2_IDX);
+  END_TEST;
+}
+
+int UtcDaliNavigationMeshCreateFromVerticesAndFacesNoNormals(void)
+{
+  tet_infoline("UtcDaliNavigationMeshCreateFromVerticesAndFacesNoNormals: Creates NavigationMesh using vertices and faces but recalculates normals");
+
+  auto buffer0 = COLLIDER_BUFFER(0);
+
+  // All calculations in the navmesh local space
+  auto fn = [&](const auto& vertices, const auto& indices) {
+    auto navmesh = NavigationMeshFactory::CreateFromVertexFaceList(vertices.data(), nullptr, vertices.size(), indices.data(), indices.size());
+    navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
+    DALI_TEST_EQUALS(navmesh->GetVertexCount(), vertices.size(), TEST_LOCATION);
+    DALI_TEST_EQUALS(navmesh->GetFaceCount(), indices.size() / 3, TEST_LOCATION);
+    DALI_TEST_EQUALS(navmesh->GetEdgeCount(), indices.size(), TEST_LOCATION);
+
+    // compare data
+    for(auto i = 0u; i < navmesh->GetVertexCount(); ++i)
+    {
+      Dali::Vector3 v(navmesh->GetVertex(i)->coordinates);
+      DALI_TEST_EQUALS(vertices[i], v, TEST_LOCATION);
+    }
+
+    for(auto i = 0u; i < navmesh->GetFaceCount() * 3; i += 3)
+    {
+      const auto& v = navmesh->GetFace(i / 3)->vertex;
+      DALI_TEST_EQUALS(indices[i], v[0], TEST_LOCATION);
+      DALI_TEST_EQUALS(indices[i + 1], v[1], TEST_LOCATION);
+      DALI_TEST_EQUALS(indices[i + 2], v[2], TEST_LOCATION);
+    }
+  };
+
+  std::vector<Vector3> normals;
+  normals.resize(COLLIDER_0_VERTS.size());
+  std::fill(normals.begin(), normals.end(), Vector3(0.0, 1.0, 0.0));
+  fn(COLLIDER_0_VERTS, COLLIDER_0_IDX);
+
+  normals.resize(COLLIDER_1_VERTS.size());
+  std::fill(normals.begin(), normals.end(), Vector3(0.0, 1.0, 0.0));
+  fn(COLLIDER_1_VERTS, COLLIDER_1_IDX);
+
+  normals.resize(COLLIDER_2_VERTS.size());
+  std::fill(normals.begin(), normals.end(), Vector3(0.0, 1.0, 0.0));
+  fn(COLLIDER_2_VERTS, COLLIDER_2_IDX);
+  END_TEST;
+}
+
+int UtcDaliNavigationMeshGetBinaryTest(void)
+{
+  tet_infoline("UtcDaliNavigationMeshGetBinaryTest: Creates meshes dynamically, reloads binaries and compares");
+
+  // Test 10 collider meshes
+  for(auto i = 0u; i < 10; ++i)
+  {
+    auto colliderMesh = NavigationMeshFactory::CreateFromBuffer(GetTestColliderMesh(i));
+    auto binary       = NavigationMeshFactory::GetMeshBinary(*colliderMesh);
+    DALI_TEST_EQUALS(binary.size() > 0, true, TEST_LOCATION);
+
+    auto colliderMesh2 = NavigationMeshFactory::CreateFromBuffer(binary);
+
+    DALI_TEST_EQUALS(colliderMesh->GetFaceCount(), colliderMesh2->GetFaceCount(), TEST_LOCATION);
+    DALI_TEST_EQUALS(colliderMesh->GetVertexCount(), colliderMesh2->GetVertexCount(), TEST_LOCATION);
+    DALI_TEST_EQUALS(colliderMesh->GetEdgeCount(), colliderMesh2->GetEdgeCount(), TEST_LOCATION);
+
+    // test vertices
+    for(auto idx = 0u; idx < colliderMesh->GetFaceCount(); ++idx)
+    {
+      auto v0  = colliderMesh->GetVertex(idx);
+      auto v1  = colliderMesh2->GetVertex(idx);
+      auto co0 = Vector3(v0->coordinates);
+      auto co1 = Vector3(v1->coordinates);
+      DALI_TEST_EQUALS(co0, co1, std::numeric_limits<float>::epsilon(), TEST_LOCATION);
+    }
+
+    // test face
+    for(auto idx = 0u; idx < colliderMesh->GetFaceCount(); ++idx)
+    {
+      auto  f0  = colliderMesh->GetFace(idx);
+      auto  f1  = colliderMesh2->GetFace(idx);
+      auto& vi0 = f0->vertex;
+      auto& vi1 = f1->vertex;
+      DALI_TEST_EQUALS(vi0[0], vi1[0], TEST_LOCATION);
+      DALI_TEST_EQUALS(vi0[1], vi1[1], TEST_LOCATION);
+      DALI_TEST_EQUALS(vi0[2], vi1[2], TEST_LOCATION);
+    }
+  }
+
+  END_TEST;
+}
+
+int UtcDaliColliderMeshModelNodeSetup(void)
+{
+  tet_infoline("UtcDaliColliderMeshModelNodeSetup: Test different variants of setting up a collider mesh to the node");
+
+  ToolkitTestApplication   application;
+  Dali::Scene3D::ModelNode node  = Dali::Scene3D::ModelNode::New();
+  Dali::Scene3D::Model     model = Dali::Scene3D::Model::New();
+  model.AddModelNode(node);
+
+  application.GetWindow().Add(model);
+  application.SendNotification();
+  application.Render();
+
+  auto colliderMesh = NavigationMeshFactory::CreateFromBuffer(GetTestColliderMesh(0));
+
+  // Redundant setup test
+  DALI_TEST_EQUALS(node.HasColliderMesh(), false, TEST_LOCATION);
+  node.SetColliderMesh(nullptr);
+  DALI_TEST_EQUALS(node.HasColliderMesh(), false, TEST_LOCATION);
+  node.SetColliderMesh(std::move(colliderMesh));
+  DALI_TEST_EQUALS(node.HasColliderMesh(), true, TEST_LOCATION);
+
+  // Reset collider mesh
+  node.SetColliderMesh(nullptr);
+  DALI_TEST_EQUALS(node.HasColliderMesh(), false, TEST_LOCATION);
+
+  auto colliderMesh2 = NavigationMeshFactory::CreateFromBuffer(GetTestColliderMesh(1));
+  auto colliderMesh3 = NavigationMeshFactory::CreateFromBuffer(GetTestColliderMesh(2));
+
+  const auto& cm2 = *colliderMesh2;
+  const auto& cm3 = *colliderMesh3;
+
+  node.SetColliderMesh(std::move(colliderMesh2));
+  DALI_TEST_EQUALS(node.HasColliderMesh(), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(&node.GetColliderMesh(), &cm2, TEST_LOCATION);
+  node.SetColliderMesh(std::move(colliderMesh3));
+  DALI_TEST_EQUALS(node.HasColliderMesh(), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(&node.GetColliderMesh(), &cm3, TEST_LOCATION);
+
+  node.SetColliderMesh(nullptr);
+  DALI_TEST_EQUALS(node.HasColliderMesh(), false, TEST_LOCATION);
+
+  END_TEST;
+}
index 61485fb..81b999d 100644 (file)
@@ -31,13 +31,6 @@ 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
  */
@@ -260,17 +253,61 @@ NavigationMesh::IntersectResult NavigationMesh::NavigationRayFaceIntersection(Na
   return result;
 }
 
+NavigationMesh::IntersectResult NavigationMesh::RayCastIntersect(NavigationRay& rayOrig) const
+{
+  auto                       faceCount = GetFaceCount();
+  std::list<IntersectResult> results;
+
+  NavigationRay ray;
+
+  ray.origin = PointSceneToLocal(rayOrig.origin); // origin is equal position
+
+  // Ray direction matches gravity direction
+  ray.direction = PointSceneToLocal(rayOrig.origin + rayOrig.direction) - ray.origin;
+  ray.direction.Normalize();
+  for(auto i = 0u; i < faceCount; ++i)
+  {
+    auto result = NavigationRayFaceIntersection(ray, *GetFace(i));
+    if(result.result)
+    {
+      result.faceIndex = i;
+      if(results.empty())
+      {
+        results.push_back(result);
+      }
+      else
+      {
+        for(auto it = results.begin(); it != results.end(); ++it)
+        {
+          if((*it).distance > result.distance)
+          {
+            results.insert(it, result);
+            break;
+          }
+        }
+      }
+    }
+  }
+  if(!results.empty())
+  {
+    return results.front();
+  }
+  else
+  {
+    return IntersectResult{Vector3::ZERO, 0.0f, 0u, false};
+  }
+}
+
 void NavigationMesh::SetTransform(const Dali::Matrix& transform)
 {
-  mTransform = transform;
-  transform.InvertTransform(mTransformInverse);
+  mTransform        = transform;
+  mTransformInverse = mTransform;
+  mTransformInverse.Invert();
 }
 
 Dali::Vector3 NavigationMesh::PointSceneToLocal(const Dali::Vector3& point) const
 {
-  // Transform point into navmesh space
-  Dali::Vector4 invNewPos = mTransformInverse * Dali::Vector4(point.x, -point.y, point.z, 1.0f);
-  invNewPos.y *= -1.0f;
+  Dali::Vector4 invNewPos = mTransformInverse * Dali::Vector4(point.x, point.y, point.z, 1.0f);
 
   return Dali::Vector3(invNewPos.AsFloat());
 }
@@ -278,8 +315,7 @@ Dali::Vector3 NavigationMesh::PointSceneToLocal(const Dali::Vector3& point) cons
 Dali::Vector3 NavigationMesh::PointLocalToScene(const Dali::Vector3& point) const
 {
   // Transform point into scene transform space
-  Dali::Vector4 newPos = mTransform * Dali::Vector4(point.x, -point.y, point.z, 1.0f);
-  newPos.y *= -1.0f;
+  Dali::Vector4 newPos = mTransform * Dali::Vector4(point.x, point.y, point.z, 1.0f);
 
   return Dali::Vector3(newPos.AsFloat());
 }
index f1db5db..6a82787 100644 (file)
@@ -40,7 +40,12 @@ class NavigationMeshFactory;
 
 namespace Dali::Scene3D::Internal::Algorithm
 {
-class NavigationRay;
+// Internal Navigation ray structure
+struct NavigationRay
+{
+  Dali::Vector3 origin;    // Origin of ray
+  Dali::Vector3 direction; // Direction of ray
+};
 
 // Make each to change each index value's type here.
 using VertexIndex = Dali::Scene3D::Algorithm::VertexIndex;
@@ -138,6 +143,14 @@ public:
   IntersectResult NavigationRayFaceIntersection(NavigationRay& ray, const Face& face) const;
 
   /**
+   * @brief Test ray against the mesh and returns intersection result
+   * @param[in] rayOrig Input ray to test collision
+   *
+   * @return Valid IntersectResult structure
+   */
+  IntersectResult RayCastIntersect(NavigationRay& rayOrig) const;
+
+  /**
    * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::PointSceneToLocal()
    */
   Dali::Vector3 PointSceneToLocal(const Dali::Vector3& point) const;
@@ -152,6 +165,15 @@ public:
    */
   [[nodiscard]] Dali::Vector3 GetGravityVector() const;
 
+  /**
+   * @brief Returns binary data of the mesh
+   * @return Reference to the binary buffer
+   */
+  [[nodiscard]] const std::vector<uint8_t>& GetData() const
+  {
+    return mBuffer;
+  }
+
 private:
   std::vector<uint8_t>     mBuffer;           //< Data buffer
   NavigationMeshHeader_V10 mHeader;           //< Navigation mesh header
@@ -165,6 +187,11 @@ inline Internal::Algorithm::NavigationMesh& GetImplementation(Dali::Scene3D::Alg
   return *navigationMesh.mImpl;
 }
 
+inline const Internal::Algorithm::NavigationMesh& GetImplementation(const Dali::Scene3D::Algorithm::NavigationMesh& navigationMesh)
+{
+  return *navigationMesh.mImpl;
+}
+
 } // namespace Dali::Scene3D::Internal::Algorithm
 
 #endif // DALI_SCENE3D_INTERNAL_NAVIGATION_MESH_H
index 315a368..0171b1e 100644 (file)
@@ -34,6 +34,7 @@
 #include <dali-scene3d/internal/common/image-resource-loader.h>
 #include <dali-scene3d/internal/common/model-cache-manager.h>
 #include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
+#include <dali-scene3d/internal/event/collider-mesh-processor.h>
 #include <dali-scene3d/internal/light/light-impl.h>
 #include <dali-scene3d/internal/model-components/model-node-impl.h>
 #include <dali-scene3d/public-api/controls/model/model.h>
@@ -45,7 +46,7 @@
 #include <dali-scene3d/public-api/loader/scene-definition.h>
 #include <dali-scene3d/public-api/loader/shader-manager.h>
 #include <dali-scene3d/public-api/model-motion/motion-index/blend-shape-index.h>
-
+#include <dali-toolkit/public-api/controls/control-impl.h>
 using namespace Dali;
 
 namespace Dali
@@ -312,14 +313,48 @@ void Model::AddModelNode(Scene3D::ModelNode modelNode)
     UpdateImageBasedLightScaleFactor();
   }
 
+  GetImplementation(modelNode).SetRootModel(*this);
+
+  // If model has a collider mesh set, add it to the container
+  if(modelNode.HasColliderMesh())
+  {
+    Scene3D::ColliderMeshProcessor::Get().ColliderMeshChanged(Scene3D::Model::DownCast(Self()));
+  }
+
   if(Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
   {
     NotifyResourceReady();
   }
 }
 
+void Model::RegisterColliderMesh(Scene3D::ModelNode& modelNode, const Dali::Scene3D::Algorithm::ColliderMesh& mesh)
+{
+  mColliderMeshes[modelNode.GetProperty<int>(Actor::Property::ID)] = modelNode;
+
+  // Add processor
+  Scene3D::ColliderMeshProcessor::Get().ColliderMeshChanged(Scene3D::Model::DownCast(Self()));
+}
+
+void Model::RemoveColliderMesh(Scene3D::ModelNode& node)
+{
+  auto id   = node.GetProperty<int>(Actor::Property::ID);
+  auto iter = std::find_if(mColliderMeshes.begin(), mColliderMeshes.end(), [id](auto& item) {
+    return item.first == id;
+  });
+  if(iter != mColliderMeshes.end())
+  {
+    mColliderMeshes.erase(iter);
+  }
+}
+
 void Model::RemoveModelNode(Scene3D::ModelNode modelNode)
 {
+  // remove collider mesh from the list if node is being removed
+  if(modelNode.HasColliderMesh())
+  {
+    RemoveColliderMesh(modelNode);
+  }
+
   if(mModelRoot)
   {
     UpdateShaderRecursively(modelNode, nullptr);
index b740da4..ff9ba0c 100644 (file)
 #include <dali/public-api/object/property-notification.h>
 #include <dali/public-api/object/weak-handle.h>
 #include <dali/public-api/rendering/texture.h>
+#include <unordered_map>
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/common/environment-map-load-task.h>
 #include <dali-scene3d/internal/common/light-observer.h>
 #include <dali-scene3d/internal/common/model-load-task.h>
+#include <dali-scene3d/internal/model-components/model-node-impl.h>
 #include <dali-scene3d/public-api/controls/model/model.h>
 #include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
 #include <dali-scene3d/public-api/light/light.h>
@@ -56,6 +58,11 @@ public:
   using CameraData             = Loader::CameraParameters;
   using BlendShapeModelNodeMap = std::map<std::string, std::vector<Scene3D::ModelNode>>;
 
+  // ColliderMeshContainer doesn't hold actual collider meshes
+  // but pairs unique ModelNode id with ModelNode for lookup purposes.
+  // All model nodes in the container have collider mesh attached.
+  using ColliderMeshContainer = std::unordered_map<int, Scene3D::ModelNode>;
+
   /**
    * @copydoc Model::New()
    */
@@ -166,6 +173,53 @@ public:
    */
   void SetMotionData(Scene3D::MotionData motionData);
 
+  /**
+   * @copydoc Scene3D::Model::MeshHitSignal()
+   */
+  Scene3D::Model::MeshHitSignalType& MeshHitSignal()
+  {
+    return mMeshHitSignal;
+  }
+
+  /**
+   * @brief Emits MeshHitSignal
+   * @param[in] modelNode ModelNode that has been hit
+   * @return Result of hit handling.
+   */
+  bool EmitMeshHitSignal(Scene3D::ModelNode modelNode)
+  {
+    bool retVal = false;
+    if(!mMeshHitSignal.Empty())
+    {
+      Scene3D::Model handle(GetOwner());
+      retVal = mMeshHitSignal.Emit(handle, modelNode);
+    }
+    return retVal;
+  }
+
+  /**
+   * @brief Returns collider mesh container
+   * @return Returns valid container
+   */
+  const ColliderMeshContainer& GetNodeColliderMeshContainer() const
+  {
+    return mColliderMeshes;
+  }
+
+  /**
+   * @brief Registers child node with collidier mesh
+   *
+   * @param[in] node ModelNode to register
+   * @param[in] mesh Collider mesh to associate with model node
+   */
+  void RegisterColliderMesh(Scene3D::ModelNode& node, const Dali::Scene3D::Algorithm::ColliderMesh& mesh);
+
+  /**
+   * @brief Removes node/collider mesh from the register
+   * @param[in] node Child node to remove from the register
+   */
+  void RemoveColliderMesh(Scene3D::ModelNode& node);
+
 protected:
   /**
    * @brief Constructs a new Model.
@@ -356,8 +410,13 @@ private:
   WeakHandle<Scene3D::SceneView> mParentSceneView;
   Dali::PropertyNotification     mSizeNotification;
 
+  // Signals
+  Scene3D::Model::MeshHitSignalType mMeshHitSignal;
+
   Dali::Scene3D::Loader::ShaderManagerPtr mShaderManager;
 
+  ColliderMeshContainer mColliderMeshes;
+
   // List of ModelNode for name of blend shape.
   BlendShapeModelNodeMap mBlendShapeModelNodeMap;
 
diff --git a/dali-scene3d/internal/event/collider-mesh-processor-impl.cpp b/dali-scene3d/internal/event/collider-mesh-processor-impl.cpp
new file mode 100644 (file)
index 0000000..d8ce462
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-scene3d/internal/event/collider-mesh-processor-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/actors/actor-devel.h>
+#include <dali/devel-api/events/hit-test-algorithm.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
+#include <dali/public-api/events/touch-event.h>
+#include <dali/public-api/render-tasks/render-task-list.h>
+#include <algorithm>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/controls/model/model-impl.h>
+#include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
+#include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
+
+namespace Dali::Scene3D::Internal
+{
+namespace
+{
+struct ColliderMeshData
+{
+  Scene3D::Model                                model;
+  Scene3D::ModelNode                            modelNode;
+  const Dali::Scene3D::Algorithm::ColliderMesh& colliderMesh;
+  Matrix                                        worldMatrix{false};
+};
+using ColliderMeshDataContainer = std::vector<ColliderMeshData>;
+
+void IterateThroughChildren(Actor actor, ColliderMeshDataContainer& meshData)
+{
+  if(actor)
+  {
+    const auto childCount = actor.GetChildCount();
+    for(auto i = 0u; i < childCount; ++i)
+    {
+      Actor          child = actor.GetChildAt(i);
+      Scene3D::Model model = Scene3D::Model::DownCast(child);
+      if(model)
+      {
+        const Model::ColliderMeshContainer& colliderMeshes = GetImpl(model).GetNodeColliderMeshContainer();
+        for(const auto& colliderMeshItem : colliderMeshes)
+        {
+          // If actor name is empty, then assume the mesh if for the model itself
+          [[maybe_unused]] int     actorId   = colliderMeshItem.first;
+          Dali::Scene3D::ModelNode modelNode = colliderMeshItem.second;
+          if(modelNode)
+          {
+            meshData.push_back({model, modelNode, GetImplementation(modelNode).GetColliderMesh(), DevelActor::GetWorldTransform(modelNode)});
+          }
+        }
+      }
+      IterateThroughChildren(child, meshData);
+    }
+  }
+}
+
+class SceneViewTouchHandler
+{
+public:
+  bool operator()(Actor actor, const TouchEvent& touchEvent)
+  {
+    Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(actor);
+    bool               retVal(false);
+    if(sceneView)
+    {
+      // Get nav-mesh information for the children
+      std::vector<ColliderMeshData> meshData;
+      IterateThroughChildren(sceneView, meshData);
+
+      auto renderTask  = touchEvent.GetRenderTask();
+      auto cameraActor = renderTask.GetCameraActor();
+
+      const auto& result = touchEvent.GetScreenPosition(0);
+      float       x = 0.0f, y = 0.0f;
+      Vector3     origin;
+      Vector3     direction;
+      cameraActor.ScreenToLocal(x, y, result.x, result.y);
+
+      auto                  list      = Stage::GetCurrent().GetRenderTaskList();
+      [[maybe_unused]] auto taskCount = list.GetTaskCount();
+      renderTask                      = list.GetTask(list.GetTaskCount() - 1);
+
+      if(HitTestAlgorithm::BuildPickingRay(renderTask, result, origin, direction))
+      {
+        for(auto& mesh : meshData)
+        {
+          // Set transform for the collider mesh
+          const_cast<Dali::Scene3D::Algorithm::ColliderMesh&>(mesh.colliderMesh).SetSceneTransform(mesh.worldMatrix);
+          auto res = mesh.colliderMesh.RayFaceIntersect(origin, direction);
+          if(res != Dali::Scene3D::Algorithm::NavigationMesh::NULL_FACE)
+          {
+            Scene3D::Model            model     = mesh.model;
+            Scene3D::Internal::Model& modelImpl = GetImpl(model);
+            retVal                              = modelImpl.EmitMeshHitSignal(mesh.modelNode);
+            break;
+          }
+        }
+      }
+    }
+    return retVal;
+  }
+};
+
+} // unnamed namespace
+
+ColliderMeshProcessor::ColliderMeshProcessor()
+{
+  Adaptor::Get().RegisterProcessor(*this, true);
+}
+
+ColliderMeshProcessor::~ColliderMeshProcessor()
+{
+  if(Adaptor::IsAvailable())
+  {
+    Adaptor::Get().UnregisterProcessor(*this, true);
+  }
+}
+
+void ColliderMeshProcessor::ColliderMeshChanged(Scene3D::Model model)
+{
+  if(model.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
+  {
+    AddSceneViewParentToProcessingQueue(model);
+  }
+  else
+  {
+    // TODO: Check if signal already connected
+    model.OnSceneSignal().Connect(this, &ColliderMeshProcessor::ModelOnScene);
+  }
+}
+
+void ColliderMeshProcessor::ModelOnScene(Actor actor)
+{
+  Scene3D::Model model = Scene3D::Model::DownCast(actor);
+  if(model)
+  {
+    AddSceneViewParentToProcessingQueue(model);
+  }
+  model.OnSceneSignal().Disconnect(this, &ColliderMeshProcessor::ModelOnScene);
+}
+
+void ColliderMeshProcessor::AddSceneViewParentToProcessingQueue(Scene3D::Model model)
+{
+  Actor actor = model;
+  do
+  {
+    actor = actor.GetParent();
+    Scene3D::SceneView sceneView(Scene3D::SceneView::DownCast(actor));
+    if(sceneView)
+    {
+      mSceneViewsToProcess.push_back(sceneView);
+      break;
+    }
+  } while(actor);
+}
+
+void ColliderMeshProcessor::Process(bool /* postProcess */)
+{
+  // Remove any duplicates
+  std::sort(mSceneViewsToProcess.begin(), mSceneViewsToProcess.end());
+  mSceneViewsToProcess.erase(std::unique(mSceneViewsToProcess.begin(), mSceneViewsToProcess.end()), mSceneViewsToProcess.end());
+
+  for(auto sceneView : mSceneViewsToProcess)
+  {
+    std::vector<ColliderMeshData> meshData;
+    IterateThroughChildren(sceneView, meshData);
+
+    if(meshData.size())
+    {
+      // TODO: Get SceneView Camera parameters and calculate bounding box
+      // for now, it's a pass-thru algorthm
+      if(std::find(mConnectedSceneViews.begin(), mConnectedSceneViews.end(), sceneView) == mConnectedSceneViews.end())
+      {
+        mConnectedSceneViews.push_back(sceneView);
+        // TODO: Consider passing in camera parameters and meshData into SceneViewTouchHandler
+        sceneView.TouchedSignal().Connect(this, SceneViewTouchHandler());
+      }
+    }
+  }
+  mSceneViewsToProcess.clear();
+}
+
+} // namespace Dali::Scene3D::Internal
diff --git a/dali-scene3d/internal/event/collider-mesh-processor-impl.h b/dali-scene3d/internal/event/collider-mesh-processor-impl.h
new file mode 100644 (file)
index 0000000..45c7b18
--- /dev/null
@@ -0,0 +1,77 @@
+#pragma once
+
+/*
+ * 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 <dali/integration-api/processor-interface.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/object/base-object.h>
+#include <dali/public-api/signals/connection-tracker.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/event/collider-mesh-processor.h>
+#include <dali-scene3d/public-api/controls/model/model.h>
+#include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
+
+namespace Dali::Scene3D
+{
+namespace Internal
+{
+class ColliderMeshProcessor : public BaseObject, public Dali::ConnectionTracker, public Integration::Processor
+{
+public:
+  ColliderMeshProcessor();
+
+  ~ColliderMeshProcessor();
+
+  void ColliderMeshChanged(Scene3D::Model model);
+
+private:
+  void ModelOnScene(Actor actor);
+
+  void Process(bool /*postProcessor*/);
+
+  void AddSceneViewParentToProcessingQueue(Scene3D::Model model);
+
+private:
+  std::vector<Scene3D::SceneView> mSceneViewsToProcess;
+  std::vector<Scene3D::SceneView> mConnectedSceneViews;
+};
+
+} // namespace Internal
+
+inline Internal::ColliderMeshProcessor& GetImpl(ColliderMeshProcessor& obj)
+{
+  DALI_ASSERT_ALWAYS(obj);
+
+  Dali::BaseObject& handle = obj.GetBaseObject();
+
+  return static_cast<Internal::ColliderMeshProcessor&>(handle);
+}
+
+inline const Internal::ColliderMeshProcessor& GetImpl(const ColliderMeshProcessor& obj)
+{
+  DALI_ASSERT_ALWAYS(obj);
+
+  const Dali::BaseObject& handle = obj.GetBaseObject();
+
+  return static_cast<const Internal::ColliderMeshProcessor&>(handle);
+}
+
+} // namespace Dali::Scene3D
diff --git a/dali-scene3d/internal/event/collider-mesh-processor.cpp b/dali-scene3d/internal/event/collider-mesh-processor.cpp
new file mode 100644 (file)
index 0000000..9f0ca0d
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-scene3d/internal/event/collider-mesh-processor.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/common/singleton-service.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/event/collider-mesh-processor-impl.h>
+
+namespace Dali::Scene3D
+{
+ColliderMeshProcessor ColliderMeshProcessor::Get()
+{
+  ColliderMeshProcessor processor;
+
+  // Check whether the processor has already created
+  SingletonService singletonService(SingletonService::Get());
+  if(singletonService)
+  {
+    Dali::BaseHandle handle = singletonService.GetSingleton(typeid(ColliderMeshProcessor));
+    if(handle)
+    {
+      // If so, downcast the handle of singleton to focus manager
+      processor = ColliderMeshProcessor(dynamic_cast<Internal::ColliderMeshProcessor*>(handle.GetObjectPtr()));
+    }
+
+    if(!processor)
+    {
+      // If not, create the focus manager and register it as a singleton
+      processor = ColliderMeshProcessor(new Internal::ColliderMeshProcessor());
+      singletonService.Register(typeid(processor), processor);
+    }
+  }
+
+  return processor;
+}
+
+void ColliderMeshProcessor::ColliderMeshChanged(Scene3D::Model model)
+{
+  GetImpl(*this).ColliderMeshChanged(model);
+}
+
+ColliderMeshProcessor::ColliderMeshProcessor(Internal::ColliderMeshProcessor* impl)
+: BaseHandle(impl)
+{
+}
+
+} // namespace Dali::Scene3D
diff --git a/dali-scene3d/internal/event/collider-mesh-processor.h b/dali-scene3d/internal/event/collider-mesh-processor.h
new file mode 100644 (file)
index 0000000..0d874ee
--- /dev/null
@@ -0,0 +1,47 @@
+#pragma once
+
+/*
+ * 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 <dali/public-api/object/base-handle.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/public-api/controls/model/model.h>
+
+namespace Dali::Scene3D
+{
+namespace Internal
+{
+class ColliderMeshProcessor;
+}
+
+class ColliderMeshProcessor : public BaseHandle
+{
+public:
+  ColliderMeshProcessor()  = default;
+  ~ColliderMeshProcessor() = default;
+
+  static ColliderMeshProcessor Get();
+
+  void ColliderMeshChanged(Scene3D::Model model);
+
+private:
+  explicit ColliderMeshProcessor(Internal::ColliderMeshProcessor* impl);
+};
+
+} // namespace Dali::Scene3D
index 48025c6..1ca545c 100644 (file)
@@ -11,6 +11,8 @@ set(scene3d_src_files ${scene3d_src_files}
        ${scene3d_internal_dir}/common/model-load-task.cpp
        ${scene3d_internal_dir}/controls/model/model-impl.cpp
        ${scene3d_internal_dir}/controls/scene-view/scene-view-impl.cpp
+       ${scene3d_internal_dir}/event/collider-mesh-processor.cpp
+       ${scene3d_internal_dir}/event/collider-mesh-processor-impl.cpp
        ${scene3d_internal_dir}/light/light-impl.cpp
        ${scene3d_internal_dir}/loader/dli-loader-impl.cpp
        ${scene3d_internal_dir}/loader/gltf2-asset.cpp
index 6cbc92b..d566163 100644 (file)
@@ -24,6 +24,7 @@
 #include <dali/public-api/object/type-registry.h>
 
 // INTERNAL INCLUDES
+#include <dali-scene3d/internal/controls/model/model-impl.h>
 #include <dali-scene3d/internal/light/light-impl.h>
 #include <dali-scene3d/internal/model-components/model-primitive-impl.h>
 
@@ -405,6 +406,52 @@ void ModelNode::UpdateBoneMatrix(Scene3D::ModelPrimitive primitive)
   }
 }
 
+void ModelNode::SetColliderMesh(ColliderMeshUniquePtr&& colliderMesh)
+{
+  if(!colliderMesh && !mColliderMesh)
+  {
+    return;
+  }
+
+  if(!mParentModel) // find parent model if not set
+  {
+    auto parent = Self().GetParent();
+    while(parent)
+    {
+      auto modelHandle = Scene3D::Model::DownCast(parent);
+      if(modelHandle)
+      {
+        mParentModel = &GetImpl(modelHandle);
+        break;
+      }
+      parent = parent.GetParent();
+    }
+  }
+
+  // Resetting collider mesh if argument is nullptr
+  auto handle = Scene3D::ModelNode::DownCast(Self());
+  if(mParentModel)
+  {
+    if(mColliderMesh || colliderMesh == nullptr)
+    {
+      mParentModel->RemoveColliderMesh(handle);
+    }
+    mParentModel->RegisterColliderMesh(handle, *colliderMesh);
+  }
+
+  mColliderMesh = std::move(colliderMesh);
+}
+
+bool ModelNode::HasColliderMesh() const
+{
+  return mColliderMesh != nullptr;
+}
+
+const Scene3D::Algorithm::ColliderMesh& ModelNode::GetColliderMesh() const
+{
+  return *mColliderMesh;
+}
+
 } // namespace Internal
 
 } // namespace Scene3D
index 7399406..35f1c12 100644 (file)
@@ -27,6 +27,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/model-components/model-primitive-modify-observer.h>
+#include <dali-scene3d/public-api/algorithm/navigation-mesh.h>
 #include <dali-scene3d/public-api/light/light.h>
 #include <dali-scene3d/public-api/loader/mesh-definition.h>
 #include <dali-scene3d/public-api/loader/shader-manager.h>
@@ -34,6 +35,7 @@
 #include <dali-scene3d/public-api/loader/skinning-details.h>
 #include <dali-scene3d/public-api/model-components/model-node.h>
 #include <dali-scene3d/public-api/model-components/model-primitive.h>
+#include "dali-scene3d/public-api/controls/model/model.h"
 
 namespace Dali
 {
@@ -46,6 +48,7 @@ namespace Scene3D
 
 namespace Internal
 {
+using ColliderMeshUniquePtr = std::unique_ptr<Dali::Scene3D::Algorithm::ColliderMesh>;
 /**
  * @brief This is the internal base class for custom node of model.
  *
@@ -286,6 +289,30 @@ public: // Public Method
    */
   void OnRendererCreated(Renderer renderer) override;
 
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::SetColliderMesh()
+   */
+  void SetColliderMesh(ColliderMeshUniquePtr&& colliderMesh);
+
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::HasColliderMesh()
+   */
+  [[nodiscard]] bool HasColliderMesh() const;
+
+  /**
+   * @copydoc Dali::Scene3D::ModelNode::GetColliderMesh()
+   */
+  [[nodiscard]] const Scene3D::Algorithm::ColliderMesh& GetColliderMesh() const;
+
+  /**
+  * @brief Sets a root model for the ModelNode
+  * @param[in] model Valid Model to set
+  */
+  void SetRootModel(Dali::Scene3D::Internal::Model& model)
+  {
+    mParentModel = &model;
+  }
+
 private:
   /**
    * @brief Updates the bone matrix for a ModelPrimitive.
@@ -294,7 +321,6 @@ private:
    */
   void UpdateBoneMatrix(Scene3D::ModelPrimitive primitive);
 
-private:
   /// @cond internal
 
   // Not copyable or movable
@@ -311,8 +337,14 @@ private:
   Dali::Texture                     mShadowMapTexture;
   Dali::Texture                     mSpecularTexture;
   Dali::Texture                     mDiffuseTexture;
-  float                             mIblScaleFactor{1.0f};
-  uint32_t                          mSpecularMipmapLevels{1u};
+
+  Internal::Model* mParentModel{nullptr};
+
+  // Collider mesh
+  std::unique_ptr<Dali::Scene3D::Algorithm::ColliderMesh> mColliderMesh;
+
+  float    mIblScaleFactor{1.0f};
+  uint32_t mSpecularMipmapLevels{1u};
   /// @endcond
 };
 
index 9b594d1..4aff076 100644 (file)
@@ -91,4 +91,18 @@ Dali::Vector3 NavigationMesh::GetGravityVector() const
   return mImpl->GetGravityVector();
 }
 
+FaceIndex NavigationMesh::RayFaceIntersect(const Vector3& origin, const Vector3& direction) const
+{
+  Internal::Algorithm::NavigationRay ray;
+  ray.origin    = origin;
+  ray.direction = direction;
+
+  auto result = mImpl->RayCastIntersect(ray);
+  if(result.result)
+  {
+    return result.faceIndex;
+  }
+  return NULL_FACE;
+}
+
 } // namespace Dali::Scene3D::Algorithm
\ No newline at end of file
index 727f9fc..62e4a08 100644 (file)
@@ -252,6 +252,16 @@ public:
    */
   Dali::Vector3 GetGravityVector() const;
 
+  /**
+   * @brief Performs ray/face intersect test
+   * @param[in] origin Origin of ray
+   * @param[in] direction Direction of ray
+   *
+   * @SINCE_2_3.0
+   * @return Valid FaceIndex on hit or NULL_FACE on miss
+   */
+  [[nodiscard]] FaceIndex RayFaceIntersect(const Vector3& origin, const Vector3& direction) const;
+
   static constexpr FaceIndex NULL_FACE{std::numeric_limits<FaceIndex>::max()}; ///< Represents null face
   static constexpr EdgeIndex NULL_EDGE{std::numeric_limits<EdgeIndex>::max()}; ///< Represents null edge
 
@@ -260,5 +270,13 @@ public:
 
   std::unique_ptr<NavigationMeshImpl> mImpl;
 };
+
+// Alias name for collider mesh
+// TODO: currently ColliderMesh is NavigationMesh however
+//       there should be separation from data and algorithms.
+//       Both, NavigationMesh and ColliderMesh use the same
+//       data structures but differ in the way they use data.
+using ColliderMesh = NavigationMesh;
+
 } // namespace Dali::Scene3D::Algorithm
 #endif // DALI_SCENE3D_NAVIGATION_MESH_H
index a02e466..c6beb61 100644 (file)
 #include <dali-scene3d/internal/controls/model/model-impl.h>
 #include <dali-scene3d/public-api/model-components/model-node.h>
 
-namespace Dali
+namespace Dali::Scene3D
 {
-namespace Scene3D
-{
-Model::Model()
-{
-}
+Model::Model() = default;
 
 Model::Model(const Model& model) = default;
 
@@ -38,9 +34,7 @@ Model& Model::operator=(const Model& model) = default;
 
 Model& Model::operator=(Model&& rhs) noexcept = default;
 
-Model::~Model()
-{
-}
+Model::~Model() = default;
 
 Model Model::New(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
 {
@@ -168,6 +162,9 @@ void Model::SetMotionData(MotionData motionData)
   GetImpl(*this).SetMotionData(motionData);
 }
 
-} // namespace Scene3D
+Model::MeshHitSignalType& Model::MeshHitSignal()
+{
+  return GetImpl(*this).MeshHitSignal();
+}
 
-} // namespace Dali
+} // namespace Dali::Scene3D
index 599eb91..b3c5ab3 100644 (file)
  */
 
 // EXTERNAL INCLUDES
+#include <memory>
+
 #include <dali-toolkit/public-api/controls/control.h>
 #include <dali/public-api/actors/camera-actor.h>
 #include <dali/public-api/common/dali-common.h>
 #include <dali/public-api/rendering/texture.h>
 
 // INTERNAL INCLUDES
+#include <dali-scene3d/public-api/algorithm/navigation-mesh.h>
 #include <dali-scene3d/public-api/api.h>
 #include <dali-scene3d/public-api/model-components/model-node.h>
 #include <dali-scene3d/public-api/model-motion/motion-data.h>
@@ -71,6 +74,10 @@ class Model;
 class DALI_SCENE3D_API Model : public Dali::Toolkit::Control
 {
 public:
+  // Typedefs
+  using MeshHitSignalType = Signal<bool(Model, Scene3D::ModelNode)>; ///< Mesh hit signal type @SINCE_2_2.99 TODO: See what parameters need to be added
+  using ColliderMeshPtr   = std::unique_ptr<Algorithm::NavigationMesh>;
+
   /**
    * @brief Create an initialized Model.
    *
@@ -366,6 +373,23 @@ public:
    */
   void SetMotionData(Scene3D::MotionData motionData);
 
+  /**
+   * @brief This signal is emitted when the collider mesh is touched/hit.
+   *
+   * A callback of the following type may be connected:
+   * @code
+   *   bool YourCallbackName(Model model, ModelNode modelNode);
+   * @endcode
+   * Here the model is the model that is hit and the ModelNode containing the collider mesh
+   * was applied to.
+   * The return value of True, indicates that the hover event should be consumed.
+   * Otherwise the signal will be emitted on the next sensitive parent of the actor.
+   *
+   * @SINCE_2_3.0
+   * @return The signal to connect to
+   */
+  MeshHitSignalType& MeshHitSignal();
+
 public: // Not intended for application developers
   /// @cond internal
   /**
index 7e09332..c54cb73 100644 (file)
@@ -23,6 +23,8 @@
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/algorithm/navigation-mesh-impl.h>
 #include <dali/devel-api/adaptor-framework/file-stream.h>
+#include <stdlib.h>
+#include <memory>
 
 namespace Dali::Scene3D::Loader
 {
@@ -78,7 +80,133 @@ std::unique_ptr<Algorithm::NavigationMesh> NavigationMeshFactory::CreateFromFile
 std::unique_ptr<Algorithm::NavigationMesh> NavigationMeshFactory::CreateFromBuffer(const std::vector<uint8_t>& buffer)
 {
   auto impl = new Scene3D::Internal::Algorithm::NavigationMesh(buffer);
-  return std::unique_ptr<Algorithm::NavigationMesh>(new Algorithm::NavigationMesh(impl));
+  return std::make_unique<Algorithm::NavigationMesh>(impl);
+}
+
+std::unique_ptr<Algorithm::NavigationMesh> NavigationMeshFactory::CreateFromVertexFaceList(const Vector3* vertices, const Vector3* vertexNormals, uint32_t vertexCount, const uint32_t* faceIndices, uint32_t indexCount)
+{
+  // The function takes the data and creates a binary buffer out of it
+  using namespace Dali::Scene3D::Algorithm;
+  auto header = Internal::Algorithm::NavigationMeshHeader_V10();
+
+  // create header
+  header.checksum = *reinterpret_cast<const uint32_t*>("NAVM");
+  header.version  = 0; // latest version
+
+  // Copy given vertices
+  std::vector<NavigationMesh::Vertex> meshVertices;
+  meshVertices.reserve(vertexCount);
+  for(auto i = 0u; i < vertexCount; ++i)
+  {
+    meshVertices.emplace_back();
+    meshVertices.back().x = vertices[i].x;
+    meshVertices.back().y = vertices[i].y;
+    meshVertices.back().z = vertices[i].z;
+  }
+
+  // copy faces and edges
+  std::vector<NavigationMesh::Face> meshFaces;
+  meshFaces.resize(indexCount / 3);
+  auto i              = 0u;
+  bool computeNormals = (vertexNormals == nullptr);
+  for(auto& f : meshFaces)
+  {
+    f.vertex[0] = faceIndices[i];
+    f.vertex[1] = faceIndices[i + 1];
+    f.vertex[2] = faceIndices[i + 2];
+
+    // compute normals (if not supplied)
+    if(computeNormals)
+    {
+      auto v01 = Vector3(meshVertices[f.vertex[1]].coordinates) - Vector3(meshVertices[f.vertex[0]].coordinates);
+      auto v02 = Vector3(meshVertices[f.vertex[2]].coordinates) - Vector3(meshVertices[f.vertex[0]].coordinates);
+      auto n   = v01.Cross(v02);
+      n.Normalize();
+      f.normal[0] = n.x;
+      f.normal[1] = n.y;
+      f.normal[2] = n.z;
+    }
+    else
+    {
+      auto& n0 = vertexNormals[faceIndices[i]];
+      auto& n1 = vertexNormals[faceIndices[i + 1]];
+      auto& n2 = vertexNormals[faceIndices[i + 2]];
+
+      auto faceNormal = (n0 + n1 + n2) / 3.0f;
+      faceNormal.Normalize();
+      f.normal[0] = faceNormal.x;
+      f.normal[1] = -faceNormal.y;
+      f.normal[2] = faceNormal.z;
+    }
+    i += 3;
+  }
+
+  // Create edges, in this case we don't care about duplicates
+  // This mesh cannot be used for navigation
+  std::vector<NavigationMesh::Edge> meshEdges;
+  meshEdges.reserve(meshFaces.size() * 3);
+  i = 0;
+  for(auto& f : meshFaces)
+  {
+    for(auto k = 0u; k < 3; ++k)
+    {
+      meshEdges.emplace_back();
+      auto& edge     = meshEdges.back();
+      edge.face[0]   = i;
+      edge.face[1]   = NavigationMesh::NULL_FACE;
+      edge.vertex[0] = f.vertex[k];
+      edge.vertex[1] = f.vertex[(k + 1) % 3];
+    }
+    ++i;
+  }
+
+  std::vector<uint8_t> navigationMeshBinary;
+
+  // Build navigationMeshBinary binary
+  navigationMeshBinary.insert(navigationMeshBinary.end(),
+              reinterpret_cast<uint8_t*>(&header),
+              reinterpret_cast<uint8_t*>(&header) + sizeof(Internal::Algorithm::NavigationMeshHeader_V10));
+
+  auto& h = *reinterpret_cast<decltype(header)*>(navigationMeshBinary.data());
+
+  h.dataOffset       = sizeof(header);
+  h.edgeCount        = meshEdges.size();
+  h.polyCount        = meshFaces.size();
+  h.vertexCount      = meshVertices.size();
+  h.gravityVector[0] = 0.0f;
+  h.gravityVector[1] = -1.0f;
+  h.gravityVector[2] = 0.0f;
+  h.version          = 0;
+  h.vertexDataOffset = 0;
+  h.edgeDataOffset   = meshVertices.size() * sizeof(NavigationMesh::Vertex);
+  h.polyDataOffset   = h.edgeDataOffset + meshEdges.size() * sizeof(NavigationMesh::Edge);
+
+  // Copy data
+  navigationMeshBinary.insert(navigationMeshBinary.end(),
+              reinterpret_cast<uint8_t*>(meshVertices.data()),
+              reinterpret_cast<uint8_t*>(meshVertices.data()) + (meshVertices.size() * sizeof(NavigationMesh::Vertex)));
+  navigationMeshBinary.insert(navigationMeshBinary.end(),
+              reinterpret_cast<uint8_t*>(meshEdges.data()),
+              reinterpret_cast<uint8_t*>(meshEdges.data()) + (meshEdges.size() * sizeof(NavigationMesh::Edge)));
+  navigationMeshBinary.insert(navigationMeshBinary.end(),
+              reinterpret_cast<uint8_t*>(meshFaces.data()),
+              reinterpret_cast<uint8_t*>(meshFaces.data()) + (meshFaces.size() * sizeof(NavigationMesh::Face)));
+
+  auto retval = std::move(NavigationMeshFactory::CreateFromBuffer(navigationMeshBinary));
+  return retval;
+}
+
+std::unique_ptr<Algorithm::NavigationMesh> NavigationMeshFactory::CreateFromVertexFaceList(const std::vector<Vector3>& vertices, const std::vector<Vector3>& normals, const std::vector<uint32_t>& faceIndices)
+{
+  return CreateFromVertexFaceList(vertices.data(), normals.data(), vertices.size(), faceIndices.data(), faceIndices.size());
+}
+
+std::vector<uint8_t> NavigationMeshFactory::GetMeshBinary(const Dali::Scene3D::Algorithm::NavigationMesh& navigationMesh)
+{
+  auto& meshImpl = Internal::Algorithm::GetImplementation(navigationMesh);
+
+  // Return as mutable copy
+  return meshImpl.GetData();
 }
 
 } // namespace Dali::Scene3D::Loader
\ No newline at end of file
index bbe5f00..b3dd576 100644 (file)
@@ -45,6 +45,48 @@ public:
    * @return Valid NavigationMesh or nullptr
    */
   static std::unique_ptr<Algorithm::NavigationMesh> CreateFromBuffer(const std::vector<uint8_t>& buffer);
+
+  /**
+   * @brief Creates new mesh from lists of vertices and faces
+   *
+   * List of faces contains indices into the vertex list
+   *
+   * @SINCE_2_3.0
+   * @param[in] vertices List of Vector3 vertices
+   * @param[in] vertexNormals List of Vector3 vertices
+   * @param[in] faceIndices List of faces
+   * @return Valid NavigationMesh or nullptr
+   */
+  static std::unique_ptr<Algorithm::NavigationMesh> CreateFromVertexFaceList(const std::vector<Vector3>& vertices, const std::vector<Vector3>& vertexNormals, const std::vector<uint32_t>& faceIndices);
+
+  /**
+   * @brief Creates new mesh from lists of vertices and faces
+   *
+   * List of faces contains indices into the vertex list
+   *
+   * This function reduces number of array copys when called from NUI.
+   *
+   * @SINCE_2_3.0
+   * @param[in] vertices Pointer to C-style array of vertices
+   * @param[in] vertexCount Number of vertices
+   * @param[in] vertexNormals to C-style array of vertex normals
+   * @param[in] faceIndices Pointer to C-style array of face elements indices
+   * @param[in] indexCount Number of indices
+   * @return Valid NavigationMesh or nullptr
+   */
+  static std::unique_ptr<Algorithm::NavigationMesh> CreateFromVertexFaceList(const Vector3* vertices, const Vector3* vertexNormals, uint32_t vertexCount, const uint32_t* faceIndices, uint32_t indexCount);
+
+  /**
+   * @brief Serializes mesh data to the binary format.
+   *
+   * The binary data returned by the function can be used
+   * as an input for NavigationMeshFactory::CreateFromBuffer()
+   *
+   * @SINCE_2_3.0
+   * @param[in] navigationMesh Navigation mesh to serialize
+   * @return Buffer containing serialized mesh data
+   */
+  static std::vector<uint8_t> GetMeshBinary(const Dali::Scene3D::Algorithm::NavigationMesh& navigationMesh);
 };
 } // namespace Dali::Scene3D::Loader
 
index f4bd758..2832531 100644 (file)
@@ -128,6 +128,21 @@ Loader::BlendShapes::Index ModelNode::GetBlendShapeIndexByName(std::string_view
   return Internal::GetImplementation(*this).GetBlendShapeIndexByName(blendShapeName);
 }
 
+void ModelNode::SetColliderMesh(std::unique_ptr<Algorithm::ColliderMesh>&& colliderMesh)
+{
+  Internal::GetImplementation(*this).SetColliderMesh(std::move(colliderMesh));
+}
+
+const Algorithm::ColliderMesh& ModelNode::GetColliderMesh()
+{
+  return Internal::GetImplementation(*this).GetColliderMesh();
+}
+
+bool ModelNode::HasColliderMesh() const
+{
+  return Internal::GetImplementation(*this).HasColliderMesh();
+}
+
 } // namespace Scene3D
 
 } // namespace Dali
index 508b80c..743eec2 100644 (file)
 #include <dali/public-api/common/dali-common.h>
 
 // INTERNAL INCLUDES
+#include <dali-scene3d/public-api/algorithm/navigation-mesh.h>
 #include <dali-scene3d/public-api/api.h>
 #include <dali-scene3d/public-api/loader/blend-shape-details.h> ///< For Loader::BlendShapes::Index
 #include <dali-scene3d/public-api/model-components/model-primitive.h>
-
 namespace Dali
 {
 namespace Scene3D
@@ -205,6 +205,40 @@ public: // Public Method
    */
   Loader::BlendShapes::Index GetBlendShapeIndexByName(std::string_view blendShapeName) const;
 
+  /**
+   * @brief Sets collider mesh on the ModelNode
+   *
+   * The ownership of a collider mesh is taken over by the ModelNode.
+   *
+   * If there was a collider mesh set previously it will be erased.
+   *
+   * To remove collider mesh empty unique_ptr must be passed.
+   *
+   * @SINCE_2_3.0
+   * @param[in] colliderMesh r-value to unique pointer of ColliderMesh
+   */
+  void SetColliderMesh(std::unique_ptr<Algorithm::ColliderMesh>&& colliderMesh);
+
+  /**
+   * @brief Returns associated collider mesh
+   *
+   * HasColliderMesh() must be called to determine whether a valid
+   * collider mesh is associated. Calling GetColliderMesh() without
+   * previous check may produce undefined behaviour.
+   *
+   * @SINCE_2_3.0
+   * @return Associated collider mesh
+   */
+  const Algorithm::ColliderMesh& GetColliderMesh();
+
+  /**
+   * @brief Determines whether there is a valid collider mesh set
+   *
+   * @SINCE_2_3.0
+   * @return True if collider mesh is set, False otherwise
+   */
+  [[nodiscard]] bool HasColliderMesh() const;
+
 public: // Not intended for application developers
   /// @cond internal
   /**