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 "../common.h"
5
6#include <vector>
7#include <glm/glm.hpp>
8#include <functional>
9#include "vertex.h"
10#include "aabb.h"
11
12#include "../lib/mikktspace.h"
13
14namespace oe::scene
15{
16 template<class F, class V>
20 concept VertexPredicateFunction = requires(F f, V v)
21 {
22 {f(v)} -> std::same_as<bool>;
23 };
24
29 {
30 public:
31 constexpr uint32_t getSubMeshCount() const noexcept
32 {
33 return _indices.size();
34 }
35
36 constexpr const std::vector<index_t>& getIndices(uint32_t sub_mesh = 0) const noexcept
37 {
38 return _indices[sub_mesh];
39 }
40
41 constexpr const AABB& getBoundingBox() const noexcept
42 {
43 return _aabb;
44 }
45
46 constexpr void flipFaces() noexcept
47 {
48 for (auto& submesh : _indices)
49 {
50 for (size_t i=0; i<submesh.size(); i+=3)
51 {
52 std::swap(submesh[i+2], submesh[i+0]);
53 }
54 }
55 }
56
60 constexpr const std::vector<glm::uvec3> getTriangles(uint32_t sub_mesh = 0) const
61 {
62 const std::vector<index_t>& indices = _indices.at(sub_mesh);
63
64 std::vector<glm::uvec3> result;
65
66 result.reserve(indices.size());
67
68 for (size_t i=0; i<indices.size(); i += 3)
69 {
70 result.emplace_back(
71 indices[i],
72 indices[i + 1],
73 indices[i + 2]
74 );
75 }
76
77 return result;
78 }
79
80 protected:
81 std::vector<std::vector<index_t>> _indices;
82
83 AABB _aabb;
84 };
85
89 template <SimpleVertex V>
90 class SimpleMesh : public CommonMesh
91 {
92 public:
93 using vertex_type = V;
94
95 SimpleMesh(const std::vector<V>& vertices, const std::vector<index_t>& indices = {}):
96 _vertices(vertices)
97 {
98 if (indices.size() > 0)
99 addSubMesh(indices);
100 }
101
102 SimpleMesh(const std::vector<V>& vertices, const std::vector<std::vector<index_t>>& mesh_indices):
103 _vertices(vertices)
104 {
105 for (auto& submesh : mesh_indices)
106 {
107 addSubMesh(submesh);
108 }
109 }
110
111 constexpr uint32_t addSubMesh(const std::vector<oe::scene::index_t>& indices) noexcept
112 {
113 _indices.push_back(indices);
114
115 // BBox is empty, init with first vertex
116 if (_aabb.isEmpty())
117 _aabb = {_vertices[indices[0]].position, _vertices[indices[0]].position};
118
119 for (auto& id_vertex : indices)
120 {
121 _aabb.extendToPoint(_vertices[id_vertex].position);
122 }
123
124 return _indices.size() -1;
125 }
126
127 constexpr std::vector<V>& getVertices() noexcept
128 {
129 return _vertices;
130 }
131
132 constexpr const std::vector<V>& getVertices() const noexcept
133 {
134 return _vertices;
135 }
136
142 template <VertexPredicateFunction<V> F>
143 constexpr AABB generateBoundingBoxFromPredicate(F&& predicate) const noexcept
144 {
145 const auto& vertices = _vertices;
146
147 AABB result;
148 bool is_first_vertex = true;
149
150 for (auto& it : vertices)
151 {
152 if (predicate(it))
153 {
154 if (is_first_vertex)
155 {
156 result = {it.position, it.position};
157 is_first_vertex = false;
158 }
159 else
160 {
161 result.extendToPoint(it.position);
162 }
163 }
164 }
165
166 return result;
167 }
168
169 protected:
170 SimpleMesh() {}
171
172 std::vector<V> _vertices;
173 };
174
178 void computeTangentsFromMikkTSpace(const SMikkTSpaceContext& context);
179
183 template <ComplexVertex V>
184 class ComplexMesh : public SimpleMesh<V>
185 {
186 public:
187 // Seamlessly use parent constructors
188 using SimpleMesh<V>::SimpleMesh;
189
196 constexpr void generateSmoothNormals(uint32_t sub_mesh = 0)
197 {
198 std::vector<glm::uvec3> triangles = CommonMesh::getTriangles(sub_mesh);
199 std::vector<V>& vertices = SimpleMesh<V>::_vertices;
200
201 if (triangles.size() == 0)
202 return;
203
204 // Triangle edges vectors + normal
205 glm::vec3 e1, e2, no;
206
207 std::vector<size_t> count_normals;
208
209 for (size_t i=0; i<vertices.size(); i++)
210 {
211 vertices[i].normal = glm::vec3(0.0);
212 count_normals.push_back(0);
213 }
214
215 for (size_t i=0; i<triangles.size(); i++)
216 {
217 const glm::uvec3& face = triangles[i];
218
219 V& va = vertices[face[0]];
220 V& vb = vertices[face[1]];
221 V& vc = vertices[face[2]];
222
223 e1 = vb.position - va.position;
224 e2 = vc.position - va.position;
225
226 no = glm::cross(e1, e2);
227
228 va.normal += no;
229 vb.normal += no;
230 vc.normal += no;
231
232 count_normals[face[0]]++;
233 count_normals[face[1]]++;
234 count_normals[face[2]]++;
235 }
236
237 for (size_t i=0; i<vertices.size(); i++)
238 {
239 if (count_normals[i] > 0)
240 vertices[i].normal = glm::normalize(vertices[i].normal / (float)count_normals[i]);
241 }
242 }
243
248 {
253
257 std::vector<glm::uvec3> triangles;
258 };
259
263 constexpr void generateTangents()
264 {
265 SMikkTSpaceInterface callbacks
266 {
267 .m_getNumFaces = [] (const SMikkTSpaceContext * pContext) -> int
268 {
269 // Tell MikkTSpace how many faces (ie triangles) the mesh have
270 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
271 const std::vector<glm::uvec3>& triangles = data->triangles;
272
273 return triangles.size();
274 },
275
276 .m_getNumVerticesOfFace = [] (const SMikkTSpaceContext*, const int) -> int
277 {
278 // We always deal with triangles
279 return 3;
280 },
281
282 .m_getPosition = [] (const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert)
283 {
284 // Tell MikkTSpace how to get a vertex position from face + vertex ids
285 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
286 const ComplexMesh<V>* mesh = data->mesh;
287 const std::vector<glm::uvec3>& triangles = data->triangles;
288
289 const std::vector<V>& vertices = mesh->getVertices();
290
291 const V& vertex = vertices[triangles[iFace][iVert]];
292 const glm::vec3& position = vertex.position;
293
294 fvPosOut[0] = position.x;
295 fvPosOut[1] = position.y;
296 fvPosOut[2] = position.z;
297 },
298
299 .m_getNormal = [] (const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert)
300 {
301 // Tell MikkTSpace how to get a vertex normal from face + vertex ids
302 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
303 const ComplexMesh<V>* mesh = data->mesh;
304 const std::vector<glm::uvec3>& triangles = data->triangles;
305
306 const std::vector<V>& vertices = mesh->getVertices();
307
308 const V& vertex = vertices[triangles[iFace][iVert]];
309 const glm::vec3& normal = vertex.normal;
310
311 fvNormOut[0] = normal.x;
312 fvNormOut[1] = normal.y;
313 fvNormOut[2] = normal.z;
314 },
315
316 .m_getTexCoord = [] (const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert)
317 {
318 // Tell MikkTSpace how to get a vertex texture coordinate from face + vertex ids
319 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
320 const ComplexMesh<V>* mesh = data->mesh;
321 const std::vector<glm::uvec3>& triangles = data->triangles;
322
323 const std::vector<V>& vertices = mesh->getVertices();
324
325 const V& vertex = vertices[triangles[iFace][iVert]];
326 const glm::vec2& tex_coords = vertex.tex_coords;
327
328 fvTexcOut[0] = tex_coords.x;
329 fvTexcOut[1] = tex_coords.y;
330 },
331
332 .m_setTSpaceBasic = [] (const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert)
333 {
334 // Tell MikkTSpace how to set a vertex tangent values (tangent + bitangent) face + vertex ids
335 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
336 ComplexMesh<V>* mesh = data->mesh;
337 const std::vector<glm::uvec3>& triangles = data->triangles;
338
339 std::vector<V>& vertices = mesh->getVertices();
340
341 V& vertex = vertices[triangles[iFace][iVert]];
342
343 vertex.tangent = glm::vec3(fvTangent[0], fvTangent[1], fvTangent[2]);
344 vertex.bitangent = fSign * glm::cross(vertex.normal, vertex.tangent);
345 },
346
347 .m_setTSpace = [] (const SMikkTSpaceContext *, const float[], const float[], const float , const float, const tbool , const int , const int )
348 {
349 // Not used, "basic" tangent space is sufficient for our use-case
350 }
351 };
352
353 // Init user data, the real magic here is getTriangles which is computed once and cached here instead of each time
354 MikkTSpaceUserData user_data = {
355 .mesh = this,
356 .triangles = CommonMesh::getTriangles()
357 };
358
360 .m_pInterface = &callbacks,
361 .m_pUserData = &user_data
362 });
363 }
364
368 constexpr void generateTexcoords()
369 {
370 std::vector<V>& vertices = SimpleMesh<V>::_vertices;
371
372 for (size_t i=0; i<vertices.size(); i++)
373 {
374 V& vertex = vertices[i];
375 vertex.tex_coords = glm::vec2(vertex.position.x, vertex.position.y);
376 }
377 }
378 };
379
380 using Mesh = ComplexMesh<Vertex>;
381 using SkinnedMesh = ComplexMesh<SkinnedVertex>;
382}
383
384#endif
Axis-aligned bounding box.
Definition aabb.h:13
bool isEmpty() const noexcept
Check if the box is empty.
Definition aabb.h:166
void extendToPoint(const glm::vec3 &point) noexcept
Update box size to contain the point.
Definition aabb.h:68
Base Mesh structure, this one doesn't relies on vertices.
Definition mesh.h:29
constexpr const std::vector< glm::uvec3 > getTriangles(uint32_t sub_mesh=0) const
Definition mesh.h:60
Render agnostic mesh data with texcoords / normal / tangents.
Definition mesh.h:185
constexpr void generateTangents()
Generate tangents from texture coordinates.
Definition mesh.h:263
constexpr void generateSmoothNormals(uint32_t sub_mesh=0)
Smooth mesh normals.
Definition mesh.h:196
constexpr void generateTexcoords()
Approximate texture coords based on vertex position.
Definition mesh.h:368
Render agnostic mesh data with only position.
Definition mesh.h:91
constexpr AABB generateBoundingBoxFromPredicate(F &&predicate) const noexcept
Generate a bounding box containing vertices matching a predicate.
Definition mesh.h:143
A function taking vertex parameter that will return true if the predicate is true,...
Definition mesh.h:20
Scene related management (Render-agnostic Geometry, Manger, etc...)
Definition debug.h:19
void computeTangentsFromMikkTSpace(const SMikkTSpaceContext &context)
Generate tangents using MikkTSpace algorithm.
Struct used to convert data to generate tangents from MikkTSpace.
Definition mesh.h:248
std::vector< glm::uvec3 > triangles
list of triangles (vector of indices)
Definition mesh.h:257
ComplexMesh * mesh
pointer to the treated mesh
Definition mesh.h:252