Vuo  2.0.0
VuoMeshUtility.cc
Go to the documentation of this file.
1 
10 #include "VuoMeshUtility.h"
11 #include "module.h"
12 #include <vector>
13 
14 extern "C"
15 {
16 #ifdef VUO_COMPILER
18  "title" : "VuoMeshUtility",
19  "dependencies" : [
20  "VuoList_VuoPoint4d",
21  "VuoList_VuoInteger"
22  ]
23  });
24 #endif
25 }
26 
28 #define PI 3.14159265359
29 
33 static inline VuoPoint4d VuoPoint4d_min(const VuoPoint4d lhs, const VuoPoint4d rhs)
34 {
35  return VuoPoint4d_make(
36  fmin(lhs.x, rhs.x),
37  fmin(lhs.y, rhs.y),
38  fmin(lhs.z, rhs.z),
39  1.
40  );
41 }
42 
46 static inline VuoPoint4d VuoPoint4d_max(const VuoPoint4d lhs, const VuoPoint4d rhs)
47 {
48  return VuoPoint4d_make(
49  fmax(lhs.x, rhs.x),
50  fmax(lhs.y, rhs.y),
51  fmax(lhs.z, rhs.z),
52  1.
53  );
54 }
55 
61 {
63  {
64  VUserLog("VuoMeshUtility_calculateNormals() requires element assembly method VuoMesh_IndividualTriangles.");
65  return;
66  }
67 
68  unsigned int elementCount = submesh->elementCount;
69  unsigned int vertexCount = submesh->vertexCount;
70  const unsigned int* elements = submesh->elements;
71  const VuoPoint4d* vertices = submesh->positions;
72 
73  VuoPoint4d* normals = (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * vertexCount);
74  unsigned int* normalsAverage = (unsigned int*)malloc(sizeof(unsigned int) * vertexCount);
75 
76  // initialize normals to {0,0,0,0}
77  for(unsigned int i = 0; i < vertexCount; i++)
78  {
79  normals[i] = VuoPoint4d_make(0,0,0,0);
80  normalsAverage[i] = 0;
81  }
82 
83  // sum up all normals
84  if (elementCount)
85  for (unsigned int i = 0; i < elementCount; i += 3)
86  {
87  unsigned int i0 = elements[i + 0];
88  unsigned int i1 = elements[i + 1];
89  unsigned int i2 = elements[i + 2];
90 
91  VuoPoint4d a = vertices[i0];
92  VuoPoint4d b = vertices[i1];
93  VuoPoint4d c = vertices[i2];
94 
96 
97  normals[i0] = VuoPoint4d_add(normals[i0], cross);
98  normals[i1] = VuoPoint4d_add(normals[i1], cross);
99  normals[i2] = VuoPoint4d_add(normals[i2], cross);
100 
101  normalsAverage[i0]++;
102  normalsAverage[i1]++;
103  normalsAverage[i2]++;
104  }
105  else
106  for (unsigned int i = 0; i < vertexCount; i += 3)
107  {
108  unsigned int i0 = i + 0;
109  unsigned int i1 = i + 1;
110  unsigned int i2 = i + 2;
111 
112  VuoPoint4d a = vertices[i0];
113  VuoPoint4d b = vertices[i1];
114  VuoPoint4d c = vertices[i2];
115 
117 
118  normals[i0] = VuoPoint4d_add(normals[i0], cross);
119  normals[i1] = VuoPoint4d_add(normals[i1], cross);
120  normals[i2] = VuoPoint4d_add(normals[i2], cross);
121 
122  normalsAverage[i0]++;
123  normalsAverage[i1]++;
124  normalsAverage[i2]++;
125  }
126 
127  // now go through and average
128  for(unsigned int i = 0; i < vertexCount; i++)
129  {
130  normals[i] = VuoPoint4d_multiply(normals[i], 1./(float)normalsAverage[i]);
131  }
132 
133  free(normalsAverage);
134 
135  if(submesh->normals != NULL) free(submesh->normals);
136  submesh->normals = normals;
137 }
138 
146 {
147  unsigned int vertexCount = submesh->vertexCount;
148  const VuoPoint4d *vertex = submesh->positions;
149  const VuoPoint4d *normal = submesh->normals;
150  const VuoPoint4d *texcoord = submesh->textureCoordinates;
151  unsigned int triangleCount = submesh->elementCount;
152  const unsigned int *triangle = submesh->elements;
153 
155  {
156  VUserLog("VuoMeshUtility_calculateNormals() requires element assembly method VuoMesh_IndividualTriangles.");
157  return;
158  }
159 
160  VuoPoint4d *tangent = submesh->tangents != NULL ? submesh->tangents : (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * vertexCount);
161  VuoPoint4d *bitangent = submesh->bitangents != NULL ? submesh->bitangents : (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * vertexCount);
162 
163  int tan1count = vertexCount * 2;
164  VuoPoint4d *tan1 = new VuoPoint4d[tan1count];
165  VuoPoint4d *tan2 = tan1 + vertexCount;
166 
167  for( int i = 0; i < tan1count; i++)
168  tan1[i] = (VuoPoint4d) {0,0,0,0};
169 
170  if (triangleCount)
171  for (int a = 0; a < triangleCount; a += 3)
172  {
173  long i1 = triangle[a + 0];
174  long i2 = triangle[a + 1];
175  long i3 = triangle[a + 2];
176 
177  if (i1 > vertexCount || i2 > vertexCount || i3 > vertexCount)
178  {
179  VUserLog("Skipping tangent generation for triangle: (%lu, %lu, %lu) because it references out of bounds vertices.", i1, i2, i3);
180  continue;
181  }
182 
183  const VuoPoint4d &v1 = vertex[i1];
184  const VuoPoint4d &v2 = vertex[i2];
185  const VuoPoint4d &v3 = vertex[i3];
186 
187  const VuoPoint4d &w1 = texcoord[i1];
188  const VuoPoint4d &w2 = texcoord[i2];
189  const VuoPoint4d &w3 = texcoord[i3];
190 
191  float x1 = v2.x - v1.x;
192  float x2 = v3.x - v1.x;
193  float y1 = v2.y - v1.y;
194  float y2 = v3.y - v1.y;
195  float z1 = v2.z - v1.z;
196  float z2 = v3.z - v1.z;
197 
198  float s1 = w2.x - w1.x;
199  float s2 = w3.x - w1.x;
200  float t1 = w2.y - w1.y;
201  float t2 = w3.y - w1.y;
202 
203  float r = 1.0 / (s1 * t2 - s2 * t1);
204  VuoPoint4d sdir = (VuoPoint4d){
205  (t2 * x1 - t1 * x2) * r,
206  (t2 * y1 - t1 * y2) * r,
207  (t2 * z1 - t1 * z2) * r,
208  1.
209  };
210 
211  VuoPoint4d tdir = (VuoPoint4d){
212  (s1 * x2 - s2 * x1) * r,
213  (s1 * y2 - s2 * y1) * r,
214  (s1 * z2 - s2 * z1) * r,
215  1.
216  };
217 
218  tan1[i1] = VuoPoint4d_add(tan1[i1], sdir);
219  tan1[i2] = VuoPoint4d_add(tan1[i2], sdir);
220  tan1[i3] = VuoPoint4d_add(tan1[i3], sdir);
221 
222  tan2[i1] = VuoPoint4d_add(tan2[i1], tdir);
223  tan2[i2] = VuoPoint4d_add(tan2[i2], tdir);
224  tan2[i3] = VuoPoint4d_add(tan2[i3], tdir);
225  }
226  else
227  for (int a = 0; a < vertexCount; a += 3)
228  {
229  long i1 = a + 0;
230  long i2 = a + 1;
231  long i3 = a + 2;
232 
233  const VuoPoint4d &v1 = vertex[i1];
234  const VuoPoint4d &v2 = vertex[i2];
235  const VuoPoint4d &v3 = vertex[i3];
236 
237  const VuoPoint4d &w1 = texcoord[i1];
238  const VuoPoint4d &w2 = texcoord[i2];
239  const VuoPoint4d &w3 = texcoord[i3];
240 
241  float x1 = v2.x - v1.x;
242  float x2 = v3.x - v1.x;
243  float y1 = v2.y - v1.y;
244  float y2 = v3.y - v1.y;
245  float z1 = v2.z - v1.z;
246  float z2 = v3.z - v1.z;
247 
248  float s1 = w2.x - w1.x;
249  float s2 = w3.x - w1.x;
250  float t1 = w2.y - w1.y;
251  float t2 = w3.y - w1.y;
252 
253  float r = 1.0 / (s1 * t2 - s2 * t1);
254  VuoPoint4d sdir = (VuoPoint4d){
255  (t2 * x1 - t1 * x2) * r,
256  (t2 * y1 - t1 * y2) * r,
257  (t2 * z1 - t1 * z2) * r,
258  1.
259  };
260 
261  VuoPoint4d tdir = (VuoPoint4d){
262  (s1 * x2 - s2 * x1) * r,
263  (s1 * y2 - s2 * y1) * r,
264  (s1 * z2 - s2 * z1) * r,
265  1.
266  };
267 
268  tan1[i1] = VuoPoint4d_add(tan1[i1], sdir);
269  tan1[i2] = VuoPoint4d_add(tan1[i2], sdir);
270  tan1[i3] = VuoPoint4d_add(tan1[i3], sdir);
271 
272  tan2[i1] = VuoPoint4d_add(tan2[i1], tdir);
273  tan2[i2] = VuoPoint4d_add(tan2[i2], tdir);
274  tan2[i3] = VuoPoint4d_add(tan2[i3], tdir);
275  }
276 
277 
278  for (long a = 0; a < vertexCount; a++)
279  {
280  VuoPoint3d n = (VuoPoint3d){ normal[a].x, normal[a].y, normal[a].z };
281  VuoPoint3d t = (VuoPoint3d){ tan1[a].x, tan1[a].y, tan1[a].z };
282  VuoPoint3d t2 = (VuoPoint3d){ tan2[a].x, tan2[a].y, tan2[a].z };
283 
284  // Gram-Schmidt orthogonalize
287 
288  tangent[a] = (VuoPoint4d){tan.x, tan.y, tan.z, 0.};
289  bitangent[a] = (VuoPoint4d){ bitan.x, bitan.y, bitan.z, 0. };
290 
291  // Calculate handedness
292  tangent[a].w = (VuoPoint3d_dotProduct(VuoPoint3d_crossProduct(n, t), t2) < 0.0F) ? -1.0F : 1.0F;
293  bitangent[a].w = (VuoPoint3d_dotProduct(VuoPoint3d_crossProduct(n, t2), t) < 0.0F) ? -1.0F : 1.0F;
294  }
295 
296  submesh->tangents = tangent;
297  submesh->bitangents = bitangent;
298 
299  delete[] tan1;
300 }
301 
305 bool VuoMeshUtility_bounds(const VuoSubmesh mesh, VuoPoint4d* min, VuoPoint4d* max)
306 {
307  const VuoPoint4d* vertices = mesh.positions;
308 
309  if(mesh.vertexCount < 1)
310  return false;
311 
312  // Calculate the center of the mesh.
313  *min = mesh.positions[0];
314  *max = mesh.positions[0];
315 
316  for(int i = 1; i < mesh.vertexCount; i++)
317  {
318  *min = VuoPoint4d_min(*min, vertices[i]);
319  *max = VuoPoint4d_max(*max, vertices[i]);
320  }
321 
322  return true;
323 }
324 
330 {
331  if (!submesh->vertexCount || !submesh->elementCount)
332  return;
333 
334  unsigned int dup_vertex_start = submesh->vertexCount;
335 
337  VuoMeshUtility_insertSeam(submesh);
338 
339  const VuoPoint4d* vertices = submesh->positions;
340 
341  if (!submesh->textureCoordinates)
342  submesh->textureCoordinates = (VuoPoint4d *)malloc(sizeof(VuoPoint4d) * submesh->vertexCount);
343  VuoPoint4d* textures = submesh->textureCoordinates;
344 
345  VuoPoint4d min, max;
346  VuoMeshUtility_bounds(*submesh, &min, &max);
347  VuoPoint4d center = VuoPoint4d_multiply(VuoPoint4d_add(max, min), .5);
348 
349  for(int i = 0; i < submesh->vertexCount; i++)
350  {
351  VuoPoint4d v = VuoPoint4d_normalize(VuoPoint4d_subtract(vertices[i], center));
352 
353  textures[i] = (VuoPoint4d)
354  {
355  (float)(1 - (.5 + (atan2(v.z, v.x) / (2 * PI)))),
356  (float)(1. - (.5 - (asin(v.y) / PI))),
357  0.,
358  1.
359  };
360 
361  if(i >= dup_vertex_start)
362  textures[i].x -= 1.;
363 
364  if(i == 0)
365  {
366  min = textures[i];
367  max = textures[i];
368  }
369 
370  min.x = fmin(min.x, textures[i].x);
371  min.y = fmin(min.y, textures[i].y);
372 
373  max.x = fmax(max.x, textures[i].x);
374  max.y = fmax(max.y, textures[i].y);
375  }
376 
377  VuoPoint2d scale = { 1 / (max.x - min.x), 1 / (max.y - min.y) };
378 
379  for(int i = 0; i < submesh->vertexCount; i++)
380  {
381  textures[i].x -= min.x;
382  textures[i].y -= min.y;
383  textures[i].x *= scale.x;
384  textures[i].y *= scale.y;
385  }
386 
387  // remove vertices *after* generating textures because
388  // textures uses the overflowing vertices to determine
389  // which coordinates need to be wrapped.
390  if( submesh->elementAssemblyMethod == VuoMesh_IndividualTriangles && dup_vertex_start < submesh->vertexCount )
392 }
393 
397 enum Plane {
398  PlaneX,
399  PlaneY,
400  PlaneZ,
401  PlaneNegX,
402  PlaneNegY,
403  PlaneNegZ
404 };
405 
411 {
412  float x = fabs(normal.x),
413  y = fabs(normal.y),
414  z = fabs(normal.z);
415 
416  if( x > y && x > z ) {
417  return normal.x < 0 ? PlaneNegX : PlaneX;
418  } else if( y > x && y > z ) {
419  return normal.y < 0 ? PlaneNegY : PlaneY;
420  } else {
421  return normal.z < 0 ? PlaneNegZ : PlaneZ;
422  }
423 }
424 
428 static inline VuoPoint4d VuoMeshUtility_averageNormal(VuoPoint4d a, VuoPoint4d b, VuoPoint4d c)
429 {
430  return VuoPoint4d_make( (a.x + b.x + c.x) / 3.,
431  (a.y + b.y + c.y) / 3.,
432  (a.z + b.z + c.z) / 3.,
433  1. );
434 }
435 
440 {
441  VuoPoint4d min, max;
442  VuoMeshUtility_bounds(*submesh, &min, &max);
443  VuoPoint4d range = VuoPoint4d_subtract(max, min);
444 
445  const VuoPoint4d* positions = submesh->positions;
446  const VuoPoint4d* normals = submesh->normals;
447 
448  if( submesh->textureCoordinates == NULL )
449  submesh->textureCoordinates = (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * submesh->vertexCount);
450 
451  for(unsigned int i = 0; i < submesh->vertexCount; i++)
452  {
453  VuoPoint4d p = positions[i];
454  VuoPoint4d uv = VuoPoint4d_subtract(p, min);
455 
456  uv.x = uv.x / range.x;
457  uv.y = uv.y / range.y;
458  uv.z = uv.z / range.z;
459 
460  switch( VuoMeshUtility_calculateBestPlane(normals[i]) )
461  {
462  case PlaneX:
463  case PlaneNegX:
464  submesh->textureCoordinates[i] = VuoPoint4d_make(uv.z, uv.y, 0., 1.);
465  break;
466 
467  case PlaneY:
468  case PlaneNegY:
469  submesh->textureCoordinates[i] = VuoPoint4d_make(uv.x, uv.z, 0., 1.);
470  break;
471 
472  case PlaneZ:
473  case PlaneNegZ:
474  submesh->textureCoordinates[i] = VuoPoint4d_make(uv.x, uv.y, 0., 1.);
475  break;
476  }
477  }
478 }
479 
484 {
485  VuoPoint4d min, max;
486  VuoMeshUtility_bounds(*submesh, &min, &max);
487  VuoPoint4d range = VuoPoint4d_subtract(max, min);
488 
489  const VuoPoint4d* positions = submesh->positions;
490  const VuoPoint4d* normals = submesh->normals;
491 
492  if( submesh->textureCoordinates == NULL )
493  submesh->textureCoordinates = (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * submesh->vertexCount);
494 
495  for(unsigned int i = 0; i < submesh->elementCount; i+=3)
496  {
497  unsigned int a = submesh->elements[i+0],
498  b = submesh->elements[i+1],
499  c = submesh->elements[i+2];
500 
501  VuoPoint4d uv_a = VuoPoint4d_subtract(positions[a], min);
502  VuoPoint4d uv_b = VuoPoint4d_subtract(positions[b], min);
503  VuoPoint4d uv_c = VuoPoint4d_subtract(positions[c], min);
504 
505  uv_a.x = uv_a.x / range.x;
506  uv_b.x = uv_b.x / range.x;
507  uv_c.x = uv_c.x / range.x;
508  uv_a.y = uv_a.y / range.y;
509  uv_b.y = uv_b.y / range.y;
510  uv_c.y = uv_c.y / range.y;
511  uv_a.z = uv_a.z / range.z;
512  uv_b.z = uv_b.z / range.z;
513  uv_c.z = uv_c.z / range.z;
514 
515  switch( VuoMeshUtility_calculateBestPlane( VuoMeshUtility_averageNormal(normals[a], normals[b], normals[c]) ) )
516  {
517  case PlaneX:
518  case PlaneNegX:
519  submesh->textureCoordinates[a] = VuoPoint4d_make(uv_a.z, uv_a.y, 0., 1.);
520  submesh->textureCoordinates[b] = VuoPoint4d_make(uv_b.z, uv_b.y, 0., 1.);
521  submesh->textureCoordinates[c] = VuoPoint4d_make(uv_c.z, uv_c.y, 0., 1.);
522  break;
523 
524  case PlaneY:
525  case PlaneNegY:
526  submesh->textureCoordinates[a] = VuoPoint4d_make(uv_a.x, uv_a.z, 0., 1.);
527  submesh->textureCoordinates[b] = VuoPoint4d_make(uv_b.x, uv_b.z, 0., 1.);
528  submesh->textureCoordinates[c] = VuoPoint4d_make(uv_c.x, uv_c.z, 0., 1.);
529  break;
530 
531  case PlaneZ:
532  case PlaneNegZ:
533  submesh->textureCoordinates[a] = VuoPoint4d_make(uv_a.x, uv_a.y, 0., 1.);
534  submesh->textureCoordinates[b] = VuoPoint4d_make(uv_b.x, uv_b.y, 0., 1.);
535  submesh->textureCoordinates[c] = VuoPoint4d_make(uv_c.x, uv_c.y, 0., 1.);
536  break;
537  }
538  }
539 }
540 
544 static float dot(const VuoPoint4d lhs, const VuoPoint4d rhs)
545 {
546  return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;
547 }
548 
552 static bool VuoPoint4d_equals(const VuoPoint4d a, const VuoPoint4d b)
553 {
554  const float epsilon = .00001f;
555 
556  return fabs(a.x-b.x) < epsilon &&
557  fabs(a.y-b.y) < epsilon &&
558  fabs(a.z-b.z) < epsilon;
559 }
560 
565 {
566  const VuoPoint4d* positions = submesh->positions;
567  const VuoPoint4d* normals = submesh->normals;
568  const VuoPoint4d* tangents = submesh->tangents;
569  const VuoPoint4d* bitangents = submesh->bitangents;
570  const VuoPoint4d* textures = submesh->textureCoordinates;
571 
572  const unsigned int* indices = submesh->elements;
573  unsigned int* seam_indices = (unsigned int*)malloc(sizeof(unsigned int) * submesh->elementCount);
574  memcpy(seam_indices, indices, sizeof(unsigned int) * submesh->elementCount);
575  unsigned int vertexCount = submesh->vertexCount;
576 
577  std::vector<VuoPoint4d> seam_positions = std::vector<VuoPoint4d>(positions != NULL ? vertexCount : 0);
578  if(positions) std::copy(positions, positions + vertexCount, seam_positions.begin());
579  std::vector<VuoPoint4d> seam_normals = std::vector<VuoPoint4d>(normals != NULL ? vertexCount : 0);
580  if(normals) std::copy(normals, normals + vertexCount, seam_normals.begin());
581  std::vector<VuoPoint4d> seam_tangents = std::vector<VuoPoint4d>(tangents != NULL ? vertexCount : 0);
582  if(tangents) std::copy(tangents, tangents + vertexCount, seam_tangents.begin());
583  std::vector<VuoPoint4d> seam_bitangents = std::vector<VuoPoint4d>(bitangents != NULL ? vertexCount : 0);
584  if(bitangents) std::copy(bitangents, bitangents + vertexCount, seam_bitangents.begin());
585  std::vector<VuoPoint4d> seam_textures = std::vector<VuoPoint4d>(textures != NULL ? vertexCount : 0);
586  if(textures) std::copy(textures, textures + vertexCount, seam_textures.begin());
587 
588  const VuoPoint4d left = VuoPoint4d_make(-1., 0., 0., 1.);
589  const VuoPoint4d forward = VuoPoint4d_make(0., 0., 1., 1.);
590  const VuoPoint4d up = VuoPoint4d_make(0., 1., 0., 1.);
591  const VuoPoint4d down = VuoPoint4d_make(0., -1., 0., 1.);
592 
593  // get mesh bounds and center point
594  VuoPoint4d min, max;
595  VuoMeshUtility_bounds(*submesh, &min, &max);
596  VuoPoint4d center = VuoPoint4d_multiply(VuoPoint4d_add(max, min), .5);
597 
598  // Insert vertical seam along western hemisphere
599  for(unsigned int i = 0; i < submesh->elementCount; i+=3)
600  {
601  if(indices[i] > vertexCount || indices[i+1] > vertexCount || indices[i+2] > vertexCount)
602  {
603  VUserLog("Triangle indices are referencing out of bounds vertices. (%u, %u, %u) Vertex Count: %u", indices[i], indices[i+1], indices[i+2], vertexCount);
604  continue;
605  }
606  // {0,1} {1,2}, {2,0}
607  for(int n = 0; n < 3; n++)
608  {
609  unsigned int x = i + n;
610  unsigned int y = i + (n == 2 ? 0 : n+1);
611 
612  // check if this triangle edge crosses the new seam
613  VuoPoint4d v0 = VuoPoint4d_normalize(VuoPoint4d_subtract(positions[indices[x]], center));
614  VuoPoint4d v1 = VuoPoint4d_normalize(VuoPoint4d_subtract(positions[indices[y]], center));
615 
616  if( VuoPoint4d_equals(v0, up) || VuoPoint4d_equals(v0, down) ||
617  VuoPoint4d_equals(v1, up) || VuoPoint4d_equals(v1, down) )
618  continue;
619 
620  // if v0 is in left back quadrant, and v1 is in the forward half, v0 needs to be duplicated. Ditto in opposite order
621  if( dot(v0, left) > 0 && dot(v0, forward) < 0 && dot(v1, forward) >= 0 )
622  {
623  if(positions)
624  seam_positions.push_back(positions[indices[x]]);
625  if(normals)
626  seam_normals.push_back(normals[indices[x]]);
627  if(tangents)
628  seam_tangents.push_back(tangents[indices[x]]);
629  if(bitangents)
630  seam_bitangents.push_back(bitangents[indices[x]]);
631  if(textures)
632  seam_textures.push_back(textures[indices[x]]);
633 
634  seam_indices[x] = seam_positions.size() - 1;
635  }
636 
637  // if v1 is in left back quadrant, but v0 isn't, duplicate v1
638  if( dot(v1, left) > 0 && dot(v1, forward) < 0 && dot(v0, forward) >= 0 )
639  {
640  if(positions)
641  seam_positions.push_back(positions[indices[y]]);
642  if(normals)
643  seam_normals.push_back(normals[indices[y]]);
644  if(tangents)
645  seam_tangents.push_back(tangents[indices[y]]);
646  if(bitangents)
647  seam_bitangents.push_back(bitangents[indices[y]]);
648  if(textures)
649  seam_textures.push_back(textures[indices[y]]);
650 
651  seam_indices[y] = seam_positions.size() - 1;
652  }
653  }
654  }
655 
656  unsigned int seam_vertexCount = seam_positions.size();
657 
658  if(positions != NULL)
659  {
660  free(submesh->positions);
661  submesh->positions = (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * seam_vertexCount);
662  std::copy(seam_positions.begin(), seam_positions.end(), submesh->positions);
663  }
664 
665  if(normals != NULL)
666  {
667  free(submesh->normals);
668  submesh->normals = (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * seam_vertexCount);
669  std::copy(seam_normals.begin(), seam_normals.end(), submesh->normals);
670  }
671 
672  if(tangents != NULL)
673  {
674  free(submesh->tangents);
675  submesh->tangents = (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * seam_vertexCount);
676  std::copy(seam_tangents.begin(), seam_tangents.end(), submesh->tangents);
677  }
678 
679  if(bitangents != NULL)
680  {
681  free(submesh->bitangents);
682  submesh->bitangents = (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * seam_vertexCount);
683  std::copy(seam_bitangents.begin(), seam_bitangents.end(), submesh->bitangents);
684  }
685 
686  if(textures != NULL)
687  {
688  free(submesh->textureCoordinates);
689  submesh->textureCoordinates = (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * seam_vertexCount);
690  std::copy(seam_textures.begin(), seam_textures.end(), submesh->textureCoordinates);
691  }
692 
693  submesh->vertexCount = seam_vertexCount;
694 
695  free(submesh->elements);
696  submesh->elements = seam_indices;
697 }
698 
702 static int compare(const void* lhs, const void* rhs)
703 {
704  return ( *(int*)lhs - *(int*)rhs );
705 }
706 
711 {
712  unsigned int elementCount = mesh->elementCount;
713  unsigned int vertexCount = mesh->vertexCount;
714 
715  unsigned int* sortedTriangles = (unsigned int*)malloc(sizeof(unsigned int) * elementCount);
716  memcpy(sortedTriangles, mesh->elements, sizeof(unsigned int) * elementCount);
717 
718  // sort indices from smallest to largest
719  qsort(sortedTriangles, elementCount, sizeof(unsigned int), compare);
720 
721  // lop off any elements that are referencing out of bounds vertices
722  unsigned int last = elementCount-1;
723 
724  while( sortedTriangles[last] > vertexCount )
725  last--;
726 
727  // if there are invalid indices, reallocate the the elements array with only the good'ns.
728  if(last < elementCount-1)
729  {
730  unsigned int* pruned_elements = (unsigned int*)malloc(sizeof(unsigned int) * (last+1));
731 
732  unsigned int n = 0;
733 
734  for(unsigned int i = 0; i < elementCount; i++)
735  {
736  if(mesh->elements[i] < vertexCount)
737  pruned_elements[n++] = mesh->elements[i];
738  }
739 
740  elementCount = last+1;
741  mesh->elementCount = elementCount;
742  free(mesh->elements);
743  mesh->elements = pruned_elements;
744  }
745 
746  // generate list of unused indices
747  std::vector<unsigned int> unused;
748 
749  int lastVal = -1;
750  for(int i = 0; i < elementCount && sortedTriangles[i] < elementCount; i++)
751  {
752  for(int n = lastVal+1; n < sortedTriangles[i]; n++)
753  {
754  unused.push_back(n);
755  }
756 
757  lastVal = sortedTriangles[i];
758  }
759  free(sortedTriangles);
760 
761  // rebuild index array by substracting the amount of vertices removed in the range lower
762  // than element[i]
763  int unusedCount = unused.size();
764  for(int i = 0; i < elementCount; i++)
765  {
766  unsigned int vertex = mesh->elements[i];
767 
768  if(vertex > vertexCount)
769  continue;
770 
771  int n = 0;
772 
773  for(auto& val : unused)
774  {
775  if(val < vertex)
776  n++;
777  else
778  break;
779  }
780 
781  mesh->elements[i] -= n;
782  }
783 
784  // rebuild vertex arrays without the unused values
785  // not using vertexCount-unusedCount because unused may contain duplicates
786  std::vector<VuoPoint4d> new_positions = std::vector<VuoPoint4d>(mesh->positions == NULL ? 0 : vertexCount);
787  std::vector<VuoPoint4d> new_normals = std::vector<VuoPoint4d>(mesh->normals == NULL ? 0 : vertexCount);
788  std::vector<VuoPoint4d> new_tangents = std::vector<VuoPoint4d>(mesh->tangents == NULL ? 0 : vertexCount);
789  std::vector<VuoPoint4d> new_bitangents = std::vector<VuoPoint4d>(mesh->bitangents == NULL ? 0 : vertexCount);
790  std::vector<VuoPoint4d> new_textureCoordinates = std::vector<VuoPoint4d>(mesh->textureCoordinates == NULL ? 0 : vertexCount);
791 
792  unsigned int curIndex = 0, n = 0;
793  for(int i = 0; i < vertexCount; i++)
794  {
795  if(curIndex >= unusedCount || i < unused[curIndex])
796  {
797  if(mesh->positions)
798  new_positions[n] = mesh->positions[i];
799  if(mesh->normals)
800  new_normals[n] = mesh->normals[i];
801  if(mesh->tangents)
802  new_tangents[n] = mesh->tangents[i];
803  if(mesh->bitangents)
804  new_bitangents[n] = mesh->bitangents[i];
805  if(mesh->textureCoordinates)
806  new_textureCoordinates[n] = mesh->textureCoordinates[i];
807 
808  n++;
809  }
810  else
811  {
812  do {
813  curIndex++;
814  } while(curIndex < unusedCount && unused[curIndex] <= i);
815  }
816  }
817 
818  // Re-apply points to mesh
819  unsigned int culledVertexCount = new_positions.size();
820 
821  if(mesh->positions)
822  {
823  free(mesh->positions);
824  mesh->positions = (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * culledVertexCount);
825  std::copy(new_positions.begin(), new_positions.end(), mesh->positions);
826  }
827 
828  if(mesh->normals)
829  {
830  free(mesh->normals);
831  mesh->normals = (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * culledVertexCount);
832  std::copy(new_normals.begin(), new_normals.end(), mesh->normals);
833  }
834 
835  if(mesh->tangents)
836  {
837  free(mesh->tangents);
838  mesh->tangents = (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * culledVertexCount);
839  std::copy(new_tangents.begin(), new_tangents.end(), mesh->tangents);
840  }
841 
842  if(mesh->bitangents)
843  {
844  free(mesh->bitangents);
845  mesh->bitangents = (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * culledVertexCount);
846  std::copy(new_bitangents.begin(), new_bitangents.end(), mesh->bitangents);
847  }
848 
849  if(mesh->textureCoordinates)
850  {
851  free(mesh->textureCoordinates);
852  mesh->textureCoordinates = (VuoPoint4d*)malloc(sizeof(VuoPoint4d) * culledVertexCount);
853  std::copy(new_textureCoordinates.begin(), new_textureCoordinates.end(), mesh->textureCoordinates);
854  }
855 
856  mesh->vertexCount = culledVertexCount;
857 }