qxLib
geom.inl
Go to the documentation of this file.
1 /**
2 
3  @file geom.inl
4  @author Khrapov
5  @date 4.04.2021
6  @copyright © Nick Khrapov, 2021. All right reserved.
7 
8 **/
9 
10 namespace qx
11 {
12 
13 namespace details
14 {
15 
16 inline vertex create_vertex(const glm::vec3& pos, bool bUnitLengthPosNormal = false)
17 {
18  vertex ret;
19  ret.pos = pos;
20 
21  auto normalPos = pos;
22  if (bUnitLengthPosNormal)
23  {
24  normalPos.x = normalPos.x > 0.f ? 1.f : -1.f;
25  normalPos.y = normalPos.y > 0.f ? 1.f : -1.f;
26  normalPos.z = normalPos.z > 0.f ? 1.f : -1.f;
27  }
28  ret.normal = glm::normalize(normalPos);
29  ret.tangent = create_tangent(ret.normal);
30  return ret;
31 }
32 
33 inline geometry create_figure(
34  std::span<const glm::vec3> positions,
35  std::span<const index_type> _indices,
36  const glm::vec3& offset,
37  bool bFlatNormals,
38  bool bUnitLengthPosNormal = false)
39 {
40  geometry ret;
41  ret.offset = offset;
42 
43  if (bFlatNormals)
44  {
45  for (size_t i = 0; i < _indices.size(); i += 3)
46  {
47  const glm::vec3 pos0 = positions[_indices[i + 0]];
48  const glm::vec3 pos1 = positions[_indices[i + 1]];
49  const glm::vec3 pos2 = positions[_indices[i + 2]];
50 
51  const glm::vec3 normal = glm::cross(pos2 - pos1, pos0 - pos1);
52  const glm::vec3 tangent = create_tangent(normal);
53 
54  ret.geomVertices.emplace_back(pos0, normal, tangent);
55  ret.geomVertices.emplace_back(pos1, normal, tangent);
56  ret.geomVertices.emplace_back(pos2, normal, tangent);
57 
58  ret.geomIndices.push_back(static_cast<index_type>(i + 0));
59  ret.geomIndices.push_back(static_cast<index_type>(i + 1));
60  ret.geomIndices.push_back(static_cast<index_type>(i + 2));
61  }
62  }
63  else
64  {
65  for (const glm::vec3& pos : positions)
66  ret.geomVertices.emplace_back(create_vertex(pos, bUnitLengthPosNormal));
67 
68  ret.geomIndices = indices(_indices.begin(), _indices.end());
69  }
70 
71  return ret;
72 }
73 
74 inline std::array<glm::vec3, 12> create_icosahedron_positions(float fRadius)
75 {
76  const float X = 0.525731112119133606f * fRadius;
77  const float Z = 0.850650808352039932f * fRadius;
78 
79  return { {
80  { -X, 0.f, Z },
81  { X, 0.f, Z },
82  { -X, 0.f, -Z },
83  { X, 0.f, -Z },
84  { 0.f, Z, X },
85  { 0.f, Z, -X },
86  { 0.f, -Z, X },
87  { 0.f, -Z, -X },
88  { Z, X, 0.f },
89  { -Z, X, 0.f },
90  { Z, -X, 0.f },
91  { -Z, -X, 0.f },
92  } };
93 }
94 
95 inline geometry create_parallelogram(
96  float fSide1,
97  float fSide2,
98  float fSide3,
99  bool bFlatNormals,
100  bool bUnitLengthPosNormal)
101 {
102  const float fHalfSide1 = fSide1 / 2.f;
103  const float fHalfSide2 = fSide2 / 2.f;
104  const float fHalfSide3 = fSide3 / 2.f;
105 
106  const std::array<glm::vec3, 8> parallelogramPositions = { { { -fHalfSide1, -fHalfSide2, -fHalfSide3 },
107  { fHalfSide1, -fHalfSide2, -fHalfSide3 },
108  { fHalfSide1, fHalfSide2, -fHalfSide3 },
109  { -fHalfSide1, fHalfSide2, -fHalfSide3 },
110  { -fHalfSide1, -fHalfSide2, fHalfSide3 },
111  { fHalfSide1, -fHalfSide2, fHalfSide3 },
112  { fHalfSide1, fHalfSide2, fHalfSide3 },
113  { -fHalfSide1, fHalfSide2, fHalfSide3 } } };
114 
115  return details::create_figure(
116  parallelogramPositions,
117  shape_indices::parallelogram,
118  { fHalfSide1, fHalfSide2, fHalfSide3 },
119  bFlatNormals,
120  bUnitLengthPosNormal);
121 }
122 
123 } // namespace details
124 
125 inline glm::vec3 create_tangent(const glm::vec3& normal)
126 {
127  // tangent vector is pointing up (positive y)
128  constexpr auto negativeZVector = glm::vec3(0.f, 0.f, -1.f);
129  return glm::normalize(glm::cross(negativeZVector, normal));
130 }
131 
132 inline indices transform_triangle_indices_to_lines(indices_view triangles)
133 {
134  indices lines;
135 
136  for (size_t i = 0; i < triangles.size(); i += 3)
137  {
138  lines.push_back(triangles[i + 0]);
139  lines.push_back(triangles[i + 1]);
140 
141  lines.push_back(triangles[i + 1]);
142  lines.push_back(triangles[i + 2]);
143 
144  lines.push_back(triangles[i + 2]);
145  lines.push_back(triangles[i + 0]);
146  }
147 
148  return lines;
149 }
150 
151 inline geometry create_parallelogram(float fSide1, float fSide2, float fSide3, bool bFlatNormals)
152 {
153  return details::create_parallelogram(fSide1, fSide2, fSide3, bFlatNormals, true);
154 }
155 
156 inline geometry create_parallelogram_lines(float fSide1, float fSide2, float fSide3)
157 {
158  auto geom = create_parallelogram(fSide1, fSide2, fSide3, false);
159  geom.eDrawMode = draw_mode::lines_list;
160  geom.geomIndices = transform_triangle_indices_to_lines(geom.geomIndices);
161 
162  return geom;
163 }
164 
165 inline geometry create_cube(float fSide, bool bFlatNormals)
166 {
167  return details::create_parallelogram(fSide, fSide, fSide, bFlatNormals, false);
168 }
169 
170 inline geometry create_cube_lines(float fSide)
171 {
172  auto geom = create_cube(fSide, false);
173  geom.eDrawMode = draw_mode::lines_list;
174  geom.geomIndices = transform_triangle_indices_to_lines(geom.geomIndices);
175 
176  return geom;
177 }
178 
179 inline geometry create_icosahedron(float fRadius, bool bFlatNormals)
180 {
181  return details::create_figure(
182  details::create_icosahedron_positions(fRadius),
183  shape_indices::icosahedron,
184  { fRadius, fRadius, fRadius },
185  bFlatNormals);
186 }
187 
188 inline geometry create_icosahedron_lines(float fRadius)
189 {
190  auto geom = create_icosahedron(fRadius, false);
191  geom.eDrawMode = draw_mode::lines_list;
192  geom.geomIndices = transform_triangle_indices_to_lines(geom.geomIndices);
193 
194  return geom;
195 }
196 
197 inline geometry create_icosphere(float fRadius, size_t nDivides, bool bFlatNormals)
198 {
199  const auto icosahedronPositions = details::create_icosahedron_positions(fRadius);
200 
201  std::vector<glm::vec3> icospherePositions = std::vector(icosahedronPositions.cbegin(), icosahedronPositions.cend());
202 
203  indices thisLevelIndices;
204  indices prevLevelIndices = indices(shape_indices::icosahedron.cbegin(), shape_indices::icosahedron.cend());
205 
206  std::unordered_map<size_t, index_type> edgeMap;
207 
208  for (size_t i = 0; i < nDivides; ++i)
209  {
210  edgeMap.clear();
211  const size_t end = prevLevelIndices.size();
212 
213  for (size_t j = 0; j < end; j += 3)
214  {
215  std::array<index_type, 3> indicesOuter;
216  std::array<index_type, 3> indicesEdge;
217 
218  for (size_t k = 0; k < 3; ++k)
219  {
220  const index_type k1 = (k + 1) % 3;
221  index_type e0 = prevLevelIndices[j + k];
222  index_type e1 = prevLevelIndices[j + k1];
223  indicesOuter[k] = e0;
224 
225  if (e1 > e0)
226  std::swap(e0, e1);
227 
228  const size_t nEdgeKey = e0 | e1 << 16; //-V101
229 
230  if (auto it = edgeMap.find(nEdgeKey); it != edgeMap.end())
231  {
232  indicesEdge[k] = it->second;
233  }
234  else
235  {
236  indicesEdge[k] = static_cast<index_type>(icospherePositions.size());
237  edgeMap[nEdgeKey] = indicesEdge[k];
238 
239  const auto& e0pos = icospherePositions[e0]; //-V108
240  const auto& e1pos = icospherePositions[e1]; //-V108
241  glm::vec3 newVertex { e0pos.x + e1pos.x, e0pos.y + e1pos.y, e0pos.z + e1pos.z };
242 
243  const float fScale = fRadius / glm::length(newVertex);
244  newVertex *= fScale;
245 
246  icospherePositions.push_back(newVertex);
247  }
248  }
249 
250  thisLevelIndices.push_back(indicesOuter[0]);
251  thisLevelIndices.push_back(indicesEdge[0]);
252  thisLevelIndices.push_back(indicesEdge[2]);
253  thisLevelIndices.push_back(indicesOuter[1]);
254  thisLevelIndices.push_back(indicesEdge[1]);
255  thisLevelIndices.push_back(indicesEdge[0]);
256  thisLevelIndices.push_back(indicesOuter[2]);
257  thisLevelIndices.push_back(indicesEdge[2]);
258  thisLevelIndices.push_back(indicesEdge[1]);
259  thisLevelIndices.push_back(indicesEdge[0]);
260  thisLevelIndices.push_back(indicesEdge[1]);
261  thisLevelIndices.push_back(indicesEdge[2]);
262  }
263 
264  prevLevelIndices = std::move(thisLevelIndices);
265  }
266 
267  return details::create_figure(icospherePositions, prevLevelIndices, { fRadius, fRadius, fRadius }, bFlatNormals);
268 }
269 
270 inline geometry create_icosphere_lines(float fRadius, size_t nDivides)
271 {
272  auto geom = create_icosphere(fRadius, nDivides, false);
273  geom.eDrawMode = draw_mode::lines_list;
274  geom.geomIndices = transform_triangle_indices_to_lines(geom.geomIndices);
275 
276  return geom;
277 }
278 
279 inline geometry create_rect(float fWidth, float fHeight)
280 {
281  const float fHalfWidth = fWidth / 2.f;
282  const float fHalfHeight = fHeight / 2.f;
283 
284  return { { { details::create_vertex({ fHalfWidth, fHalfHeight, 0.f }),
285  details::create_vertex({ fHalfWidth, -fHalfHeight, 0.f }),
286  details::create_vertex({ -fHalfWidth, -fHalfHeight, 0.f }),
287  details::create_vertex({ -fHalfWidth, fHalfHeight, 0.f }) } },
288  std::vector(shape_indices::rect.cbegin(), shape_indices::rect.cend()),
289  { fHalfWidth, fHalfHeight, 0.f },
290  draw_mode::triangles_list };
291 }
292 
293 inline geometry create_rect_lines(float fWidth, float fHeight)
294 {
295  const float fLinesWidth = fWidth - 1.f;
296  const float fLinesHeight = fHeight - 1.f;
297 
298  const float fHalfWidth = fLinesWidth / 2.f;
299  const float fHalfHeight = fLinesHeight / 2.f;
300 
301  // one additional line for missing corner pixel fix
302  return { { { details::create_vertex({ fHalfWidth, fHalfHeight, 0.f }),
303  details::create_vertex({ fHalfWidth, -fHalfHeight, 0.f }),
304  details::create_vertex({ -fHalfWidth, -fHalfHeight, 0.f }),
305  details::create_vertex({ -fHalfWidth, -fHalfHeight - 1.f, 0.f }),
306  details::create_vertex({ -fHalfWidth, fHalfHeight, 0.f }) } },
307  { 0, 1, 2, 3, 4, 0 },
308  { fHalfWidth + 1.f, fHalfHeight + 1.f, 0.f },
309  draw_mode::lines_strip };
310 }
311 
312 inline geometry create_square(float fSideLength)
313 {
314  return create_rect(fSideLength, fSideLength);
315 }
316 
317 inline geometry create_square_lines(float fSideLength)
318 {
319  return create_rect_lines(fSideLength, fSideLength);
320 }
321 
322 inline geometry create_ellipse(float fHorRadius, float fVertRadius, size_t nSides)
323 {
324  const size_t nVertices = nSides + 2;
325  const float fSides = static_cast<float>(nSides);
326  constexpr float f2Pi = 2.0f * std::numbers::pi_v<float>;
327 
328  geometry ret;
329  ret.geomIndices.reserve(nVertices);
330  ret.geomVertices.reserve(nVertices);
331 
332  ret.geomIndices.push_back(0);
333  ret.geomVertices.emplace_back(details::create_vertex({ 0.f, 0.f, 0.f }));
334 
335  for (size_t i = 1; i < nVertices; ++i)
336  {
337  ret.geomIndices.push_back(static_cast<index_type>(i));
338  ret.geomVertices.emplace_back(
339  details::create_vertex({ fHorRadius * std::cos(static_cast<float>(i) * f2Pi / fSides),
340  fVertRadius * std::sin(static_cast<float>(i) * f2Pi / fSides),
341  0.f }));
342  }
343 
344  ret.eDrawMode = draw_mode::triangles_fan;
345  ret.offset = { fHorRadius, fVertRadius, 0.f };
346 
347  return ret;
348 }
349 
350 inline geometry create_ellipse_lines(float fHorRadius, float fVertRadius, size_t nSides)
351 {
352  geometry ret = create_ellipse(fHorRadius, fVertRadius, nSides);
353  ret.geomIndices[0] = ret.geomIndices[ret.geomIndices.size() - 1];
354  ret.eDrawMode = draw_mode::lines_strip;
355  return ret;
356 }
357 
358 inline geometry create_circle(float fRadius, size_t nSides)
359 {
360  return create_ellipse(fRadius, fRadius, nSides);
361 }
362 
363 inline geometry create_circle_lines(float fRadius, size_t nSides)
364 {
365  return create_ellipse_lines(fRadius, fRadius, nSides);
366 }
367 
368 } // namespace qx
geometry create_cube(float fSide, bool bFlatNormals=true)
Create cube geometry.
Definition: geom.inl:165
geometry create_circle_lines(float fRadius, size_t nSides)
Create circle geometry for lines drawing.
Definition: geom.inl:363
geometry create_ellipse(float fHorRadius, float fVertRadius, size_t nSides)
Create ellipse geometry.
Definition: geom.inl:322
geometry create_icosahedron(float fRadius, bool bFlatNormals=true)
Create icosahedron geometry.
Definition: geom.inl:179
geometry create_square_lines(float fSideLength)
Create square geometry for lines drawing.
Definition: geom.inl:317
geometry create_parallelogram_lines(float fSide1, float fSide2, float fSide3)
Create parallelogram geometry for lines drawing.
Definition: geom.inl:156
geometry create_icosphere_lines(float fRadius, size_t nDivides)
Create icosphere geometry for lines drawing.
Definition: geom.inl:270
geometry create_ellipse_lines(float fHorRadius, float fVertRadius, size_t nSides)
Create ellipse geometry for lines drawing.
Definition: geom.inl:350
glm::vec3 create_tangent(const glm::vec3 &normal)
Create tangent vector for a given normal.
Definition: geom.inl:125
geometry create_parallelogram(float fSide1, float fSide2, float fSide3, bool bFlatNormals=true)
Create parallelogram geometry.
Definition: geom.inl:151
geometry create_square(float fSideLength)
Create square geometry.
Definition: geom.inl:312
geometry create_cube_lines(float fSide)
Create cube geometry for lines drawing.
Definition: geom.inl:170
indices transform_triangle_indices_to_lines(indices_view triangles)
Transform triangle indices to line indices.
Definition: geom.inl:132
geometry create_icosahedron_lines(float fRadius)
Create icosahedron geometry for lines drawing.
Definition: geom.inl:188
geometry create_circle(float fRadius, size_t nSides)
Create circle geometry for lines drawing.
Definition: geom.inl:358
geometry create_icosphere(float fRadius, size_t nDivides, bool bFlatNormals=false)
Create icosphere geometry.
Definition: geom.inl:197
geometry create_rect(float fWidth, float fHeight)
Create rectangle geometry.
Definition: geom.inl:279
geometry create_rect_lines(float fWidth, float fHeight)
Create rectangle geometry for lines drawing.
Definition: geom.inl:293
@ geom
geometry
indices geomIndices
indices array
Definition: geom.h:42
vertices geomVertices
vertices array
Definition: geom.h:41
glm::vec3 offset
offset of the center
Definition: geom.h:43
draw_mode eDrawMode
draw mode
Definition: geom.h:44