Cogs.Core
NormalGenerator.h
1#pragma once
2
3#include <vector>
4#include <stdint.h>
5
6namespace Cogs
7{
8 namespace Geometry
9 {
16 template<class IndexType, int maxSize>
18 {
19 typedef std::vector<IndexType> IndexVector;
20
22 AdjacencyList_T() = default;
23
25 {
26 if (currentSize == -1) {
27 auto indexVector = reinterpret_cast<IndexVector *>(&data);
28 indexVector->~IndexVector();
29 }
30 }
31
33 void push_back(IndexType index)
34 {
35 static_assert(maxSize * sizeof(IndexType) >= sizeof(IndexVector), "No room for index vector.");
36
37 if (currentSize == maxSize) {
38 currentSize = -1;
39
40 IndexType temp[maxSize];
41
42 std::memcpy(temp, data, maxSize * sizeof(IndexType));
43
44 auto indexVector = reinterpret_cast<IndexVector *>(&data);
45 new (indexVector) IndexVector();
46 indexVector->assign(temp, temp + maxSize);
47 }
48
49 if (currentSize == -1) {
50 auto indexVector = reinterpret_cast<IndexVector *>(&data);
51 indexVector->push_back(index);
52 } else {
53 data[currentSize++] = index;
54 }
55 }
56
58 const IndexType & operator[](size_t i) const
59 {
60 if (currentSize == -1) {
61 auto indexVector = reinterpret_cast<const IndexVector *>(&data);
62
63 assert(i < indexVector->size() && "Index out of range.");
64
65 return (*indexVector)[i];
66 } else {
67 return this->data[i];
68 }
69 }
70
72 size_t size() const
73 {
74 if (currentSize == -1) {
75 auto indexVector = reinterpret_cast<const IndexVector *>(&data);
76 return indexVector->size();
77 } else {
78 return currentSize;
79 }
80 }
81
82 private:
83 IndexType data[maxSize];
84
85 int8_t currentSize = 0;
86 };
87
88 typedef AdjacencyList_T<int32_t, 8> AdjacencyList;
89
93 template<class IndexType, class AdjacencyListType, bool quads>
94 void generateAdjacencyInfo(const size_t numFaces, const IndexType * indices, const size_t positionOffset, std::vector<AdjacencyListType> & adjacencyList)
95 {
96 const size_t numIndicesPerFace = quads ? 5 : 4;
97
98 for (size_t i = 0; i < numFaces; i++) {
99 const size_t baseIndex = i * numIndicesPerFace;
100
101 adjacencyList[indices[baseIndex] - static_cast<IndexType>(positionOffset)].push_back(static_cast<IndexType>(i));
102 adjacencyList[indices[baseIndex + 1] - static_cast<IndexType>(positionOffset)].push_back(static_cast<IndexType>(i));
103 adjacencyList[indices[baseIndex + 2] - static_cast<IndexType>(positionOffset)].push_back(static_cast<IndexType>(i));
104
105 if (quads) {
106 adjacencyList[indices[baseIndex + 3] - positionOffset].push_back(static_cast<int32_t>(i));
107 }
108 }
109 }
110
114 template<class PositionType, class IndexType, class NormalType, bool quads>
115 void calculateFaceNormals(const size_t numFaces, const PositionType * positions, const IndexType * indices, std::vector<NormalType> & faceNormals)
116 {
117 const size_t numIndicesPerFace = quads ? 5 : 4;
118
119 NormalType faceNormal;
120 PositionType leftLeg;
121 PositionType rightLeg;
122
123 // Calculate per face normals by doing a cross product of the legs in the triangle.
124 for (size_t i = 0; i < numFaces; i++) {
125 const size_t baseIndex = i * numIndicesPerFace;
126
127 leftLeg = positions[indices[baseIndex + 1]] - positions[indices[baseIndex]];
128 rightLeg = positions[indices[baseIndex + 2]] - positions[indices[baseIndex]];
129
130 faceNormal = cross(rightLeg, leftLeg);
131
132 faceNormal = normalize(faceNormal);
133
134 faceNormals[i] = faceNormal;
135 }
136 }
137
142 template<class NormalType, class AdjacencyListType>
143 void calculateSmoothVertexNormals(NormalType * normals, const size_t numNormals, const std::vector<NormalType> & faceNormals, const size_t positionOffset, const std::vector<AdjacencyListType> & adjacencyList)
144 {
145 for (size_t i = positionOffset; i < numNormals; i++) {
146 NormalType faceNormal = NormalType(0, 0, 0);
147
148 const size_t adjacencyIndex = i - positionOffset;
149 const size_t numAdjacentFaces = adjacencyList[adjacencyIndex].size();
150
151 for (size_t k = 0; k < numAdjacentFaces; k++) {
152 faceNormal = faceNormal + faceNormals[adjacencyList[adjacencyIndex][k]];
153 }
154
155 faceNormal = normalize(faceNormal);
156
157 normals[i] = faceNormal;
158 }
159 }
160
162 static double pi()
163 {
164 return 3.14159265358979323846;
165 }
166
171 template<class NormalType, class AdjacencyListType, class IndexType>
172 void calculateFacetVertexNormals(const std::vector<NormalType> & faceNormals, const std::vector<AdjacencyListType> & adjacencyList, const IndexType * indexes, const float creaseAngle, bool quads, NormalType * normals)
173 {
174 const size_t numIndicesPerFace = quads ? 5 : 4;
175 const size_t numVertexesPerFace = quads ? 4 : 3;
176
177 const float threshold = std::cos(std::max(0.0f, std::min(creaseAngle, static_cast<float>(pi()))));
178
179 for (size_t i = 0; i < faceNormals.size(); ++i) {
180 const NormalType & faceNormal = faceNormals[i];
181
182 for (size_t j = 0; j < numIndicesPerFace - 1; ++j) {
183 const int currentIndex = indexes[i * numIndicesPerFace + j];
184
185 NormalType vertexNormal = faceNormal;
186
187 const AdjacencyListType & faceList = adjacencyList[currentIndex];
188
189 for (size_t k = 0; k < faceList.size(); ++k) {
190 const NormalType & adjacentFaceNormal = faceNormals[faceList[k]];
191
192 if (static_cast<size_t>(faceList[k]) != i && dot(adjacentFaceNormal, faceNormal) > threshold) {
193 vertexNormal = vertexNormal + adjacentFaceNormal;
194 }
195 }
196
197 vertexNormal = normalize(vertexNormal);
198
199 normals[i * numVertexesPerFace + j] = vertexNormal;
200 }
201 }
202 }
203
209 template<class IndexType, bool quads>
210 void generateLinearIndexes(const size_t numFaces, IndexType * indexes)
211 {
212 int index = 0;
213 int vertexIndex = 0;
214
215 for (size_t i = 0; i < numFaces; ++i) {
216 indexes[index++] = vertexIndex++;
217 indexes[index++] = vertexIndex++;
218 indexes[index++] = vertexIndex++;
219
220 if (quads) {
221 indexes[index++] = vertexIndex++;
222 }
223
224 indexes[index++] = -1;
225 }
226 }
227
231 template<class ValueType, class IndexType>
232 void reindexValues(const ValueType * inputValues, const IndexType * indexes, const size_t numFaces, const size_t numIndexesPerFace, ValueType * outputValues)
233 {
234 int index = 0;
235
236 for (size_t i = 0; i < numFaces; ++i) {
237 for (size_t j = 0; j < numIndexesPerFace - 1; ++j) {
238 outputValues[index++] = inputValues[indexes[i * numIndexesPerFace + j]];
239 }
240 }
241 }
242
247 template<class NormalType>
248 void correctZeroLengthNormals(NormalType * normals, const size_t numNormals)
249 {
250 for (size_t i = 0; i < numNormals; ++i) {
251 if (normals[i] == NormalType(0, 0, 0) || normals[i][0] == std::numeric_limits<float>::quiet_NaN()) {
252 normals[i] = NormalType(0, 0, 1);
253 }
254 }
255 }
256
260 template<class NormalType>
261 void reverseNormalDirections(NormalType * normals, const size_t numNormals)
262 {
263 for (size_t i = 0; i < numNormals; ++i) {
264 normals[i] = -normals[i];
265 }
266 }
267
272 template<class AdjacencyListType>
273 void addExtrusionSeamFaces(const size_t numFaces, size_t segmentCount, std::vector<AdjacencyListType> & adjacencyList)
274 {
275 const size_t numSections = numFaces / segmentCount;
276
277 for (size_t i = 0; i < numSections; ++i) {
278 const size_t firstSegmentIndex = i * (segmentCount + 1);
279 const size_t lastSegmentIndex = i * (segmentCount + 1) + segmentCount;
280
281 const size_t firstSegmentIndexNextRow = (i + 1) * (segmentCount + 1);
282 const size_t lastSegmentIndexNextRow = (i + 1) * (segmentCount + 1) + segmentCount;
283
284 adjacencyList[lastSegmentIndex].push_back(static_cast<int32_t>(i * segmentCount));
285 adjacencyList[firstSegmentIndex].push_back(static_cast<int32_t>(i * segmentCount + (segmentCount - 1)));
286 adjacencyList[lastSegmentIndexNextRow].push_back(static_cast<int32_t>(i * segmentCount));
287 adjacencyList[firstSegmentIndexNextRow].push_back(static_cast<int32_t>(i * segmentCount + (segmentCount - 1)));
288 }
289 }
290
291 template<class AdjacencyListType>
292 void addTriangleExtrusionSeamFaces(const size_t numFaces, size_t segmentCount, std::vector<AdjacencyListType> & adjacencyList)
293 {
294 const size_t numSections = (numFaces / 2) / segmentCount;
295 const size_t facePerSectionCount = segmentCount * 2;
296
297 for (size_t i = 0; i < numSections; ++i) {
298 const size_t firstSegmentIndex = i * (segmentCount + 1);
299 const size_t lastSegmentIndex = i * (segmentCount + 1) + segmentCount;
300
301 const size_t firstSegmentIndexNextRow = (i + 1) * (segmentCount + 1);
302 const size_t lastSegmentIndexNextRow = (i + 1) * (segmentCount + 1) + segmentCount;
303
304 adjacencyList[firstSegmentIndex].push_back(static_cast<int32_t>(i * facePerSectionCount + (facePerSectionCount - 2)));
305
306 adjacencyList[lastSegmentIndex].push_back(static_cast<int32_t>(i * facePerSectionCount));
307 adjacencyList[lastSegmentIndex].push_back(static_cast<int32_t>(i * facePerSectionCount + 1));
308
309 adjacencyList[firstSegmentIndexNextRow].push_back(static_cast<int32_t>(i * facePerSectionCount + (facePerSectionCount - 1)));
310 adjacencyList[firstSegmentIndexNextRow].push_back(static_cast<int32_t>(i * facePerSectionCount + (facePerSectionCount - 2)));
311
312 adjacencyList[lastSegmentIndexNextRow].push_back(static_cast<int32_t>(i * facePerSectionCount + 1));
313 }
314 }
315
319 template<typename VectorType, typename IndexType>
320 void generateNormals(VectorType * positions, const size_t numVertexes, IndexType * indexes, const size_t numIndexes, bool quads, bool flipNormals, bool correctNormals, std::vector<VectorType> & normals)
321 {
322 if (!numIndexes) return;
323 if (!numVertexes) return;
324
325 normals.resize(numVertexes);
326
327 generateNormals(positions, normals.data(), numVertexes, indexes, numIndexes, flipNormals, correctNormals, quads);
328 }
329
333 template<typename VectorType, typename IndexType>
335 const VectorType * positions,
336 VectorType * normals,
337 const size_t numPositions,
338 const IndexType * indices,
339 const size_t numIndices,
340 bool flipNormals,
341 bool correctNormals,
342 bool quads,
343 size_t positionOffset)
344 {
345 const size_t numIndicesPerFace = quads ? 5u : 4u;
346 const size_t numFaces = numIndices / numIndicesPerFace;
347 const size_t numActualVertices = numPositions - positionOffset;
348
349 std::vector<VectorType> faceNormals(static_cast<unsigned>(numFaces));
350 std::vector<AdjacencyList> adjacencyList(static_cast<unsigned>(numActualVertices));
351
352 if (quads) {
353 generateAdjacencyInfo<IndexType, AdjacencyList, true>(numFaces, indices, positionOffset, adjacencyList);
354
355 calculateFaceNormals<VectorType, IndexType, VectorType, true>(numFaces, positions, indices, faceNormals);
356 } else {
357 generateAdjacencyInfo<IndexType, AdjacencyList, false>(numFaces, indices, positionOffset, adjacencyList);
358
359 calculateFaceNormals<VectorType, IndexType, VectorType, false>(numFaces, positions, indices, faceNormals);
360 }
361
362 if (correctNormals) {
363 correctZeroLengthNormals(faceNormals.data(), faceNormals.size());
364 }
365
366 if (flipNormals) {
367 reverseNormalDirections(faceNormals.data(), faceNormals.size());
368 }
369
370 calculateSmoothVertexNormals(normals, numPositions, faceNormals, positionOffset, adjacencyList);
371 }
372
382 template<typename VectorType, typename IndexType>
383 void generateExtrusionNormals(VectorType * positions, const size_t numVertexes, IndexType * indexes, const size_t numIndexes, int segmentCount, bool quads, bool flipNormals, std::vector<VectorType> & normals)
384 {
385 if (!numVertexes) return;
386 if (!numIndexes) return;
387
388 const int numIndicesPerFace = quads ? 5 : 4;
389 const int numVertices = numVertexes;
390 const int numFaces = numIndexes / numIndicesPerFace;
391
392 normals.resize(numVertices);
393
394 std::vector<VectorType> faceNormals(numVertices);
395 std::vector<AdjacencyList> adjacencyList(numVertices);
396
397 if (quads) {
398 generateAdjacencyInfo<IndexType, AdjacencyList, true>(numFaces, indexes, 0, adjacencyList);
399 } else {
400 generateAdjacencyInfo<IndexType, AdjacencyList, false>(numFaces, indexes, 0, adjacencyList);
401 }
402
403 if (quads) {
404 addExtrusionSeamFaces(numFaces, segmentCount, adjacencyList);
405 } else {
406 addTriangleExtrusionSeamFaces(numFaces, segmentCount, adjacencyList);
407 }
408
409 if (quads) {
410 calculateFaceNormals<VectorType, IndexType, VectorType, true>(numFaces, positions, indexes, faceNormals);
411 } else {
412 calculateFaceNormals<VectorType, IndexType, VectorType, false>(numFaces, positions, indexes, faceNormals);
413 }
414
415 if (flipNormals) {
416 reverseNormalDirections(faceNormals.data(), faceNormals.size());
417 }
418
419 calculateSmoothVertexNormals(normals, numVertices, faceNormals, 0, adjacencyList);
420 }
421
422 template<typename VectorType, typename IndexType, typename Vector2Type>
423 void generateFacetNormals(
424 const VectorType * positions,
425 const size_t numPositions,
426 const IndexType * indexes,
427 const size_t numIndexes,
428 const Vector2Type * texCoords,
429 bool quads, bool flipNormals, bool correctNormals, const float creaseAngle, bool createExtrusionSeam, const size_t segmentCount,
430 std::vector<VectorType> & outputPositions,
431 std::vector<VectorType> & outputNormals,
432 std::vector<IndexType> & outputIndexes,
433 std::vector<Vector2Type> * outputTexCoords)
434 {
435 if (!numPositions) return;
436 if (!numIndexes) return;
437
438 const size_t positionOffset = 0;
439 const size_t numIndicesPerFace = quads ? 5 : 4;
440 const size_t numVertexesPerFace = quads ? 4 : 3;
441 const size_t numFaces = numIndexes / numIndicesPerFace;
442 const size_t numActualVertices = numPositions - positionOffset;
443
444 std::vector<VectorType> faceNormals(numFaces);
445 std::vector<AdjacencyList> adjacencyList(numActualVertices);
446
447 if (quads) {
448 generateAdjacencyInfo<IndexType, AdjacencyList, true>(numFaces, indexes, positionOffset, adjacencyList);
449 } else {
450 generateAdjacencyInfo<IndexType, AdjacencyList, false>(numFaces, indexes, positionOffset, adjacencyList);
451 }
452
453 if (createExtrusionSeam) {
454 if (quads) {
455 addExtrusionSeamFaces(numFaces, segmentCount - 1, adjacencyList);
456 } else {
457 addTriangleExtrusionSeamFaces(numFaces, segmentCount - 1, adjacencyList);
458 }
459 }
460
461 if (quads) {
462 calculateFaceNormals<VectorType, IndexType, VectorType, true>(numFaces, positions, indexes, faceNormals);
463 } else {
464 calculateFaceNormals<VectorType, IndexType, VectorType, false>(numFaces, positions, indexes, faceNormals);
465 }
466
467 if (correctNormals) {
468 correctZeroLengthNormals(faceNormals.data(), faceNormals.size());
469 }
470
471 if (flipNormals) {
472 reverseNormalDirections(faceNormals.data(), faceNormals.size());
473 }
474
475 outputNormals.resize(numFaces * numVertexesPerFace);
476 outputPositions.resize(numFaces * numVertexesPerFace);
477
478 VectorType * normal = outputNormals.data();
479 VectorType * position = outputPositions.data();
480
481 calculateFacetVertexNormals(faceNormals, adjacencyList, indexes, creaseAngle, quads, normal);
482
483 reindexValues(positions, indexes, numFaces, numIndicesPerFace, position);
484
485 if (texCoords) {
486 outputTexCoords->resize(numFaces * numVertexesPerFace);
487
488 reindexValues(texCoords, indexes, numFaces, numIndicesPerFace, outputTexCoords->data());
489 }
490
491 outputIndexes.resize(numFaces * numIndicesPerFace);
492 IndexType * newIndex = outputIndexes.data();
493
494 if (quads) {
495 generateLinearIndexes<IndexType, true>(numFaces, newIndex);
496 } else {
497 generateLinearIndexes<IndexType, false>(numFaces, newIndex);
498 }
499 }
500 }
501}
@ Geometry
Store entity vector fields (vector<vec3>, vector<vec2>, vector<int>, vector<float>).
void addExtrusionSeamFaces(const size_t numFaces, size_t segmentCount, std::vector< AdjacencyListType > &adjacencyList)
Adds the faces along the seam of an extrusion to the adjacency list of the seam vertexes.
void calculateFaceNormals(const size_t numFaces, const PositionType *positions, const IndexType *indices, std::vector< NormalType > &faceNormals)
Calculates face normals for the list of faces given.
void generateExtrusionNormals(VectorType *positions, const size_t numVertexes, IndexType *indexes, const size_t numIndexes, int segmentCount, bool quads, bool flipNormals, std::vector< VectorType > &normals)
Generate normals for an extrusion shape.
void generateAdjacencyInfo(const size_t numFaces, const IndexType *indices, const size_t positionOffset, std::vector< AdjacencyListType > &adjacencyList)
Generates vertex adjacency info for the list of faces given by indices.
void generateNormals(VectorType *positions, const size_t numVertexes, IndexType *indexes, const size_t numIndexes, bool quads, bool flipNormals, bool correctNormals, std::vector< VectorType > &normals)
Generate normals for the given shape.
void correctZeroLengthNormals(NormalType *normals, const size_t numNormals)
Corrects invalid normals (zero length) by setting them to the unit Z vector.
void calculateSmoothVertexNormals(NormalType *normals, const size_t numNormals, const std::vector< NormalType > &faceNormals, const size_t positionOffset, const std::vector< AdjacencyListType > &adjacencyList)
Calculates smooth vertex normals from the list of face normals and adjacency info given.
void generateLinearIndexes(const size_t numFaces, IndexType *indexes)
Generates a set of indexes from zero upwards for the amount of faces given.
void reverseNormalDirections(NormalType *normals, const size_t numNormals)
Reverse the set of normals given.
void reindexValues(const ValueType *inputValues, const IndexType *indexes, const size_t numFaces, const size_t numIndexesPerFace, ValueType *outputValues)
Reindexes the input values into the output values based on the index array given.
void calculateFacetVertexNormals(const std::vector< NormalType > &faceNormals, const std::vector< AdjacencyListType > &adjacencyList, const IndexType *indexes, const float creaseAngle, bool quads, NormalType *normals)
Calculates per vertex facet normals.
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
Utility class for storing the face indexes a vertex belongs to.
const IndexType & operator[](size_t i) const
Gets the index at the given location.
AdjacencyList_T()=default
Constructs a new list. Initial index count is 0.
size_t size() const
Gets the size of the index array. Does not correspond to the size of allocated memory.
void push_back(IndexType index)
Adds a new index value to the back of the index vector.