Oxygen Engine
Modern C++ 3D Engine using OpenGL
Loading...
Searching...
No Matches
bezier.h
1#ifndef OE_SCENE_LOADER_BEZIER_H
2#define OE_SCENE_LOADER_BEZIER_H
3
4#include "../mesh.h"
5#include "../../util/bezier_curve.h"
6#include <span>
7#include <stdexcept>
8
10{
11 enum class BezierPatchType
12 {
13 Biquadratic = 3, //< Each patch contains 9 (3x3) control points
14 Bicubic = 4, //< Each patch contains 16 (4x4) control points
15 };
16
17 template <typename VertexType = Vertex>
18 class Bezier : public Mesh<VertexType>
19 {
20 public:
22
23 Bezier(const BezierPatchType type):
24 _patch_type(type),
25 _patch_size(type == BezierPatchType::Biquadratic ? 9 : 16)
26 {}
27
28 void addControlPoint(const Vertex& point);
29
30 void setControlPoints(const std::vector<Vertex>& points)
31 {
32 if (points.size() < _patch_size)
33 {
34 throw std::runtime_error("Missing control points!");
35 }
36
37 _control_points = points;
38 }
39
40 const std::vector<Vertex>& getControlPoints() const
41 {
42 return _control_points;
43 }
44
46 {
47 const uint16_t lod;
48
49 const bool merge_patches = true;
50 const bool generate_texcoords = true;
51 const bool generate_normals = true;
52 };
53
64 void buildMesh(const BuildMeshInfo& params, BezierMeshType& result)
65 {
66 int32_t divisions = params.lod > 1
67 ? params.lod
68 : 1
69 ;
70
71 auto& vertices = result.vertices;
72 auto& indices = result.indices;
73 auto& primitives = result.primitives;
74
75 const int32_t total_patches = _control_points.size() / _patch_size;
76
77 // Generate all points vertices
78 for (int32_t id_patch = 0; id_patch < total_patches; ++id_patch)
79 {
80 const auto first = _control_points.cbegin() + _patch_size * id_patch;
81 const auto last = _control_points.cbegin() + _patch_size * (id_patch+1);
82 const std::vector<Vertex> patches_points(first, last);
83
84 std::vector<glm::vec3> patches_positions;
85
86 if (params.generate_normals)
87 {
88 patches_positions.reserve(_patch_size);
89
90 for (auto& it : patches_points)
91 {
92 patches_positions.push_back(it.position);
93 }
94 }
95
96 for (int32_t i = 0; i <= divisions; ++i)
97 {
98 float u = i / (float)divisions;
99
100 for (int32_t j = 0; j <= divisions; ++j)
101 {
102 float v = j / (float)divisions;
103
104 Vertex vertex = calculatePatch(patches_points, u, v);
105
107 {
108 if (params.generate_normals)
109 {
110 glm::vec3 tangent = calculateDerivateU<glm::vec3>(patches_positions, u, v);
111 glm::vec3 bitangent = calculateDerivateV<glm::vec3>(patches_positions, u, v);
112
113 if (length(tangent) > 0 && length(bitangent) > 0) [[likely]]
114 {
115 vertex.normal = glm::normalize(glm::cross(tangent, bitangent));
116 vertex.tangent = tangent;
117 vertex.bitangent = bitangent;
118 }
119 else
120 {
121 // Temp fix
122 // Sometimes, the same control point is used multiple times in the same patch
123 // So it is not possible to compute tangent space. As a failsafe measure we use normalized position instead
124 // (gives wrong normal but its better than nothing)
125 //
126 // TODO better
127 vertex.normal = glm::normalize(vertex.position);
128
129 if (length(tangent) == 0 && length(bitangent) != 0)
130 {
131 vertex.tangent = glm::normalize(glm::cross(vertex.normal, bitangent));
132 vertex.bitangent = bitangent;
133 }
134 else if (length(bitangent) == 0 && length(tangent) != 0)
135 {
136 vertex.tangent = tangent;
137 vertex.bitangent = glm::normalize(glm::cross(vertex.normal, tangent));
138 }
139 else
140 {
141 assert(false && "Tangent space failed !");
142 }
143 }
144 }
145 }
146
148 {
149 if (params.generate_texcoords)
150 {
151 vertex.tex_coords_0 = glm::vec2(u, v);
152 }
153 }
154
155 vertices.push_back(vertex);
156 }
157 }
158 }
159
160 /*
161 Fill triangles indices:
162
163 Example surface:
164 6 3 0
165 7 4 1
166 8 5 2
167
168 If we isolate face points like this...
169 a b
170 x y
171
172 ...We should make 2 triangles like this
173 {x, b, a}
174 {y, b, x}
175 */
176
177 // Helper values
178 const size_t vertices_per_edge = (divisions + 1);
179 const size_t vertices_per_edge_pow_2 = vertices_per_edge * vertices_per_edge;
180
181 const size_t indices_per_patch = divisions * divisions * 6;
182
183 indices.reserve(total_patches * indices_per_patch);
184
185 for (int32_t k = 0; k < total_patches; ++k)
186 {
187 for (int32_t j = 0; j < divisions; ++j)
188 {
189 for (int32_t i = 0; i < divisions; ++i)
190 {
191 size_t a = k * vertices_per_edge_pow_2 + j * vertices_per_edge + i;
192 size_t b = a + vertices_per_edge;
193 size_t x = a + 1;
194 size_t y = b + 1;
195
196 indices.push_back(x);
197 indices.push_back(b);
198 indices.push_back(a);
199
200 indices.push_back(y);
201 indices.push_back(b);
202 indices.push_back(x);
203 }
204 }
205 }
206
207 if (params.merge_patches)
208 {
209 primitives = {{0, indices.size()}};
210 }
211 else
212 {
213 primitives.reserve(total_patches);
214
215 for (int32_t id_patch = 0; id_patch < total_patches; ++id_patch)
216 {
217 primitives.push_back({id_patch * indices_per_patch, indices_per_patch});
218 }
219 }
220 }
221
225 template <util::CurveValueConcept T>
226 T calculateDerivateU(std::span<const T> control_points, const float u, const float v)
227 {
228 using util::Bezier;
229
230 if (_patch_type == BezierPatchType::Biquadratic)
231 {
232 assert(control_points.size() == 9);
233
234 std::vector<T> v_curve;
235 v_curve.reserve(3);
236
237 for (int32_t i = 0; i < 3; ++i)
238 {
239 auto points = {
240 control_points[i],
241 control_points[3 + i],
242 control_points[6 + i],
243 };
244
245 v_curve.push_back(Bezier::evaluateCurve<util::BezierCurveType::Quadratic, T>(points, v));
246 }
247
248 return Bezier::evaluateDerivativeCurve<util::BezierCurveType::Quadratic, T>(v_curve, u);
249 }
250 else //if (_patch_type == BezierPatchType::Bicubic)
251 {
252 assert(control_points.size() == 16);
253
254 std::vector<T> v_curve;
255 v_curve.reserve(4);
256
257 for (int32_t i = 0; i < 4; ++i)
258 {
259 auto points = {
260 control_points[i],
261 control_points[4 + i],
262 control_points[8 + i],
263 control_points[12 + i]
264 };
265
266 v_curve.push_back(Bezier::evaluateCurve<util::BezierCurveType::Cubic, T>(points, v));
267 }
268
269 return Bezier::evaluateDerivativeCurve<util::BezierCurveType::Cubic, T>(v_curve, u);
270 }
271 }
272
276 template <util::CurveValueConcept T>
277 T calculateDerivateV(std::span<const T> control_points, const float u, const float v)
278 {
279 using util::Bezier;
280
281 if (_patch_type == BezierPatchType::Biquadratic)
282 {
283 assert(control_points.size() == 9);
284
285 std::vector<T> u_curve;
286 u_curve.reserve(3);
287
288 for (int32_t i = 0; i < 3; ++i)
289 {
290 u_curve.push_back(Bezier::evaluateCurve<util::BezierCurveType::Quadratic, T>({control_points.data() + 3 * i, 3u}, u));
291 }
292
293 return Bezier::evaluateDerivativeCurve<util::BezierCurveType::Quadratic, T>(u_curve, v);
294 }
295 else //if (_patch_type == BezierPatchType::Bicubic)
296 {
297 assert(control_points.size() == 16);
298
299 std::vector<T> u_curve;
300 u_curve.reserve(4);
301
302 for (int32_t i = 0; i < 4; ++i)
303 {
304 u_curve.push_back(Bezier::evaluateCurve<util::BezierCurveType::Cubic, T>({control_points.data() + 4 * i, 4u}, u));
305 }
306
307 return Bezier::evaluateDerivativeCurve<util::BezierCurveType::Cubic, T>(u_curve, v);
308 }
309 }
310
314 template <typename T>
315 T calculatePatch(const std::vector<T>& control_points, const float u, const float v)
316 {
317 using util::Bezier;
318
319 int32_t patch_edge_size = (int32_t)_patch_type;
320
321 std::vector<T> curve;
322 curve.reserve(patch_edge_size);
323
324 for (int32_t i = 0; i < patch_edge_size; ++i)
325 {
326 const auto first = control_points.cbegin() + patch_edge_size*i;
327 const auto last = control_points.cbegin() + patch_edge_size*(i+1);
328 const std::vector<T> patches_points(first, last);
329
330 if (_patch_type == BezierPatchType::Biquadratic)
331 {
332 curve.push_back(Bezier::evaluateCurve<util::BezierCurveType::Quadratic, T>(patches_points, u));
333 }
334 else
335 {
336 curve.push_back(Bezier::evaluateCurve<util::BezierCurveType::Cubic, T>(patches_points, u));
337 }
338 }
339
340 if (_patch_type == BezierPatchType::Biquadratic)
341 {
342 return Bezier::evaluateCurve<util::BezierCurveType::Quadratic, T>(curve, v);
343 }
344 else
345 {
346 return Bezier::evaluateCurve<util::BezierCurveType::Cubic, T>(curve, v);
347 }
348 }
349
350 private:
351 BezierPatchType _patch_type;
352 uint8_t _patch_size;
353
354 std::vector<Vertex> _control_points;
355 };
356}
357
358#endif
Definition bezier.h:19
void buildMesh(const BuildMeshInfo &params, BezierMeshType &result)
Build a mesh Bezier surface (using De Casteljau's algorithm)
Definition bezier.h:64
T calculateDerivateV(std::span< const T > control_points, const float u, const float v)
Calculate derivative point of the bezier patch along the u coordinate.
Definition bezier.h:277
T calculateDerivateU(std::span< const T > control_points, const float u, const float v)
Calculate derivative point of the bezier patch along the u coordinate.
Definition bezier.h:226
T calculatePatch(const std::vector< T > &control_points, const float u, const float v)
Calculate point of the bezier patch along the u and v coordinates.
Definition bezier.h:315
Definition vertex.h:39
Definition vertex.h:50
Generators of geometry / scene by computation (eg. Bezier) or from files (eg. Gltf)
Definition bezier.h:10
Definition mesh.h:61
Definition vertex.h:70
glm::vec2 tex_coords_0
Definition vertex.h:75
glm::vec3 tangent
Definition vertex.h:87
glm::vec3 bitangent
Definition vertex.h:93
glm::vec3 normal
Definition vertex.h:84
glm::vec3 position
Definition vertex.h:72
const bool merge_patches
Set to false to keep each patch as a mesh primitive.
Definition bezier.h:49
const uint16_t lod
Level of details (higher gives more vertices)
Definition bezier.h:47
const bool generate_texcoords
Generate texcoods based on patches (silently ignored if VertexType doersn't satisfy IsVertexWithTexco...
Definition bezier.h:50
const bool generate_normals
Generate vertex normals (silently ignored if VertexType doersn't satisfy IsVertexWithTangents)
Definition bezier.h:51
Definition bezier_curve.h:25