[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / navigation-mesh-factory.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 // CLASS HEADER
18 #include <dali-scene3d/public-api/loader/navigation-mesh-factory.h>
19
20 // EXTERNAL INCLUDES
21 #include <dali/integration-api/debug.h>
22
23 // INTERNAL INCLUDES
24 #include <dali-scene3d/internal/algorithm/navigation-mesh-impl.h>
25 #include <dali/devel-api/adaptor-framework/file-stream.h>
26 #include <stdlib.h>
27 #include <memory>
28
29 namespace Dali::Scene3D::Loader
30 {
31 std::unique_ptr<Algorithm::NavigationMesh> NavigationMeshFactory::CreateFromFile(std::string filename)
32 {
33   std::vector<uint8_t> buffer;
34
35   Dali::FileStream fileStream(filename, Dali::FileStream::READ | Dali::FileStream::BINARY);
36   auto             fin = fileStream.GetFile();
37
38   if(DALI_UNLIKELY(!fin))
39   {
40     const int bufferLength = 128;
41     char      buffer[bufferLength];
42
43     // Return type of stderror_r is different between system type. We should not use return value.
44     [[maybe_unused]] auto ret = strerror_r(errno, buffer, bufferLength - 1);
45
46     DALI_LOG_ERROR("NavigationMesh: Can't open %s for reading: %s", filename.c_str(), buffer);
47     return nullptr;
48   }
49
50   if(DALI_UNLIKELY(fseek(fin, 0, SEEK_END)))
51   {
52     DALI_LOG_ERROR("NavigationMesh: Error reading file: %s\n", filename.c_str());
53     return nullptr;
54   }
55
56   auto size = ftell(fin);
57   if(DALI_UNLIKELY(size < 0))
58   {
59     DALI_LOG_ERROR("NavigationMesh: Error reading file: %s\n", filename.c_str());
60     return nullptr;
61   }
62
63   auto fileSize = size_t(size);
64   if(DALI_UNLIKELY(fseek(fin, 0, SEEK_SET)))
65   {
66     DALI_LOG_ERROR("NavigationMesh: Error reading file: %s\n", filename.c_str());
67     return nullptr;
68   }
69
70   buffer.resize(size);
71   auto count = fread(buffer.data(), 1, fileSize, fin);
72   if(DALI_UNLIKELY(count != fileSize))
73   {
74     DALI_LOG_ERROR("NavigationMesh: Error reading file: %s\n", filename.c_str());
75     return nullptr;
76   }
77   return CreateFromBuffer(buffer);
78 }
79
80 std::unique_ptr<Algorithm::NavigationMesh> NavigationMeshFactory::CreateFromBuffer(const std::vector<uint8_t>& buffer)
81 {
82   auto impl = new Scene3D::Internal::Algorithm::NavigationMesh(buffer);
83   return std::make_unique<Algorithm::NavigationMesh>(impl);
84 }
85
86 std::unique_ptr<Algorithm::NavigationMesh> NavigationMeshFactory::CreateFromVertexFaceList(const Vector3* vertices, const Vector3* vertexNormals, uint32_t vertexCount, const uint32_t* faceIndices, uint32_t indexCount)
87 {
88   // The function takes the data and creates a binary buffer out of it
89   using namespace Dali::Scene3D::Algorithm;
90   auto header = Internal::Algorithm::NavigationMeshHeader_V10();
91
92   // create header
93   header.checksum = *reinterpret_cast<const uint32_t*>("NAVM");
94   header.version  = 0; // latest version
95
96   // Copy given vertices
97   std::vector<NavigationMesh::Vertex> meshVertices;
98   meshVertices.reserve(vertexCount);
99   for(auto i = 0u; i < vertexCount; ++i)
100   {
101     meshVertices.emplace_back();
102     meshVertices.back().x = vertices[i].x;
103     meshVertices.back().y = vertices[i].y;
104     meshVertices.back().z = vertices[i].z;
105   }
106
107   // copy faces and edges
108   std::vector<NavigationMesh::Face> meshFaces;
109   meshFaces.resize(indexCount / 3);
110   auto i              = 0u;
111   bool computeNormals = (vertexNormals == nullptr);
112   for(auto& f : meshFaces)
113   {
114     f.vertex[0] = faceIndices[i];
115     f.vertex[1] = faceIndices[i + 1];
116     f.vertex[2] = faceIndices[i + 2];
117
118     // compute normals (if not supplied)
119     if(computeNormals)
120     {
121       auto v01 = Vector3(meshVertices[f.vertex[1]].coordinates) - Vector3(meshVertices[f.vertex[0]].coordinates);
122       auto v02 = Vector3(meshVertices[f.vertex[2]].coordinates) - Vector3(meshVertices[f.vertex[0]].coordinates);
123       auto n   = v01.Cross(v02);
124       n.Normalize();
125       f.normal[0] = n.x;
126       f.normal[1] = n.y;
127       f.normal[2] = n.z;
128     }
129     else
130     {
131       auto& n0 = vertexNormals[faceIndices[i]];
132       auto& n1 = vertexNormals[faceIndices[i + 1]];
133       auto& n2 = vertexNormals[faceIndices[i + 2]];
134
135       auto faceNormal = (n0 + n1 + n2) / 3.0f;
136       faceNormal.Normalize();
137       f.normal[0] = faceNormal.x;
138       f.normal[1] = -faceNormal.y;
139       f.normal[2] = faceNormal.z;
140     }
141     i += 3;
142   }
143
144   // Create edges, in this case we don't care about duplicates
145   // This mesh cannot be used for navigation
146   std::vector<NavigationMesh::Edge> meshEdges;
147   meshEdges.reserve(meshFaces.size() * 3);
148   i = 0;
149   for(auto& f : meshFaces)
150   {
151     for(auto k = 0u; k < 3; ++k)
152     {
153       meshEdges.emplace_back();
154       auto& edge     = meshEdges.back();
155       edge.face[0]   = i;
156       edge.face[1]   = NavigationMesh::NULL_FACE;
157       edge.vertex[0] = f.vertex[k];
158       edge.vertex[1] = f.vertex[(k + 1) % 3];
159     }
160     ++i;
161   }
162
163   std::vector<uint8_t> navigationMeshBinary;
164
165   // Build navigationMeshBinary binary
166   navigationMeshBinary.insert(navigationMeshBinary.end(),
167               reinterpret_cast<uint8_t*>(&header),
168               reinterpret_cast<uint8_t*>(&header) + sizeof(Internal::Algorithm::NavigationMeshHeader_V10));
169
170   auto& h = *reinterpret_cast<decltype(header)*>(navigationMeshBinary.data());
171
172   h.dataOffset       = sizeof(header);
173   h.edgeCount        = meshEdges.size();
174   h.polyCount        = meshFaces.size();
175   h.vertexCount      = meshVertices.size();
176   h.gravityVector[0] = 0.0f;
177   h.gravityVector[1] = -1.0f;
178   h.gravityVector[2] = 0.0f;
179   h.version          = 0;
180   h.vertexDataOffset = 0;
181   h.edgeDataOffset   = meshVertices.size() * sizeof(NavigationMesh::Vertex);
182   h.polyDataOffset   = h.edgeDataOffset + meshEdges.size() * sizeof(NavigationMesh::Edge);
183
184   // Copy data
185   navigationMeshBinary.insert(navigationMeshBinary.end(),
186               reinterpret_cast<uint8_t*>(meshVertices.data()),
187               reinterpret_cast<uint8_t*>(meshVertices.data()) + (meshVertices.size() * sizeof(NavigationMesh::Vertex)));
188   navigationMeshBinary.insert(navigationMeshBinary.end(),
189               reinterpret_cast<uint8_t*>(meshEdges.data()),
190               reinterpret_cast<uint8_t*>(meshEdges.data()) + (meshEdges.size() * sizeof(NavigationMesh::Edge)));
191   navigationMeshBinary.insert(navigationMeshBinary.end(),
192               reinterpret_cast<uint8_t*>(meshFaces.data()),
193               reinterpret_cast<uint8_t*>(meshFaces.data()) + (meshFaces.size() * sizeof(NavigationMesh::Face)));
194
195   return NavigationMeshFactory::CreateFromBuffer(navigationMeshBinary);
196 }
197
198 std::unique_ptr<Algorithm::NavigationMesh> NavigationMeshFactory::CreateFromVertexFaceList(const std::vector<Vector3>& vertices, const std::vector<Vector3>& normals, const std::vector<uint32_t>& faceIndices)
199 {
200   return CreateFromVertexFaceList(vertices.data(), normals.data(), vertices.size(), faceIndices.data(), faceIndices.size());
201 }
202
203 std::vector<uint8_t> NavigationMeshFactory::GetMeshBinary(const Dali::Scene3D::Algorithm::NavigationMesh& navigationMesh)
204 {
205   auto& meshImpl = Internal::Algorithm::GetImplementation(navigationMesh);
206
207   // Return as mutable copy
208   return meshImpl.GetData();
209 }
210
211 } // namespace Dali::Scene3D::Loader