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{
20 {
21 public:
22 inline uint32_t getSubMeshCount() const { return _indices.size(); }
23 inline const std::vector<index_t>& getIndices(uint32_t sub_mesh = 0) const { return _indices[sub_mesh]; }
24
25 inline const AABB& getBoundingBox() const { return _aabb; }
26
27 void flipFaces();
28
32 const std::vector<glm::uvec3> getTriangles(uint32_t sub_mesh = 0) const;
33
34 protected:
35 std::vector<std::vector<index_t>> _indices;
36
37 AABB _aabb;
38 };
39
43 template <SimpleVertex V>
44 class SimpleMesh : public CommonMesh
45 {
46 public:
47 using vertex_type = V;
48
49 SimpleMesh(const std::vector<V>& vertices, const std::vector<index_t>& indices = {}): _vertices(vertices)
50 {
51 if (indices.size() > 0)
52 addSubMesh(indices);
53 }
54
55 SimpleMesh(const std::vector<V>& vertices, const std::vector<std::vector<index_t>>& mesh_indices):
56 _vertices(vertices)
57 {
58 for (auto& submesh : mesh_indices)
59 {
60 addSubMesh(submesh);
61 }
62 }
63
64 uint32_t addSubMesh(const std::vector<oe::scene::index_t>& indices)
65 {
66 _indices.push_back(indices);
67
68 // BBox is empty, init with first vertex
69 if (_aabb.isEmpty())
70 _aabb = {_vertices[indices[0]].position, _vertices[indices[0]].position};
71
72 for (auto& id_vertex : indices)
73 {
74 _aabb.extendToPoint(_vertices[id_vertex].position);
75 }
76
77 return _indices.size() -1;
78 }
79
80 inline std::vector<V>& getVertices()
81 {
82 return _vertices;
83 }
84
85 inline const std::vector<V>& getVertices() const
86 {
87 return _vertices;
88 }
89
93 inline AABB generateBoundingBoxFromPredicate(const std::function<bool(const V&)>& predicate)
94 {
95 const auto& vertices = _vertices;
96
97 AABB result;
98 bool is_first_vertex = true;
99
100 for (auto& it : vertices)
101 {
102 if (predicate(it))
103 {
104 if (is_first_vertex)
105 {
106 result = {it.position, it.position};
107 is_first_vertex = false;
108 }
109 else
110 {
111 result.extendToPoint(it.position);
112 }
113 }
114 }
115
116 return result;
117 }
118
119 protected:
120 SimpleMesh() {}
121
122 std::vector<V> _vertices;
123 };
124
128 void computeTangentsFromMikkTSpace(const SMikkTSpaceContext& context);
129
133 template <ComplexVertex V>
134 class ComplexMesh : public SimpleMesh<V>
135 {
136 public:
137 // Seamlessly use parent constructors
138 using SimpleMesh<V>::SimpleMesh;
139
146 void generateSmoothNormals(uint32_t sub_mesh = 0)
147 {
148 std::vector<glm::uvec3> triangles = CommonMesh::getTriangles(sub_mesh);
149 std::vector<V>& vertices = SimpleMesh<V>::_vertices;
150
151 if (triangles.size() == 0)
152 return;
153
154 glm::vec3 e1, e2, no;
155
156 std::vector<size_t> count_normals;
157
158 for (size_t i=0; i<vertices.size(); i++)
159 {
160 vertices[i].normal = glm::vec3(0.0);
161 count_normals.push_back(0);
162 }
163
164 for (size_t i=0; i<triangles.size(); i++)
165 {
166 const glm::ivec3& face = triangles[i];
167
168 V& va = vertices[face[0]];
169 V& vb = vertices[face[1]];
170 V& vc = vertices[face[2]];
171
172 e1 = vb.position - va.position;
173 e2 = vc.position - va.position;
174
175 no = glm::cross(e1, e2);
176
177 va.normal += no;
178 vb.normal += no;
179 vc.normal += no;
180
181 count_normals[face[0]]++;
182 count_normals[face[1]]++;
183 count_normals[face[2]]++;
184 }
185
186 for (size_t i=0; i<vertices.size(); i++)
187 {
188 if (count_normals[i] > 0)
189 vertices[i].normal = glm::normalize(vertices[i].normal / (float)count_normals[i]);
190 }
191 }
192
193 // TODO generate normals
194 // https://gamedev.stackexchange.com/questions/8408/best-way-to-compute-vertex-normals-from-a-triangles-list
195
199 void optimizeVertices(uint32_t sub_mesh = 0);
200
205 {
210
214 std::vector<glm::uvec3> triangles;
215 };
216
221 {
222 SMikkTSpaceInterface callbacks
223 {
224 .m_getNumFaces = [] (const SMikkTSpaceContext * pContext) -> int
225 {
226 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
227 const std::vector<glm::uvec3>& triangles = data->triangles;
228
229 return triangles.size();
230 },
231
232 .m_getNumVerticesOfFace = [] (const SMikkTSpaceContext*, const int) -> int
233 {
234 // We always deal with triangles
235 return 3;
236 },
237
238 .m_getPosition = [] (const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert)
239 {
240 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
241 const ComplexMesh<V>* mesh = data->mesh;
242 const std::vector<glm::uvec3>& triangles = data->triangles;
243
244 const std::vector<V>& vertices = mesh->getVertices();
245
246 const V& vertex = vertices[triangles[iFace][iVert]];
247 const glm::vec3& position = vertex.position;
248
249 fvPosOut[0] = position.x;
250 fvPosOut[1] = position.y;
251 fvPosOut[2] = position.z;
252 },
253
254 .m_getNormal = [] (const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert)
255 {
256 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
257 const ComplexMesh<V>* mesh = data->mesh;
258 const std::vector<glm::uvec3>& triangles = data->triangles;
259
260 const std::vector<V>& vertices = mesh->getVertices();
261
262 const V& vertex = vertices[triangles[iFace][iVert]];
263 const glm::vec3& normal = vertex.normal;
264
265 fvNormOut[0] = normal.x;
266 fvNormOut[1] = normal.y;
267 fvNormOut[2] = normal.z;
268 },
269
270 .m_getTexCoord = [] (const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert)
271 {
272 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
273 const ComplexMesh<V>* mesh = data->mesh;
274 const std::vector<glm::uvec3>& triangles = data->triangles;
275
276 const std::vector<V>& vertices = mesh->getVertices();
277
278 const V& vertex = vertices[triangles[iFace][iVert]];
279 const glm::vec2& tex_coords = vertex.tex_coords;
280
281 fvTexcOut[0] = tex_coords.x;
282 fvTexcOut[1] = tex_coords.y;
283 },
284
285 .m_setTSpaceBasic = [] (const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert)
286 {
287 const MikkTSpaceUserData* data = static_cast<MikkTSpaceUserData*>(pContext->m_pUserData);
288 ComplexMesh<V>* mesh = data->mesh;
289 const std::vector<glm::uvec3>& triangles = data->triangles;
290
291 std::vector<V>& vertices = mesh->getVertices();
292
293 V& vertex = vertices[triangles[iFace][iVert]];
294
295 vertex.tangent = glm::vec3(fvTangent[0], fvTangent[1], fvTangent[2]);
296 vertex.bitangent = fSign * glm::cross(vertex.normal, vertex.tangent);
297 },
298
299 .m_setTSpace = [] (const SMikkTSpaceContext *, const float[], const float[], const float , const float, const tbool , const int , const int )
300 {
301 // Not used
302 }
303 };
304
305 // Init user data, the real magic here is getTriangles which is is avoided to be computed each time
306 MikkTSpaceUserData user_data = {
307 .mesh = this,
308 .triangles = CommonMesh::getTriangles()
309 };
310
312 .m_pInterface = &callbacks,
313 .m_pUserData = &user_data
314 });
315 }
316
321 {
322 std::vector<V>& vertices = SimpleMesh<V>::_vertices;
323
324 for (size_t i=0; i<vertices.size(); i++)
325 {
326 V& vertex = vertices[i];
327 vertex.tex_coords = glm::vec2(vertex.position.x, vertex.position.y);
328 }
329 }
330 };
331
332 class Mesh : public ComplexMesh<Vertex>
333 {
334 public:
335 using ComplexMesh<Vertex>::ComplexMesh;
336 };
337
338 class SkinnedMesh : public ComplexMesh<SkinnedVertex>
339 {
340 public:
341 using ComplexMesh<SkinnedVertex>::ComplexMesh;
342 };
343}
344
345#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:20
const std::vector< glm::uvec3 > getTriangles(uint32_t sub_mesh=0) const
Render agnostic mesh data with texcoords / normal / tangents.
Definition mesh.h:135
void generateTangents()
Generate tangents from texture coordinates.
Definition mesh.h:220
void generateTexcoords()
Approximate texture coords based on vertex position.
Definition mesh.h:320
void optimizeVertices(uint32_t sub_mesh=0)
Merge multiple duplicated vertices into one.
void generateSmoothNormals(uint32_t sub_mesh=0)
Smooth mesh normals.
Definition mesh.h:146
Definition mesh.h:333
Render agnostic mesh data with only position.
Definition mesh.h:45
AABB generateBoundingBoxFromPredicate(const std::function< bool(const V &)> &predicate)
Generate a bounding box containing vertices matching a predicate.
Definition mesh.h:93
Definition mesh.h:339
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:205
std::vector< glm::uvec3 > triangles
list of triangles (vector of indices)
Definition mesh.h:214
ComplexMesh * mesh
pointer to the treated mesh
Definition mesh.h:209
Definition vertex.h:105
Definition vertex.h:43