Vuo 2.4.4
Loading...
Searching...
No Matches
VuoMeshUtility.cc
Go to the documentation of this file.
1
10#include "VuoMeshUtility.h"
11#include <vector>
12
13extern "C"
14{
15#ifdef VUO_COMPILER
17 "title" : "VuoMeshUtility",
18 "dependencies" : [
19 "VuoList_VuoPoint4d",
20 "VuoList_VuoInteger"
21 ]
22 });
23#endif
24}
25
27#define PI 3.14159265359f
28
34{
36 {
37 VUserLog("VuoMeshUtility_calculateNormals() requires element assembly method VuoMesh_IndividualTriangles.");
38 return;
39 }
40
41 unsigned int vertexCount, elementCount, *elements;
42 float *vertices, *textureCoordinates, *colors;
43 VuoMesh_getCPUBuffers(mesh, &vertexCount, &vertices, nullptr, &textureCoordinates, &colors, &elementCount, &elements);
44 if (!vertexCount)
45 return;
46
47 float *normals = (float *)malloc(sizeof(float) * 3 * vertexCount);
48 unsigned int* normalsAverage = (unsigned int*)malloc(sizeof(unsigned int) * vertexCount);
49
50 // initialize normals to {0,0,0,0}
51 for(unsigned int i = 0; i < vertexCount; i++)
52 {
53 normals[i * 3 ] = 0;
54 normals[i * 3 + 1] = 0;
55 normals[i * 3 + 2] = 0;
56 normalsAverage[i] = 0;
57 }
58
59 // sum up all normals
60 if (elementCount)
61 for (unsigned int i = 0; i < elementCount; i += 3)
62 {
63 unsigned int i0 = elements[i + 0];
64 unsigned int i1 = elements[i + 1];
65 unsigned int i2 = elements[i + 2];
66
67 VuoPoint3d a = VuoPoint3d_makeFromArray(&vertices[i0 * 3]);
68 VuoPoint3d b = VuoPoint3d_makeFromArray(&vertices[i1 * 3]);
69 VuoPoint3d c = VuoPoint3d_makeFromArray(&vertices[i2 * 3]);
70
71 VuoPoint3d cross = VuoPoint3d_normalize(VuoPoint3d_crossProduct(b - a, c - a));
72
73 VuoPoint3d n = VuoPoint3d_makeFromArray(&normals[i0 * 3]) + cross;
74 VuoPoint3d_setArray(&normals[i0 * 3], n);
75
76 n = VuoPoint3d_makeFromArray(&normals[i1 * 3]) + cross;
77 VuoPoint3d_setArray(&normals[i1 * 3], n);
78
79 n = VuoPoint3d_makeFromArray(&normals[i2 * 3]) + cross;
80 VuoPoint3d_setArray(&normals[i2 * 3], n);
81
82 normalsAverage[i0]++;
83 normalsAverage[i1]++;
84 normalsAverage[i2]++;
85 }
86 else
87 for (unsigned int i = 0; i < vertexCount; i += 3)
88 {
89 unsigned int i0 = i + 0;
90 unsigned int i1 = i + 1;
91 unsigned int i2 = i + 2;
92
93 VuoPoint3d a = VuoPoint3d_makeFromArray(&vertices[i0 * 3]);
94 VuoPoint3d b = VuoPoint3d_makeFromArray(&vertices[i1 * 3]);
95 VuoPoint3d c = VuoPoint3d_makeFromArray(&vertices[i2 * 3]);
96
97 VuoPoint3d cross = VuoPoint3d_normalize(VuoPoint3d_crossProduct(b - a, c - a));
98
99 VuoPoint3d n = VuoPoint3d_makeFromArray(&normals[i0 * 3]) + cross;
100 VuoPoint3d_setArray(&normals[i0 * 3], n);
101
102 n = VuoPoint3d_makeFromArray(&normals[i1 * 3]) + cross;
103 VuoPoint3d_setArray(&normals[i1 * 3], n);
104
105 n = VuoPoint3d_makeFromArray(&normals[i2 * 3]) + cross;
106 VuoPoint3d_setArray(&normals[i2 * 3], n);
107
108 normalsAverage[i0]++;
109 normalsAverage[i1]++;
110 normalsAverage[i2]++;
111 }
112
113 // now go through and average
114 for (unsigned int i = 0; i < vertexCount; i++)
115 {
116 normals[i * 3 ] /= normalsAverage[i];
117 normals[i * 3 + 1] /= normalsAverage[i];
118 normals[i * 3 + 2] /= normalsAverage[i];
119 }
120
121 free(normalsAverage);
122
123 VuoMesh_setCPUBuffers(mesh, vertexCount, vertices, normals, textureCoordinates, colors, elementCount, elements);
124}
125
129bool VuoMeshUtility_bounds(const VuoMesh mesh, VuoPoint3d *min, VuoPoint3d *max)
130{
131 unsigned int vertexCount;
132 float *positions;
133 VuoMesh_getCPUBuffers(mesh, &vertexCount, &positions, nullptr, nullptr, nullptr, nullptr, nullptr);
134 if (!vertexCount)
135 return false;
136
137 // Calculate the center of the mesh.
138 *min = *max = VuoPoint3d_makeFromArray(&positions[0]);
139
140 for (unsigned int i = 1; i < vertexCount; i++)
141 {
142 *min = VuoPoint3d_min(*min, VuoPoint3d_makeFromArray(&positions[i * 3]));
143 *max = VuoPoint3d_max(*max, VuoPoint3d_makeFromArray(&positions[i * 3]));
144 }
145
146 return true;
147}
148
154{
157
158 unsigned int vertexCount, elementCount, *elements;
159 float *vertices, *normals, *colors;
160 VuoMesh_getCPUBuffers(mesh, &vertexCount, &vertices, &normals, nullptr, &colors, &elementCount, &elements);
161 if (!vertexCount)
162 return;
163
164 unsigned int dup_vertex_start = vertexCount;
165
166 float *textureCoordinates = (float *)malloc(sizeof(float) * 2 * vertexCount);
167
168 VuoPoint3d center;
169 {
170 VuoPoint3d min, max;
171 VuoMeshUtility_bounds(mesh, &min, &max);
172 center = (min + max) / 2.f;
173 }
174
175 VuoPoint2d min, max;
176 for (unsigned int i = 0; i < vertexCount; i++)
177 {
178 VuoPoint3d v = VuoPoint3d_normalize(vertices[i] - center);
179
180 VuoPoint2d tc = (VuoPoint2d){
181 1.f - (.5f + (atan2f(v.z, v.x) / (2.f * PI))),
182 1.f - (.5f - (asinf(v.y) / PI))};
183
184 if(i >= dup_vertex_start)
185 tc.x -= 1.;
186
187 if(i == 0)
188 {
189 min = tc;
190 max = tc;
191 }
192
193 min.x = fmin(min.x, tc.x);
194 min.y = fmin(min.y, tc.y);
195
196 max.x = fmax(max.x, tc.x);
197 max.y = fmax(max.y, tc.y);
198
199 textureCoordinates[i * 2 ] = tc.x;
200 textureCoordinates[i * 2 + 1] = tc.y;
201 }
202
203 VuoPoint2d scale = { 1 / (max.x - min.x), 1 / (max.y - min.y) };
204
205 for (unsigned int i = 0; i < vertexCount; i++)
206 {
207 textureCoordinates[i * 2 ] -= min.x;
208 textureCoordinates[i * 2 + 1] -= min.y;
209 textureCoordinates[i * 2 ] *= scale.x;
210 textureCoordinates[i * 2 + 1] *= scale.y;
211 }
212
213 VuoMesh_setCPUBuffers(mesh, vertexCount, vertices, normals, textureCoordinates, colors, elementCount, elements);
214
215 // remove vertices *after* generating textures because
216 // textures uses the overflowing vertices to determine
217 // which coordinates need to be wrapped.
218 if (VuoMesh_getElementAssemblyMethod(mesh) == VuoMesh_IndividualTriangles && dup_vertex_start < vertexCount)
220}
221
225enum Plane {
226 PlaneX,
227 PlaneY,
228 PlaneZ,
229 PlaneNegX,
230 PlaneNegY,
231 PlaneNegZ
232};
233
239{
240 float x = fabs(normal.x),
241 y = fabs(normal.y),
242 z = fabs(normal.z);
243
244 if( x > y && x > z ) {
245 return normal.x < 0 ? PlaneNegX : PlaneX;
246 } else if( y > x && y > z ) {
247 return normal.y < 0 ? PlaneNegY : PlaneY;
248 } else {
249 return normal.z < 0 ? PlaneNegZ : PlaneZ;
250 }
251}
252
256static inline VuoPoint3d VuoMeshUtility_averageNormal(VuoPoint3d a, VuoPoint3d b, VuoPoint3d c)
257{
258 return (VuoPoint3d){(a.x + b.x + c.x) / 3.f,
259 (a.y + b.y + c.y) / 3.f,
260 (a.z + b.z + c.z) / 3.f};
261}
262
267{
268 VuoPoint3d min, max;
269 VuoMeshUtility_bounds(mesh, &min, &max);
270 VuoPoint3d range = max - min;
271
272 unsigned int vertexCount, elementCount, *elements;
273 float *positions, *normals, *colors;
274 VuoMesh_getCPUBuffers(mesh, &vertexCount, &positions, &normals, nullptr, &colors, &elementCount, &elements);
275 if (!vertexCount || !normals)
276 return;
277
278 float *textureCoordinates = (float *)malloc(sizeof(float) * 2 * vertexCount);
279
280 for (unsigned int i = 0; i < vertexCount; i++)
281 {
282 VuoPoint3d p = VuoPoint3d_makeFromArray(&positions[i * 3]);
283 VuoPoint3d n = VuoPoint3d_makeFromArray(&normals[i * 3]);
284 VuoPoint3d uv = p - min;
285
286 uv.x = uv.x / range.x;
287 uv.y = uv.y / range.y;
288 uv.z = uv.z / range.z;
289
291 {
292 case PlaneX:
293 case PlaneNegX:
294 textureCoordinates[i * 2 ] = uv.z;
295 textureCoordinates[i * 2 + 1] = uv.y;
296 break;
297
298 case PlaneY:
299 case PlaneNegY:
300 textureCoordinates[i * 2 ] = uv.x;
301 textureCoordinates[i * 2 + 1] = uv.z;
302 break;
303
304 case PlaneZ:
305 case PlaneNegZ:
306 textureCoordinates[i * 2 ] = uv.x;
307 textureCoordinates[i * 2 + 1] = uv.y;
308 break;
309 }
310 }
311
312 VuoMesh_setCPUBuffers(mesh, vertexCount, positions, normals, textureCoordinates, colors, elementCount, elements);
313}
314
319{
320 VuoPoint3d min, max;
321 VuoMeshUtility_bounds(mesh, &min, &max);
322 VuoPoint3d range = max - min;
323
324 unsigned int vertexCount, elementCount, *elements;
325 float *positions, *normals, *colors;
326 VuoMesh_getCPUBuffers(mesh, &vertexCount, &positions, &normals, nullptr, &colors, &elementCount, &elements);
327 if (!vertexCount || !normals || !elementCount)
328 return;
329
330 float *textureCoordinates = (float *)malloc(sizeof(float) * 2 * vertexCount);
331
332 for (unsigned int i = 0; i < elementCount; i+=3)
333 {
334 unsigned int a = elements[i+0],
335 b = elements[i+1],
336 c = elements[i+2];
337
338 VuoPoint3d pa = VuoPoint3d_makeFromArray(&positions[a * 3]);
339 VuoPoint3d pb = VuoPoint3d_makeFromArray(&positions[b * 3]);
340 VuoPoint3d pc = VuoPoint3d_makeFromArray(&positions[c * 3]);
341
342 VuoPoint3d uv_a = pa - min;
343 VuoPoint3d uv_b = pb - min;
344 VuoPoint3d uv_c = pc - min;
345
346 uv_a.x = uv_a.x / range.x;
347 uv_b.x = uv_b.x / range.x;
348 uv_c.x = uv_c.x / range.x;
349 uv_a.y = uv_a.y / range.y;
350 uv_b.y = uv_b.y / range.y;
351 uv_c.y = uv_c.y / range.y;
352 uv_a.z = uv_a.z / range.z;
353 uv_b.z = uv_b.z / range.z;
354 uv_c.z = uv_c.z / range.z;
355
356 switch (VuoMeshUtility_calculateBestPlane( VuoMeshUtility_averageNormal(normals[a], normals[b], normals[c])))
357 {
358 case PlaneX:
359 case PlaneNegX:
360 textureCoordinates[a * 2 ] = uv_a.z;
361 textureCoordinates[a * 2 + 1] = uv_a.y;
362 textureCoordinates[b * 2 ] = uv_b.z;
363 textureCoordinates[b * 2 + 1] = uv_b.y;
364 textureCoordinates[c * 2 ] = uv_c.z;
365 textureCoordinates[c * 2 + 1] = uv_c.y;
366 break;
367
368 case PlaneY:
369 case PlaneNegY:
370 textureCoordinates[a * 2 ] = uv_a.x;
371 textureCoordinates[a * 2 + 1] = uv_a.z;
372 textureCoordinates[b * 2 ] = uv_b.x;
373 textureCoordinates[b * 2 + 1] = uv_b.z;
374 textureCoordinates[c * 2 ] = uv_c.x;
375 textureCoordinates[c * 2 + 1] = uv_c.z;
376 break;
377
378 case PlaneZ:
379 case PlaneNegZ:
380 textureCoordinates[a * 2 ] = uv_a.x;
381 textureCoordinates[a * 2 + 1] = uv_a.y;
382 textureCoordinates[b * 2 ] = uv_b.x;
383 textureCoordinates[b * 2 + 1] = uv_b.y;
384 textureCoordinates[c * 2 ] = uv_c.x;
385 textureCoordinates[c * 2 + 1] = uv_c.y;
386 break;
387 }
388 }
389
390 VuoMesh_setCPUBuffers(mesh, vertexCount, positions, normals, textureCoordinates, colors, elementCount, elements);
391}
392
397{
398 unsigned int vertexCount, elementCount, *indices;
399 float *positions, *normals, *textures;
400 VuoMesh_getCPUBuffers(mesh, &vertexCount, &positions, &normals, &textures, nullptr, &elementCount, &indices);
401 if (!vertexCount || !elementCount)
402 return;
403
404 unsigned int *seam_indices = (unsigned int *)malloc(sizeof(unsigned int) * elementCount);
405 memcpy(seam_indices, indices, sizeof(unsigned int) * elementCount);
406
407 std::vector<float> seam_positions = std::vector<float>(positions != NULL ? vertexCount * 3 : 0);
408 if(positions) std::copy(positions, positions + vertexCount * 3, seam_positions.begin());
409 std::vector<float> seam_normals = std::vector<float>(normals != NULL ? vertexCount * 3 : 0);
410 if(normals) std::copy(normals, normals + vertexCount * 3, seam_normals.begin());
411 std::vector<float> seam_textures = std::vector<float>(textures != NULL ? vertexCount * 2 : 0);
412 if(textures) std::copy(textures, textures + vertexCount * 2, seam_textures.begin());
413
414 const VuoPoint3d left = (VuoPoint3d){-1., 0., 0.};
415 const VuoPoint3d forward = (VuoPoint3d){ 0., 0., 1.};
416 const VuoPoint3d up = (VuoPoint3d){ 0., 1., 0.};
417 const VuoPoint3d down = (VuoPoint3d){ 0., -1., 0.};
418
419 // get mesh bounds and center point
420 VuoPoint3d min, max;
421 VuoMeshUtility_bounds(mesh, &min, &max);
422 VuoPoint3d center = (max + min) / 2.f;
423
424 // Insert vertical seam along western hemisphere
425 for (unsigned int i = 0; i < elementCount; i+=3)
426 {
427 if(indices[i] > vertexCount || indices[i+1] > vertexCount || indices[i+2] > vertexCount)
428 {
429 VUserLog("Triangle indices are referencing out of bounds vertices. (%u, %u, %u) Vertex Count: %u", indices[i], indices[i+1], indices[i+2], vertexCount);
430 continue;
431 }
432 // {0,1} {1,2}, {2,0}
433 for(int n = 0; n < 3; n++)
434 {
435 unsigned int x = i + n;
436 unsigned int y = i + (n == 2 ? 0 : n+1);
437
438 // check if this triangle edge crosses the new seam
439 VuoPoint3d px = VuoPoint3d_makeFromArray(&positions[indices[x] * 3]);
440 VuoPoint3d py = VuoPoint3d_makeFromArray(&positions[indices[y] * 3]);
441 VuoPoint3d v0 = VuoPoint3d_normalize(px - center);
442 VuoPoint3d v1 = VuoPoint3d_normalize(py - center);
443
444 if (VuoPoint3d_areEqual(v0, up) || VuoPoint3d_areEqual(v0, down)
445 || VuoPoint3d_areEqual(v1, up) || VuoPoint3d_areEqual(v1, down))
446 continue;
447
448 // if v0 is in left back quadrant, and v1 is in the forward half, v0 needs to be duplicated. Ditto in opposite order
449 if (VuoPoint3d_dotProduct(v0, left) > 0
450 && VuoPoint3d_dotProduct(v0, forward) < 0
451 && VuoPoint3d_dotProduct(v1, forward) >= 0)
452 {
453 if (positions)
454 {
455 seam_positions.push_back(positions[indices[x] * 3 ]);
456 seam_positions.push_back(positions[indices[x] * 3 + 1]);
457 seam_positions.push_back(positions[indices[x] * 3 + 2]);
458 }
459 if (normals)
460 {
461 seam_normals.push_back(normals[indices[x] * 3 ]);
462 seam_normals.push_back(normals[indices[x] * 3 + 1]);
463 seam_normals.push_back(normals[indices[x] * 3 + 2]);
464 }
465 if (textures)
466 {
467 seam_textures.push_back(textures[indices[x] * 2 ]);
468 seam_textures.push_back(textures[indices[x] * 2 + 1]);
469 }
470
471 seam_indices[x] = seam_positions.size() / 3 - 1;
472 }
473
474 // if v1 is in left back quadrant, but v0 isn't, duplicate v1
475 if (VuoPoint3d_dotProduct(v1, left) > 0
476 && VuoPoint3d_dotProduct(v1, forward) < 0
477 && VuoPoint3d_dotProduct(v0, forward) >= 0)
478 {
479 if(positions)
480 {
481 seam_positions.push_back(positions[indices[y] * 3 ]);
482 seam_positions.push_back(positions[indices[y] * 3 + 1]);
483 seam_positions.push_back(positions[indices[y] * 3 + 2]);
484 }
485 if(normals)
486 {
487 seam_normals.push_back(normals[indices[y] * 3 ]);
488 seam_normals.push_back(normals[indices[y] * 3 + 1]);
489 seam_normals.push_back(normals[indices[y] * 3 + 2]);
490 }
491 if(textures)
492 {
493 seam_textures.push_back(textures[indices[y] * 2 ]);
494 seam_textures.push_back(textures[indices[y] * 2 + 1]);
495 }
496
497 seam_indices[y] = seam_positions.size() / 3 - 1;
498 }
499 }
500 }
501
502 float *newPositions = (float *)malloc(sizeof(float) * seam_positions.size());
503 std::copy(seam_positions.begin(), seam_positions.end(), newPositions);
504
505 float *newNormals = nullptr;
506 if (normals)
507 {
508 newNormals = (float *)malloc(sizeof(float) * seam_normals.size());
509 std::copy(seam_normals.begin(), seam_normals.end(), newNormals);
510 }
511
512 float *newTextureCoordinates = nullptr;
513 if (textures)
514 {
515 newTextureCoordinates = (float *)malloc(sizeof(float) * seam_textures.size());
516 std::copy(seam_textures.begin(), seam_textures.end(), newTextureCoordinates);
517 }
518
519 VuoMesh_setCPUBuffers(mesh, seam_positions.size() / 3,
520 newPositions, newNormals, newTextureCoordinates, nullptr,
521 elementCount, seam_indices);
522}
523
527static int compare(const void* lhs, const void* rhs)
528{
529 return ( *(int*)lhs - *(int*)rhs );
530}
531
536{
537 unsigned int vertexCount, elementCount, *elements;
538 float *positions, *normals, *textureCoordinates;
539 VuoMesh_getCPUBuffers(mesh, &vertexCount, &positions, &normals, &textureCoordinates, nullptr, &elementCount, &elements);
540 if (!vertexCount || !elementCount)
541 return;
542
543 unsigned int* sortedTriangles = (unsigned int*)malloc(sizeof(unsigned int) * elementCount);
544 memcpy(sortedTriangles, elements, sizeof(unsigned int) * elementCount);
545
546 // sort indices from smallest to largest
547 qsort(sortedTriangles, elementCount, sizeof(unsigned int), compare);
548
549 // lop off any elements that are referencing out of bounds vertices
550 unsigned int last = elementCount-1;
551
552 while( sortedTriangles[last] > vertexCount )
553 last--;
554
555 // if there are invalid indices, reallocate the the elements array with only the good'ns.
556 if(last < elementCount-1)
557 {
558 unsigned int* pruned_elements = (unsigned int*)malloc(sizeof(unsigned int) * (last+1));
559
560 unsigned int n = 0;
561
562 for(unsigned int i = 0; i < elementCount; i++)
563 {
564 if (elements[i] < vertexCount)
565 pruned_elements[n++] = elements[i];
566 }
567
568 elementCount = last+1;
569 elements = pruned_elements;
570 }
571
572 // generate list of unused indices
573 std::vector<unsigned int> unused;
574
575 int lastVal = -1;
576 for(int i = 0; i < elementCount && sortedTriangles[i] < elementCount; i++)
577 {
578 for(int n = lastVal+1; n < sortedTriangles[i]; n++)
579 {
580 unused.push_back(n);
581 }
582
583 lastVal = sortedTriangles[i];
584 }
585 free(sortedTriangles);
586
587 // rebuild index array by substracting the amount of vertices removed in the range lower
588 // than element[i]
589 int unusedCount = unused.size();
590 for(int i = 0; i < elementCount; i++)
591 {
592 unsigned int vertex = elements[i];
593
594 if(vertex > vertexCount)
595 continue;
596
597 int n = 0;
598
599 for(auto& val : unused)
600 {
601 if(val < vertex)
602 n++;
603 else
604 break;
605 }
606
607 elements[i] -= n;
608 }
609
610 // rebuild vertex arrays without the unused values
611 // not using vertexCount-unusedCount because unused may contain duplicates
612 std::vector<float> new_positions = std::vector<float>(vertexCount * 3);
613 std::vector<float> new_normals = std::vector<float>(normals ? vertexCount * 3 : 0);
614 std::vector<float> new_textureCoordinates = std::vector<float>(textureCoordinates ? vertexCount * 2 : 0);
615
616 unsigned int curIndex = 0, n = 0;
617 for(int i = 0; i < vertexCount; i++)
618 {
619 if(curIndex >= unusedCount || i < unused[curIndex])
620 {
621 new_positions[n * 3 ] = positions[i * 3 ];
622 new_positions[n * 3 + 1] = positions[i * 3 + 1];
623 new_positions[n * 3 + 2] = positions[i * 3 + 2];
624 if (normals)
625 {
626 new_normals[n * 3 ] = normals[i * 3 ];
627 new_normals[n * 3 + 1] = normals[i * 3 + 1];
628 new_normals[n * 3 + 2] = normals[i * 3 + 2];
629 }
630 if (textureCoordinates)
631 {
632 new_textureCoordinates[n * 2 ] = textureCoordinates[i * 2 ];
633 new_textureCoordinates[n * 2 + 1] = textureCoordinates[i * 2 + 1];
634 }
635
636 n++;
637 }
638 else
639 {
640 do {
641 curIndex++;
642 } while(curIndex < unusedCount && unused[curIndex] <= i);
643 }
644 }
645
646 // Re-apply points to mesh
647 unsigned int culledVertexCount = new_positions.size();
648
649 float *newPositions = (float *)malloc(sizeof(float) * 3 * culledVertexCount);
650 std::copy(new_positions.begin(), new_positions.end(), newPositions);
651
652 float *newNormals = nullptr;
653 if (normals)
654 {
655 newNormals = (float *)malloc(sizeof(float) * 3 * culledVertexCount);
656 std::copy(new_normals.begin(), new_normals.end(), newNormals);
657 }
658
659 float *newTextureCoordinates = nullptr;
660 if (textureCoordinates)
661 {
662 newTextureCoordinates = (float *)malloc(sizeof(float) * 2 * culledVertexCount);
663 std::copy(new_textureCoordinates.begin(), new_textureCoordinates.end(), newTextureCoordinates);
664 }
665
666 VuoMesh_setCPUBuffers(mesh, culledVertexCount,
667 newPositions, newNormals, newTextureCoordinates, nullptr,
668 elementCount, elements);
669}