Oxygen Engine
Modern C++ 3D Engine using OpenGL
Loading...
Searching...
No Matches
mesh.h
1#ifndef OE_SCENE_MESH_H
2#define OE_SCENE_MESH_H
3
4#include <vector>
5#include <glm/glm.hpp>
6#include "vertex.h"
7#include "aabb.h"
8
9#include "../lib/mikktspace.h"
10
11#include <algorithm>
12#include <span>
13
14namespace oe::scene
15{
19 struct Primitive
20 {
21 size_t offset;
22 size_t size;
23 };
24
25 #if 0
29 template <typename VertexType, typename IndexType = uint32_t>
30 struct MeshView
31 {
32 std::span<VertexType> vertices;
33 std::span<IndexType> indices;
34 };
35
39 template <typename VertexType, typename IndexType = uint32_t>
40 struct ReadOnlyMeshView
41 {
42 std::span<const VertexType> vertices;
43 std::span<const IndexType> indices;
44 };
45 #endif
46
50 template <typename VertexType, typename IndexType>
52 {
53 using VertexContainerType = std::vector<VertexType>;
54 using IndexContainerType = std::vector<IndexType>;
55
56 using PrimitivesContainerType = std::vector<Primitive>;
57 };
58
59 template <typename VertexType = Vertex, typename IndexType = uint32_t, typename MeshSettingsType = DefaultMeshSettings<VertexType, IndexType>>
60 struct Mesh
61 {
62 using vertex_type = VertexType;
63 using index_type = IndexType;
64
65 using VertexContainerType = MeshSettingsType::VertexContainerType;
66 using IndexContainerType = MeshSettingsType::IndexContainerType;
67
68 VertexContainerType vertices;
69 IndexContainerType indices;
70
71 MeshSettingsType::PrimitivesContainerType primitives = {};
72
80 template <typename PrimitiveType>
81 static Mesh generateFromPrimitives(std::span<const PrimitiveType> primitives)
82 {
83 Mesh result;
84
85 // Get total vertices and indices sizes + Fill primitives data (indice offsets + sizes)
86 size_t total_vertices_count = 0;
87 size_t total_indices_count = 0;
88
89 result.primitives.reserve(primitives.size());
90
91 for (const auto &it : primitives)
92 {
93 result.primitives.emplace_back(total_indices_count, it.indices.size());
94
95 total_vertices_count += it.vertices.size();
96 total_indices_count += it.indices.size();
97 }
98
99 // Reserve mesh data and fill them
100 result.vertices.reserve(total_vertices_count);
101 result.indices.reserve(total_indices_count);
102
103 size_t vertices_count = 0;
104 for (const auto &it : primitives)
105 {
106 const auto& vertices = it.vertices;
107 const auto& indices = it.indices;
108
109 // Append vertices (copy as-is)
110 result.vertices.insert(result.vertices.end(), vertices.cbegin(), vertices.cend());
111
112 // Append indices (add vertices offset to make them work)
113 std::ranges::transform(
114 indices,
115 std::back_inserter(result.indices),
116 [vertices_count](const IndexType& i) { return vertices_count + i; }
117 );
118
119 // Update vertices count inserted so far (used as indices offset)
120 vertices_count += vertices.size();
121 }
122
123 return result;
124 }
125
129 constexpr AABB getPrimitiveBoundingBox(size_t primitive) const noexcept
130 {
131 assert(primitive < primitives.size());
132
133 const auto& primitive_data = primitives.at(primitive);
134
135 std::span<index_type> primitive_indices = {indices.begin() + primitive_data.offset, primitive_data.size};
136
137 AABB result;
138 bool is_first_vertex = true;
139
140 for (auto &it : primitive_indices)
141 {
142 const vertex_type& vertex = vertices.at(it);
143
144 if (is_first_vertex)
145 {
146 result = {vertex.position, vertex.position};
147 is_first_vertex = false;
148 }
149 else
150 {
151 result.extendToPoint(vertex.position);
152 }
153 }
154
155 return result;
156 }
157
162 template <typename F>
163 constexpr AABB getPrimitiveBoundingBox(size_t primitive, F&& predicate) const noexcept
164 {
165 assert(primitive < primitives.size());
166
167 const auto& primitive_data = primitives.at(primitive);
168
169 std::span<index_type> primitive_indices = {indices.begin() + primitive_data.offset, primitive_data.size};
170
171 AABB result;
172 bool is_first_vertex = true;
173
174 for (auto &it : primitive_indices)
175 {
176 const vertex_type& vertex = vertices.at(it);
177
178 if (predicate(vertex))
179 {
180 if (is_first_vertex)
181 {
182 result = {vertex.position, vertex.position};
183 is_first_vertex = false;
184 }
185 else
186 {
187 result.extendToPoint(vertex.position);
188 }
189 }
190 }
191
192 return result;
193 }
194
198 constexpr AABB getBoundingBox() const noexcept
199 {
200 AABB result;
201 bool is_first_vertex = true;
202
203 for (auto &vertex : vertices)
204 {
205 if (is_first_vertex)
206 {
207 result = {vertex.position, vertex.position};
208 is_first_vertex = false;
209 }
210 else
211 {
212 result.extendToPoint(vertex.position);
213 }
214 }
215
216 return result;
217 }
218
219 constexpr void flipFaces()
220 {
221 for (size_t i=0; i<indices.size(); i+=3)
222 {
223 std::swap(indices[i+2], indices[i+0]);
224 }
225 }
226
230 constexpr const std::vector<glm::uvec3> getTriangles() const
231 {
232 std::vector<glm::uvec3> result;
233
234 result.reserve(indices.size());
235
236 for (size_t i=0; i<indices.size(); i += 3)
237 {
238 result.emplace_back(
239 indices[i],
240 indices[i + 1],
241 indices[i + 2]
242 );
243 }
244
245 return result;
246 }
247
248 /*
249 * @brief Get groups of 3 indices that form triangles
250 */
251 /*constexpr const std::vector<glm::uvec3> getTriangles(uint32_t sub_mesh) const
252 {
253 std::vector<glm::uvec3> result;
254
255 result.reserve(indices.size());
256
257 for (size_t i=0; i<indices.size(); i += 3)
258 {
259 result.emplace_back(
260 indices[i],
261 indices[i + 1],
262 indices[i + 2]
263 );
264 }
265
266 return result;
267 }*/
268
272 constexpr void generateNormals()
273 {
274 std::vector<glm::uvec3> triangles = getTriangles();
275
276 if (triangles.size() == 0)
277 return;
278
279 // Triangle edges vectors + normal
280 glm::vec3 e1, e2, no;
281
282 std::vector<size_t> count_normals;
283 count_normals.resize(vertices.size());
284
285 for (size_t i=0; i<vertices.size(); i++)
286 {
287 vertices[i].normal = glm::vec3(0.0);
288 }
289
290 for (size_t i=0; i<triangles.size(); i++)
291 {
292 const glm::uvec3& face = triangles[i];
293
294 VertexType& va = vertices[face[0]];
295 VertexType& vb = vertices[face[1]];
296 VertexType& vc = vertices[face[2]];
297
298 e1 = vb.position - va.position;
299 e2 = vc.position - va.position;
300
301 no = glm::cross(e1, e2);
302
303 va.normal += no;
304 vb.normal += no;
305 vc.normal += no;
306
307 count_normals[face[0]]++;
308 count_normals[face[1]]++;
309 count_normals[face[2]]++;
310 }
311
312 for (size_t i=0; i<vertices.size(); i++)
313 {
314 if (count_normals[i] > 0)
315 {
316 vertices[i].normal = glm::normalize(vertices[i].normal / (float)count_normals[i]);
317 }
318 }
319 }
320
325 {
330
334 std::vector<glm::uvec3> triangles;
335 };
336
340 constexpr void generateTangents()
341 {
342 SMikkTSpaceInterface callbacks
343 {
344 .m_getNumFaces = [] (const SMikkTSpaceContext * pContext) -> int
345 {
346 // Tell MikkTSpace how many faces (ie triangles) the mesh have
347 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
348 const std::vector<glm::uvec3>& triangles = data->triangles;
349
350 return triangles.size();
351 },
352
353 .m_getNumVerticesOfFace = [] (const SMikkTSpaceContext*, const int32_t) -> int32_t
354 {
355 // We always deal with triangles
356 return 3;
357 },
358
359 .m_getPosition = [] (const SMikkTSpaceContext * pContext, float fvPosOut[], const int32_t iFace, const int32_t iVert)
360 {
361 // Tell MikkTSpace how to get a vertex position from face + vertex ids
362 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
363 const Mesh* mesh = data->mesh;
364 const std::vector<glm::uvec3>& triangles = data->triangles;
365
366 const std::vector<VertexType>& vertices = mesh->vertices;
367
368 const VertexType& vertex = vertices[triangles[iFace][iVert]];
369 const glm::vec3& position = vertex.position;
370
371 fvPosOut[0] = position.x;
372 fvPosOut[1] = position.y;
373 fvPosOut[2] = position.z;
374 },
375
376 .m_getNormal = [] (const SMikkTSpaceContext * pContext, float fvNormOut[], const int32_t iFace, const int32_t iVert)
377 {
378 // Tell MikkTSpace how to get a vertex normal from face + vertex ids
379 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
380 const Mesh* mesh = data->mesh;
381 const std::vector<glm::uvec3>& triangles = data->triangles;
382
383 const std::vector<VertexType>& vertices = mesh->vertices;
384
385 const VertexType& vertex = vertices[triangles[iFace][iVert]];
386 const glm::vec3& normal = vertex.normal;
387
388 fvNormOut[0] = normal.x;
389 fvNormOut[1] = normal.y;
390 fvNormOut[2] = normal.z;
391 },
392
393 .m_getTexCoord = [] (const SMikkTSpaceContext * pContext, float fvTexcOut[], const int32_t iFace, const int32_t iVert)
394 {
395 // Tell MikkTSpace how to get a vertex texture coordinate from face + vertex ids
396 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
397 const Mesh* mesh = data->mesh;
398 const std::vector<glm::uvec3>& triangles = data->triangles;
399
400 const std::vector<VertexType>& vertices = mesh->vertices;
401
402 const VertexType& vertex = vertices[triangles[iFace][iVert]];
403 const glm::vec2& tex_coords = vertex.tex_coords_0;
404
405 fvTexcOut[0] = tex_coords.x;
406 fvTexcOut[1] = tex_coords.y;
407 },
408
409 .m_setTSpaceBasic = [] (const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int32_t iFace, const int32_t iVert)
410 {
411 // Tell MikkTSpace how to set a vertex tangent values (tangent + bitangent) face + vertex ids
412 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
413 Mesh* mesh = data->mesh;
414 const std::vector<glm::uvec3>& triangles = data->triangles;
415
416 std::vector<VertexType>& vertices = mesh->vertices;
417
418 VertexType& vertex = vertices[triangles[iFace][iVert]];
419
420 vertex.tangent = glm::vec3(fvTangent[0], fvTangent[1], fvTangent[2]);
421 vertex.bitangent = fSign * glm::cross(vertex.tangent, vertex.normal);
422 },
423
424 .m_setTSpace = [] (const SMikkTSpaceContext *, const float[], const float[], const float , const float, const tbool, const int32_t, const int32_t)
425 {
426 // Not used, "basic" tangent space is sufficient for our use-case
427 }
428 };
429
430 // Init user data, the real magic here is getTriangles which is computed once and cached here instead of each time
431 MikkTSpaceUserData user_data = {
432 .mesh = this,
433 .triangles = getTriangles()
434 };
435
436 SMikkTSpaceContext context = {
437 .m_pInterface = &callbacks,
438 .m_pUserData = &user_data
439 };
440
441 genTangSpaceDefault(&context);
442 }
443 };
444
445 using SolidMesh = Mesh<Vertex>;
446 using SkinnedMesh = Mesh<SkinnedVertex>;
447}
448
449#endif
Axis-aligned bounding box.
Definition aabb.h:13
void extendToPoint(const glm::vec3 &point) noexcept
Update box size to contain the point.
Definition aabb.h:68
Scene related management (Render-agnostic Geometry, Manger, etc...)
Definition debug.h:19
Settings to specialize Mesh.
Definition mesh.h:52
Struct used to convert data to generate tangents from MikkTSpace.
Definition mesh.h:325
Mesh * mesh
pointer to the treated mesh
Definition mesh.h:329
std::vector< glm::uvec3 > triangles
list of triangles (vector of indices)
Definition mesh.h:334
Definition mesh.h:61
constexpr AABB getPrimitiveBoundingBox(size_t primitive) const noexcept
Generate bounding box for the specified primitive.
Definition mesh.h:129
constexpr AABB getPrimitiveBoundingBox(size_t primitive, F &&predicate) const noexcept
Generate bounding box for the specified primitive containing vertices matching a predicate.
Definition mesh.h:163
constexpr AABB getBoundingBox() const noexcept
Generate englobing mesh bounding box.
Definition mesh.h:198
static Mesh generateFromPrimitives(std::span< const PrimitiveType > primitives)
Generate a mesh from primitives.
Definition mesh.h:81
constexpr void generateNormals()
Generate mesh normals from vertices positions.
Definition mesh.h:272
constexpr void generateTangents()
Compute Tangents using mikkTSpace.
Definition mesh.h:340
constexpr const std::vector< glm::uvec3 > getTriangles() const
Get groups of 3 indices that form triangles.
Definition mesh.h:230
Part of a mesh (used to separate mesh materials for example)
Definition mesh.h:20