Sparkle 0.0.1
Loading...
Searching...
No Matches
spk_mesh.hpp
1#pragma once
2
3#include "structure/design_pattern/spk_cached_data.hpp"
4#include "structure/design_pattern/spk_contract_provider.hpp"
5#include "structure/geometry/spk_polygon.hpp"
6#include "structure/opengl/spk_buffer_set_object.hpp"
7
8#include <cstdint>
9#include <memory>
10#include <utility>
11#include <vector>
12
13namespace spk
14{
27 template <typename TVertex>
28 class IMesh
29 {
30 public:
38 using EditionContract = ContractProvider::Contract;
43
44 private:
45 std::vector<Polygon> _shapes;
46 mutable std::shared_ptr<spk::OpenGL::BufferSetObject> _bufferSet;
47 mutable bool _bufferSetConfigured = false;
48 mutable bool _needsSynchronization = true;
49
50 struct Cache
51 {
52 std::vector<TVertex> vertices;
53 std::vector<uint32_t> indexes;
54 };
55
56 CachedData<Cache> _cache;
57 mutable ContractProvider _onEditionContractProvider;
58
59 std::pair<std::size_t, std::size_t> _computeTotals() const
60 {
61 std::size_t totalVertices = 0;
62 std::size_t totalIndices = 0;
63
64 for (const auto &polygon : _shapes)
65 {
66 const std::size_t n = polygon.points.size();
67 totalVertices += n;
68
69 if (n >= 3)
70 {
71 totalIndices += (n - 2) * 3;
72 }
73 }
74
75 return {totalVertices, totalIndices};
76 }
77
78 void _appendPolygon(const Polygon &p_polygon, Cache &p_cache) const
79 {
80 const std::size_t vertexCount = p_polygon.points.size();
81 if (vertexCount < 3)
82 {
83 return;
84 }
85
86 const uint32_t startIndex = static_cast<uint32_t>(p_cache.vertices.size());
87
88 p_cache.vertices.insert(p_cache.vertices.end(), p_polygon.points.begin(), p_polygon.points.end());
89
90 switch (p_polygon.order)
91 {
92 case PolygonOrder::TriangleFanFromFirst:
93 _emitTriangleFanIndices(startIndex, vertexCount, p_cache.indexes);
94 break;
95
96 case PolygonOrder::TriangleStrip:
97 _emitTriangleStripIndices(startIndex, vertexCount, p_cache.indexes);
98 break;
99 }
100 }
101
102 static void _emitTriangleFanIndices(uint32_t p_startIndex, std::size_t p_vertexCount, std::vector<uint32_t> &p_indices)
103 {
104 for (uint32_t i = 1; i + 1 < p_vertexCount; ++i)
105 {
106 p_indices.push_back(p_startIndex);
107 p_indices.push_back(p_startIndex + i);
108 p_indices.push_back(p_startIndex + i + 1);
109 }
110 }
111
112 static void _emitTriangleStripIndices(uint32_t p_startIndex, std::size_t p_vertexCount, std::vector<uint32_t> &p_indices)
113 {
114 for (uint32_t i = 0; i + 2 < p_vertexCount; ++i)
115 {
116 if ((i % 2) == 0)
117 {
118 p_indices.push_back(p_startIndex + i);
119 p_indices.push_back(p_startIndex + i + 1);
120 p_indices.push_back(p_startIndex + i + 2);
121 }
122 else
123 {
124 p_indices.push_back(p_startIndex + i + 1);
125 p_indices.push_back(p_startIndex + i);
126 p_indices.push_back(p_startIndex + i + 2);
127 }
128 }
129 }
130
131 Cache _updateCache() const
132 {
133 Cache cache;
134
135 const auto [totalVertices, totalIndices] = _computeTotals();
136 cache.vertices.reserve(totalVertices);
137 cache.indexes.reserve(totalIndices);
138
139 for (const auto &polygon : _shapes)
140 {
141 _appendPolygon(polygon, cache);
142 }
143
144 return cache;
145 }
146
147 void _synchronize() const
148 {
149 if (_bufferSet == nullptr)
150 {
151 return;
152 }
153
154 if (_bufferSetConfigured == false)
155 {
156 _configureBufferSet();
157 _bufferSetConfigured = true;
158 }
159
160 _bufferSet->vbo().setVertices(vertices());
161 _bufferSet->ibo().setIndexes(indexes());
162 }
163
164 virtual void _configureBufferSet() const = 0;
165
166 public:
171 _bufferSet(std::make_shared<spk::OpenGL::BufferSetObject>(spk::OpenGL::BufferObject::Usage::Dynamic)),
172 _cache([this]() {
173 return _updateCache();
174 })
175 {
176 }
177
182 IMesh(const IMesh &p_other) :
183 _shapes(p_other._shapes),
184 _bufferSet(std::make_shared<spk::OpenGL::BufferSetObject>(spk::OpenGL::BufferObject::Usage::Dynamic)),
185 _bufferSetConfigured(false),
186 _needsSynchronization(true),
187 _cache([this]() {
188 return _updateCache();
189 }),
190 _onEditionContractProvider()
191 {
192 }
193
199 IMesh &operator=(const IMesh &p_other)
200 {
201 if (this != &p_other)
202 {
203 _shapes = p_other._shapes;
204 _bufferSet = std::make_shared<spk::OpenGL::BufferSetObject>(spk::OpenGL::BufferObject::Usage::Dynamic);
205 _bufferSetConfigured = false;
206 _needsSynchronization = true;
207 _cache.configure([this]() {
208 return _updateCache();
209 });
210 _onEditionContractProvider = ContractProvider{};
211 }
212 return *this;
213 }
214
219 IMesh(IMesh &&p_other) noexcept :
220 _shapes(std::move(p_other._shapes)),
221 _bufferSet(std::move(p_other._bufferSet)),
222 _bufferSetConfigured(p_other._bufferSetConfigured),
223 _needsSynchronization(p_other._needsSynchronization),
224 _cache([this]() {
225 return _updateCache();
226 }),
227 _onEditionContractProvider(std::move(p_other._onEditionContractProvider))
228 {
229 _cache.release();
230 }
231
237 IMesh &operator=(IMesh &&p_other) noexcept
238 {
239 if (this != &p_other)
240 {
241 _shapes = std::move(p_other._shapes);
242 _bufferSet = std::move(p_other._bufferSet);
243 _bufferSetConfigured = p_other._bufferSetConfigured;
244 _needsSynchronization = p_other._needsSynchronization;
245 _onEditionContractProvider = std::move(p_other._onEditionContractProvider);
246 _cache.configure([this]() {
247 return _updateCache();
248 });
249 }
250 return *this;
251 }
252
253 virtual ~IMesh() = default;
254
258 void clear()
259 {
260 _shapes.clear();
261 _cache.release();
262 _needsSynchronization = true;
263 _onEditionContractProvider.trigger();
264 }
265
270 void append(const Polygon &p_shape)
271 {
272 _shapes.push_back(p_shape);
273 _cache.release();
274 _needsSynchronization = true;
275 _onEditionContractProvider.trigger();
276 }
277
284 {
285 return (std::move(_onEditionContractProvider.subscribe(p_job)));
286 }
287
292 const std::vector<TVertex> &vertices() const
293 {
294 return (_cache.get().vertices);
295 }
296
301 const std::vector<Polygon> &polygons() const
302 {
303 return (_shapes);
304 }
305
310 const std::vector<uint32_t> &indexes() const
311 {
312 return (_cache.get().indexes);
313 }
314
320 IMesh &operator+=(const IMesh &p_other)
321 {
322 if (p_other._shapes.empty())
323 {
324 return *this;
325 }
326
327 _shapes.insert(_shapes.end(), p_other._shapes.begin(), p_other._shapes.end());
328 _cache.release();
329 _needsSynchronization = true;
330 _onEditionContractProvider.trigger();
331 return *this;
332 }
333
338 const std::shared_ptr<spk::OpenGL::BufferSetObject> &bufferSet() const
339 {
340 return _bufferSet;
341 }
342
346 void synchronize() const
347 {
348 if (_needsSynchronization == false)
349 {
350 return;
351 }
352
353 _synchronize();
354 _needsSynchronization = false;
355 }
356
362 IMesh fuze(const IMesh &p_other) const
363 {
364 IMesh result(*this);
365 result += p_other;
366 return result;
367 }
368
375 friend IMesh operator+(const IMesh<TVertex> &p_lhs, const IMesh<TVertex> &p_rhs)
376 {
377 p_lhs += p_rhs;
378 return p_lhs;
379 }
380 };
381}
Lazily generates and caches a value with optional custom destructor.
Definition spk_cached_data.hpp:26
void release() const
Releases the cached value by invoking destructor if provided.
Definition spk_cached_data.hpp:190
Stores polygon data and exposes GPU-ready vertex/index buffers.
Definition spk_mesh.hpp:29
const std::shared_ptr< spk::OpenGL::BufferSetObject > & bufferSet() const
Returns the underlying buffer set.
Definition spk_mesh.hpp:338
friend IMesh operator+(const IMesh< TVertex > &p_lhs, const IMesh< TVertex > &p_rhs)
Returns a mesh that combines both operands.
Definition spk_mesh.hpp:375
EditionContract subscribeToEdition(const EditionJob &p_job) const
Subscribes to mesh edition notifications.
Definition spk_mesh.hpp:283
const std::vector< Polygon > & polygons() const
Returns the stored polygons.
Definition spk_mesh.hpp:301
IMesh fuze(const IMesh &p_other) const
Returns a new mesh by concatenating another mesh.
Definition spk_mesh.hpp:362
IMesh()
Builds an empty mesh with a dynamic buffer set.
Definition spk_mesh.hpp:170
IMesh & operator=(const IMesh &p_other)
Assigns polygon data from another mesh.
Definition spk_mesh.hpp:199
void append(const Polygon &p_shape)
Appends a polygon and notifies subscribers.
Definition spk_mesh.hpp:270
ContractProvider::Job EditionJob
Definition spk_mesh.hpp:42
void synchronize() const
Updates GPU buffers when mesh data has changed.
Definition spk_mesh.hpp:346
ContractProvider::Contract EditionContract
Definition spk_mesh.hpp:38
IPolygon< spk::Vector2 > Polygon
Definition spk_mesh.hpp:34
IMesh & operator=(IMesh &&p_other) noexcept
Move-assigns polygon data from another mesh.
Definition spk_mesh.hpp:237
const std::vector< spk::Vector2 > & vertices() const
Definition spk_mesh.hpp:292
IMesh(const IMesh &p_other)
Copies polygon data into a new mesh instance.
Definition spk_mesh.hpp:182
const std::vector< uint32_t > & indexes() const
Definition spk_mesh.hpp:310
void clear()
Clears all polygons and notifies subscribers.
Definition spk_mesh.hpp:258
IMesh & operator+=(const IMesh &p_other)
Appends polygons from another mesh and notifies subscribers.
Definition spk_mesh.hpp:320
IMesh(IMesh &&p_other) noexcept
Moves polygon data from another mesh.
Definition spk_mesh.hpp:219
typename Contract::Job Job
Definition spk_contract_provider.hpp:267
STL namespace.
Stores vertex points and winding order for polygon triangulation.
Definition spk_polygon.hpp:24