Vuo  2.3.2
VuoMesh.c
Go to the documentation of this file.
1 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include "VuoMacOSSDKWorkaround.h"
15 #include <OpenGL/CGLMacro.h>
17 #define glGenVertexArrays glGenVertexArraysAPPLE
18 #define glBindVertexArray glBindVertexArrayAPPLE
19 #define glDeleteVertexArrays glDeleteVertexArraysAPPLE
21 
22 #include "type.h"
23 
25 #ifdef VUO_COMPILER
27  "title" : "Mesh",
28  "description" : "A 3D shape represented by a set of vertices with associated normals and other per-vertex data.",
29  "keywords" : [ "mesh", "vertex" ],
30  "version" : "1.0.0",
31  "dependencies" : [
32  "VuoColor",
33  "VuoPoint2d",
34  "VuoPoint3d",
35  "VuoPoint4d",
36  "VuoList_VuoColor",
37  "VuoList_VuoPoint2d",
38  "VuoList_VuoPoint3d",
39  "VuoText",
40  "VuoTransform",
41  "VuoGlContext",
42  "VuoGlPool"
43  ]
44  });
45 #endif
47 
51 typedef struct
52 {
53  unsigned int vertexCount;
54  float *positions;
55  float *normals;
56  float *textureCoordinates;
57  float *colors;
58 
59  unsigned int elementCount;
63  unsigned int *elements;
64 
66  VuoMesh_ElementAssemblyMethod elementAssemblyMethod;
67 
72  VuoReal primitiveSize;
73 
74  VuoMesh_FaceCulling faceCulling;
75 
79  struct
80  {
81  unsigned int combinedBuffer;
82  unsigned int combinedBufferSize;
83  unsigned int combinedBufferStride;
84 
85  void *normalOffset;
86  void *textureCoordinateOffset;
87  void *colorOffset;
88 
89  unsigned int elementBuffer;
90  unsigned int elementBufferSize;
91  } glUpload;
92 } VuoMesh_internal;
93 
105 void VuoMesh_allocateCPUBuffers(unsigned int vertexCount,
106  float **positions, float **normals, float **textureCoordinates, float **colors,
107  unsigned int elementCount, unsigned int **elements)
108 {
109  if (positions)
110  *positions = (float *)malloc(sizeof(float) * 3 * vertexCount);
111  if (normals)
112  *normals = (float *)malloc(sizeof(float) * 3 * vertexCount);
113  if (textureCoordinates)
114  *textureCoordinates = (float *)malloc(sizeof(float) * 2 * vertexCount);
115  if (colors)
116  *colors = (float *)malloc(sizeof(float) * 4 * vertexCount);
117  if (elements)
118  *elements = elementCount ? (unsigned int *)malloc(sizeof(unsigned int) * elementCount) : NULL;
119 }
120 
124 static void VuoMesh_free(void *value)
125 {
126  VuoMesh_internal *m = (VuoMesh_internal *)value;
127 
128  free(m->positions);
129  free(m->normals);
130  free(m->textureCoordinates);
131  free(m->colors);
132  free(m->elements);
133 
134  VuoGlPool_release(VuoGlPool_ArrayBuffer, m->glUpload.combinedBufferSize, m->glUpload.combinedBuffer);
135  VuoGlPool_release(VuoGlPool_ElementArrayBuffer, m->glUpload.elementBufferSize, m->glUpload.elementBuffer);
136 
137  free(m);
138 }
139 
143 static VuoMesh_internal *VuoMesh_makeInternal(void)
144 {
145  VuoMesh_internal *m = (VuoMesh_internal *)calloc(1, sizeof(VuoMesh_internal));
147  m->faceCulling = VuoMesh_CullBackfaces;
148  return m;
149 }
150 
154 static VuoMesh_internal *VuoMesh_makeSingletonInternal(void)
155 {
156  VuoMesh_internal *m = (VuoMesh_internal *)calloc(1, sizeof(VuoMesh_internal));
158  m->faceCulling = VuoMesh_CullBackfaces;
159  return m;
160 }
161 
162 static void VuoMesh_upload(VuoMesh_internal *m);
163 
176 VuoMesh VuoMesh_makeFromCPUBuffers(unsigned int vertexCount,
177  float *positions, float *normals, float *textureCoordinates, float *colors,
178  unsigned int elementCount, unsigned int *elements, VuoMesh_ElementAssemblyMethod elementAssemblyMethod)
179 {
180  VuoMesh_internal *m = VuoMesh_makeInternal();
181 
182  m->vertexCount = vertexCount;
183  m->positions = positions;
184  m->normals = normals;
185  m->textureCoordinates = textureCoordinates;
186  m->colors = colors;
187  m->elementCount = elementCount;
188  m->elements = elements;
189  m->elementAssemblyMethod = elementAssemblyMethod;
190 
191  VuoMesh_upload(m);
192  return (VuoMesh)m;
193 }
194 
198 VuoMesh VuoMesh_makeFromGPUBuffers(unsigned int vertexCount, unsigned int combinedBuffer, unsigned int combinedBufferSize, void *normalOffset, void *textureCoordinateOffset, void *colorOffset, unsigned int elementCount, unsigned int elementBuffer, unsigned int elementBufferSize, VuoMesh_ElementAssemblyMethod elementAssemblyMethod)
199 {
200  VuoMesh_internal *m = VuoMesh_makeInternal();
201 
202  m->vertexCount = vertexCount;
203  m->elementCount = elementCount;
204  m->elementAssemblyMethod = elementAssemblyMethod;
205  m->glUpload.combinedBuffer = combinedBuffer;
206  m->glUpload.combinedBufferSize = combinedBufferSize;
207  m->glUpload.normalOffset = normalOffset;
208  m->glUpload.textureCoordinateOffset = textureCoordinateOffset;
209  m->glUpload.colorOffset = colorOffset;
210  m->glUpload.elementBuffer = elementBuffer;
211  m->glUpload.elementBufferSize = elementBufferSize;
212 
213  return (VuoMesh)m;
214 }
215 
219 unsigned long VuoMesh_getGlMode(VuoMesh mesh)
220 {
221  if (!mesh)
222  return GL_NONE;
223 
224  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
225 
226  if (m->elementAssemblyMethod == VuoMesh_IndividualTriangles)
227  return GL_TRIANGLES;
228  else if (m->elementAssemblyMethod == VuoMesh_TriangleStrip)
229  return GL_TRIANGLE_STRIP;
230  else if (m->elementAssemblyMethod == VuoMesh_TriangleFan)
231  return GL_TRIANGLE_FAN;
232  else if (m->elementAssemblyMethod == VuoMesh_IndividualLines)
233  return GL_LINES;
234  else if (m->elementAssemblyMethod == VuoMesh_LineStrip)
235  return GL_LINE_STRIP;
236  else if (m->elementAssemblyMethod == VuoMesh_Points)
237  return GL_POINTS;
238 
239  return GL_TRIANGLES;
240 }
241 
251 {
252  if (!mesh)
253  return 0;
254 
255  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
256 
257  if (m->elementAssemblyMethod == VuoMesh_IndividualTriangles)
258  return m->elementCount ? m->elementCount/3 : m->vertexCount/3;
259  else if (m->elementAssemblyMethod == VuoMesh_TriangleStrip)
260  return m->elementCount ? m->elementCount-2 : m->vertexCount-2;
261  else if (m->elementAssemblyMethod == VuoMesh_TriangleFan)
262  return m->elementCount ? m->elementCount-2 : m->vertexCount-2;
263  else if (m->elementAssemblyMethod == VuoMesh_IndividualLines)
264  return m->elementCount ? m->elementCount/2 : m->vertexCount/2;
265  else if (m->elementAssemblyMethod == VuoMesh_LineStrip)
266  return m->elementCount ? m->elementCount-1 : m->vertexCount-1;
267  else if (m->elementAssemblyMethod == VuoMesh_Points)
268  return m->elementCount ? m->elementCount : m->vertexCount;
269 
270  return 0;
271 }
272 
282 {
283  if (!mesh)
284  return 0;
285 
286  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
287 
288  if (m->elementCount == 0 && m->vertexCount == 0)
289  return 0;
290 
291  if (m->elementAssemblyMethod == VuoMesh_IndividualTriangles)
292  return m->elementCount ? m->elementCount : m->vertexCount;
293  else if (m->elementAssemblyMethod == VuoMesh_TriangleStrip)
294  return m->elementCount ? (m->elementCount-2)*3 : (m->vertexCount-2)*3;
295  else if (m->elementAssemblyMethod == VuoMesh_TriangleFan)
296  return m->elementCount ? (m->elementCount-2)*3 : (m->vertexCount-2)*3;
297  else if (m->elementAssemblyMethod == VuoMesh_IndividualLines)
298  return m->elementCount ? m->elementCount : m->vertexCount;
299  else if (m->elementAssemblyMethod == VuoMesh_LineStrip)
300  return m->elementCount ? (m->elementCount-1)*2 : (m->vertexCount-1)*2;
301  else if (m->elementAssemblyMethod == VuoMesh_Points)
302  return m->elementCount ? m->elementCount : m->vertexCount;
303 
304  return 0;
305 }
306 
307 static unsigned long VuoMesh_getCompleteElementCountInternal(unsigned long elementCount, VuoMesh_ElementAssemblyMethod elementAssemblyMethod);
308 
318 unsigned long VuoMesh_getCompleteElementCount(const VuoMesh mesh)
319 {
320  if (!mesh)
321  return 0;
322 
323  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
324 
325  unsigned long elementCount = m->elementCount ? m->elementCount : m->vertexCount;
326  return VuoMesh_getCompleteElementCountInternal(elementCount, m->elementAssemblyMethod);
327 }
328 
332 static void VuoMesh_upload(VuoMesh_internal *m)
333 {
334  if (!m || m->glUpload.combinedBuffer)
335  return;
336 
337  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
338 
339  // Create a temporary Vertex Array Object, so we can bind the buffers in order to upload them.
340  GLuint vertexArray;
341  glGenVertexArrays(1, &vertexArray);
342  glBindVertexArray(vertexArray);
343 
344  // Combine the vertex attribute buffers together, so we only have to upload a single one per mesh:
345 
346  m->glUpload.combinedBufferSize = sizeof(float) * 3 * m->vertexCount; // positions
347 
348  uint64_t normalOffsetBytes = 0;
349  if (m->normals)
350  {
351  normalOffsetBytes = m->glUpload.combinedBufferSize;
352  m->glUpload.combinedBufferSize += sizeof(float) * 3 * m->vertexCount;
353  }
354  m->glUpload.normalOffset = (void *)normalOffsetBytes;
355 
356  uint64_t textureCoordinateOffsetBytes = 0;
357  if (m->textureCoordinates)
358  {
359  textureCoordinateOffsetBytes = m->glUpload.combinedBufferSize;
360  m->glUpload.combinedBufferSize += sizeof(float) * 2 * m->vertexCount;
361  }
362  m->glUpload.textureCoordinateOffset = (void *)textureCoordinateOffsetBytes;
363 
364  uint64_t colorOffsetBytes = 0;
365  if (m->colors)
366  {
367  colorOffsetBytes = m->glUpload.combinedBufferSize;
368  m->glUpload.combinedBufferSize += sizeof(float) * 4 * m->vertexCount;
369  }
370  m->glUpload.colorOffset = (void *)colorOffsetBytes;
371 
372  float *combinedData = (float *)malloc(m->glUpload.combinedBufferSize);
373  for (int i =0; i < m->glUpload.combinedBufferSize/sizeof(float); ++i)
374  combinedData[i]=-42;
375 
376 
377  // Combine vertex attributes into the interleaved client-side buffer.
378  for (unsigned long i = 0; i < m->vertexCount; ++i)
379  {
380  combinedData[i * 3 ] = m->positions[i * 3 ];
381  combinedData[i * 3 + 1] = m->positions[i * 3 + 1];
382  combinedData[i * 3 + 2] = m->positions[i * 3 + 2];
383  if (m->normals)
384  {
385  combinedData[i * 3 + normalOffsetBytes / sizeof(float) ] = m->normals[i * 3 ];
386  combinedData[i * 3 + normalOffsetBytes / sizeof(float) + 1] = m->normals[i * 3 + 1];
387  combinedData[i * 3 + normalOffsetBytes / sizeof(float) + 2] = m->normals[i * 3 + 2];
388  }
389  if (m->textureCoordinates)
390  {
391  combinedData[i * 2 + textureCoordinateOffsetBytes / sizeof(float) ] = m->textureCoordinates[i * 2 ];
392  combinedData[i * 2 + textureCoordinateOffsetBytes / sizeof(float) + 1] = m->textureCoordinates[i * 2 + 1];
393  }
394  if (m->colors)
395  {
396  combinedData[i * 4 + colorOffsetBytes / sizeof(float) ] = m->colors[i * 4 ];
397  combinedData[i * 4 + colorOffsetBytes / sizeof(float) + 1] = m->colors[i * 4 + 1];
398  combinedData[i * 4 + colorOffsetBytes / sizeof(float) + 2] = m->colors[i * 4 + 2];
399  combinedData[i * 4 + colorOffsetBytes / sizeof(float) + 3] = m->colors[i * 4 + 3];
400  }
401  }
402 
403 // for (int i =0; i < m->glUpload.combinedBufferSize/sizeof(float); ++i)
404 // VLog("%3d %f",i,combinedData[i]);
405 
406 
407  // Upload the combined buffer.
408 // VLog("vertexCount=%d normals=%d tc=%d colors=%d combinedBuffer=%u", m->vertexCount, m->normals?1:0, m->textureCoordinates?1:0, m->colors?1:0, m->glUpload.combinedBufferSize);
409  m->glUpload.combinedBuffer = VuoGlPool_use(cgl_ctx, VuoGlPool_ArrayBuffer, m->glUpload.combinedBufferSize);
410  VuoGlPool_retain(m->glUpload.combinedBuffer);
411  glBindBuffer(GL_ARRAY_BUFFER, m->glUpload.combinedBuffer);
412  glBufferSubData(GL_ARRAY_BUFFER, 0, m->glUpload.combinedBufferSize, combinedData);
413  free(combinedData);
414 
415 
416  // Upload the Element Buffer and add it to the Vertex Array Object
417  m->glUpload.elementBufferSize = sizeof(unsigned int)*m->elementCount;
418  m->glUpload.elementBuffer = VuoGlPool_use(cgl_ctx, VuoGlPool_ElementArrayBuffer, m->glUpload.elementBufferSize);
419  VuoGlPool_retain(m->glUpload.elementBuffer);
420  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->glUpload.elementBuffer);
421  glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(unsigned int) * m->elementCount, m->elements);
422 
423  glBindBuffer(GL_ARRAY_BUFFER, 0);
424 // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // handled by glDeleteVertexArrays()
425 // glBindVertexArray(0); // handled by glDeleteVertexArrays()
426  glDeleteVertexArrays(1, &vertexArray);
427 
428  // Prepare for this mesh to be used on a different OpenGL context.
429  // @@@ is this still needed?
430  glFlushRenderAPPLE();
431 
432  });
433 }
434 
439 {
440  VuoMesh_internal *m = VuoMesh_makeSingletonInternal();
441  m->vertexCount = 4;
442  m->elementCount = 6;
443  VuoMesh_allocateCPUBuffers(m->vertexCount, &m->positions, &m->normals, &m->textureCoordinates, NULL, m->elementCount, &m->elements);
444 
445  // Positions
446  VuoPoint3d_setArray(&m->positions[0 * 3], (VuoPoint3d){-.5, -.5, 0});
447  VuoPoint3d_setArray(&m->positions[1 * 3], (VuoPoint3d){ .5, -.5, 0});
448  VuoPoint3d_setArray(&m->positions[2 * 3], (VuoPoint3d){-.5, .5, 0});
449  VuoPoint3d_setArray(&m->positions[3 * 3], (VuoPoint3d){ .5, .5, 0});
450 
451  // Normals
452  for (int i = 0; i < m->vertexCount; ++i)
453  VuoPoint3d_setArray(&m->normals[i * 3], (VuoPoint3d){0, 0, 1});
454 
455  // Texture Coordinates
456  VuoPoint2d_setArray(&m->textureCoordinates[0 * 2], (VuoPoint2d){0, 0});
457  VuoPoint2d_setArray(&m->textureCoordinates[1 * 2], (VuoPoint2d){1, 0});
458  VuoPoint2d_setArray(&m->textureCoordinates[2 * 2], (VuoPoint2d){0, 1});
459  VuoPoint2d_setArray(&m->textureCoordinates[3 * 2], (VuoPoint2d){1, 1});
460 
461  // Triangle elements
462  m->elementAssemblyMethod = VuoMesh_IndividualTriangles;
463  // Order the elements so that the diagonal edge of each triangle
464  // is last, so that vuo.shader.make.wireframe can optionally omit them.
465  m->elements[0] = 2;
466  m->elements[1] = 0;
467  m->elements[2] = 1;
468  m->elements[3] = 1;
469  m->elements[4] = 3;
470  m->elements[5] = 2;
471 
472  VuoMesh_upload(m);
473  return (VuoMesh)m;
474 }
475 
482 {
483  VuoMesh_internal *m = VuoMesh_makeSingletonInternal();
484  m->vertexCount = 4;
485  m->elementCount = 6;
486  VuoMesh_allocateCPUBuffers(m->vertexCount, &m->positions, NULL, &m->textureCoordinates, NULL, m->elementCount, &m->elements);
487 
488  // Positions
489  m->positions[0] = -.5;
490  m->positions[1] = -.5;
491  m->positions[2] = 0;
492 
493  m->positions[3] = .5;
494  m->positions[4] = -.5;
495  m->positions[5] = 0;
496 
497  m->positions[6] = -.5;
498  m->positions[7] = .5;
499  m->positions[8] = 0;
500 
501  m->positions[9] = .5;
502  m->positions[10] = .5;
503  m->positions[11] = 0;
504 
505  // Texture Coordinates
506  m->textureCoordinates[0] = 0;
507  m->textureCoordinates[1] = 0;
508 
509  m->textureCoordinates[2] = 1;
510  m->textureCoordinates[3] = 0;
511 
512  m->textureCoordinates[4] = 0;
513  m->textureCoordinates[5] = 1;
514 
515  m->textureCoordinates[6] = 1;
516  m->textureCoordinates[7] = 1;
517 
518  // Triangle elements
519  m->elementAssemblyMethod = VuoMesh_IndividualTriangles;
520  // Order the elements so that the diagonal edge of each triangle
521  // is last, so that vuo.shader.make.wireframe can optionally omit them.
522  m->elements[0] = 2;
523  m->elements[1] = 0;
524  m->elements[2] = 1;
525  m->elements[3] = 1;
526  m->elements[4] = 3;
527  m->elements[5] = 2;
528 
529  VuoMesh_upload(m);
530  return (VuoMesh)m;
531 }
532 
537 {
538  VuoMesh_internal *m = VuoMesh_makeSingletonInternal();
539  m->vertexCount = 3;
540  m->elementCount = 3;
541  VuoMesh_allocateCPUBuffers(m->vertexCount, &m->positions, &m->normals, &m->textureCoordinates, NULL, m->elementCount, &m->elements);
542 
543  // Positions
544  for (int i = 0; i < m->vertexCount; ++i)
545  {
546  float angle = M_PI/2. + i * 2*M_PI/3.;
547  VuoPoint3d_setArray(&m->positions[i * 3], (VuoPoint3d){ cos(angle) / sqrt(3), sin(angle) / sqrt(3), 0 });
548  }
549 
550  // Normals
551  for (int i = 0; i < m->vertexCount; ++i)
552  VuoPoint3d_setArray(&m->normals[i * 3], (VuoPoint3d){ 0, 0, 1 });
553 
554  // Texture Coordinates
555  VuoPoint2d_setArray(&m->textureCoordinates[0 * 2], (VuoPoint2d){.5, 1});
556  VuoPoint2d_setArray(&m->textureCoordinates[1 * 2], (VuoPoint2d){ 0, 0});
557  VuoPoint2d_setArray(&m->textureCoordinates[2 * 2], (VuoPoint2d){ 1, 0});
558 
559  // Triangle Strip elements
560  m->elementAssemblyMethod = VuoMesh_TriangleStrip;
561  m->elements[0] = 0;
562  m->elements[1] = 1;
563  m->elements[2] = 2;
564 
565  VuoMesh_upload(m);
566  return (VuoMesh)m;
567 }
568 
575 {
576  static VuoMesh sharedQuadWithNormals;
577  static dispatch_once_t token = 0;
578  dispatch_once(&token, ^{
579  sharedQuadWithNormals = VuoMesh_makeQuadWithNormalsInternal();
580  VuoRetain(sharedQuadWithNormals);
581  });
582  return sharedQuadWithNormals;
583 }
584 
593 {
594  static VuoMesh sharedQuadWithoutNormals;
595  static dispatch_once_t token = 0;
596  dispatch_once(&token, ^{
597  sharedQuadWithoutNormals = VuoMesh_makeQuadWithoutNormalsInternal();
598  VuoRetain(sharedQuadWithoutNormals);
599  });
600  return sharedQuadWithoutNormals;
601 }
602 
609 {
610  static VuoMesh sharedEquilateralTriangle;
611  static dispatch_once_t token = 0;
612  dispatch_once(&token, ^{
613  sharedEquilateralTriangle = VuoMesh_makeEquilateralTriangleInternal();
614  VuoRetain(sharedEquilateralTriangle);
615  });
616  return sharedEquilateralTriangle;
617 }
618 
623 {
624  // Separate vertices for each face, so each face can have its own sharp normals and texture coordinates.
625 
626  VuoPoint3d positions[] = (VuoPoint3d[]){
627  // Front
628  {-.5, -.5, .5},
629  { .5, -.5, .5},
630  {-.5, .5, .5},
631  { .5, .5, .5},
632  // Right
633  { .5, -.5, .5},
634  { .5, -.5, -.5},
635  { .5, .5, .5},
636  { .5, .5, -.5},
637  // Bottom
638  {-.5, -.5, -.5},
639  { .5, -.5, -.5},
640  {-.5, -.5, .5},
641  { .5, -.5, .5},
642  // Left
643  {-.5, -.5, -.5},
644  {-.5, -.5, .5},
645  {-.5, .5, -.5},
646  {-.5, .5, .5},
647  // Top
648  {-.5, .5, .5},
649  { .5, .5, .5},
650  {-.5, .5, -.5},
651  { .5, .5, -.5},
652  // Back
653  { .5, -.5, -.5},
654  {-.5, -.5, -.5},
655  { .5, .5, -.5},
656  {-.5, .5, -.5},
657  };
658 
659  VuoPoint3d normals[] = (VuoPoint3d[]){
660  // Front
661  {0, 0, 1},
662  {0, 0, 1},
663  {0, 0, 1},
664  {0, 0, 1},
665  // Right
666  {1, 0, 0},
667  {1, 0, 0},
668  {1, 0, 0},
669  {1, 0, 0},
670  // Bottom
671  {0, -1, 0},
672  {0, -1, 0},
673  {0, -1, 0},
674  {0, -1, 0},
675  // Left
676  {-1, 0, 0},
677  {-1, 0, 0},
678  {-1, 0, 0},
679  {-1, 0, 0},
680  // Top
681  {0, 1, 0},
682  {0, 1, 0},
683  {0, 1, 0},
684  {0, 1, 0},
685  // Back
686  {0, 0, -1},
687  {0, 0, -1},
688  {0, 0, -1},
689  {0, 0, -1},
690  };
691 
692  VuoPoint2d textureCoordinates[] = (VuoPoint2d[]){
693  // Front
694  {0, 0},
695  {1, 0},
696  {0, 1},
697  {1, 1},
698  // Right
699  {0, 0},
700  {1, 0},
701  {0, 1},
702  {1, 1},
703  // Bottom
704  {0, 0},
705  {1, 0},
706  {0, 1},
707  {1, 1},
708  // Left
709  {0, 0},
710  {1, 0},
711  {0, 1},
712  {1, 1},
713  // Top
714  {0, 0},
715  {1, 0},
716  {0, 1},
717  {1, 1},
718  // Back
719  {0, 0},
720  {1, 0},
721  {0, 1},
722  {1, 1},
723  };
724 
725  unsigned int elements[] = (unsigned int[]){
726  // Front
727  2, 0, 1,
728  1, 3, 2,
729  // Right
730  6, 4, 5,
731  5, 7, 6,
732  // Bottom
733  10, 8, 9,
734  9, 11, 10,
735  // Left
736  14, 12, 13,
737  13, 15, 14,
738  // Top
739  18, 16, 17,
740  17, 19, 18,
741  // Back
742  22, 20, 21,
743  21, 23, 22,
744  };
745 
746  VuoMesh_internal *m = VuoMesh_makeSingletonInternal();
747  m->vertexCount = 6 * 4;
748  m->elementCount = 6 * 6;
749  VuoMesh_allocateCPUBuffers(m->vertexCount, &m->positions, &m->normals, &m->textureCoordinates, NULL, m->elementCount, &m->elements);
750 
751  for (int i = 0; i < m->vertexCount; ++i)
752  {
753  VuoPoint3d_setArray(&m->positions[i * 3], positions[i]);
754  VuoPoint3d_setArray(&m->normals[i * 3], normals[i]);
755  VuoPoint2d_setArray(&m->textureCoordinates[i * 2], textureCoordinates[i]);
756  }
757  memcpy(m->elements, elements, sizeof(elements));
758 
759  VuoMesh_upload(m);
760  return (VuoMesh)m;
761 }
762 
769 {
770  static VuoMesh sharedCube;
771  static dispatch_once_t token = 0;
772  dispatch_once(&token, ^{
773  sharedCube = VuoMesh_makeCubeInternal();
774  VuoRetain(sharedCube);
775  });
776  return sharedCube;
777 }
778 
785 {
786  unsigned int vertexCount = rows * columns;
787  unsigned int triangleCount = (rows-1) * (columns-1) * 6;
788 
789  float *positions, *normals, *textureCoordinates;
790  unsigned int *elements;
791  VuoMesh_allocateCPUBuffers(vertexCount, &positions, &normals, &textureCoordinates, NULL, triangleCount, &elements);
792 
793  unsigned int index = 0, t_index = 0;
794 
795  for(unsigned int i = 0; i < rows; i++)
796  {
797  float y = (i/(float)(rows-1)) - .5;
798 
799  for(unsigned int n = 0; n < columns; n++)
800  {
801  float x = (n/(float)(columns-1)) - .5;
802 
803  VuoPoint3d_setArray(&positions [index * 3], (VuoPoint3d){x, y, 0});
804  VuoPoint3d_setArray(&normals [index * 3], (VuoPoint3d){0, 0, 1});
805  VuoPoint2d_setArray(&textureCoordinates[index * 2], (VuoPoint2d){x + .5, y + .5});
806 
807  if(n < columns-1 && i < rows-1)
808  {
809  elements[t_index++] = index + columns;
810  elements[t_index++] = index;
811  elements[t_index++] = index + 1;
812 
813  elements[t_index++] = index + 1;
814  elements[t_index++] = index + columns + 1;
815  elements[t_index++] = index + columns;
816  }
817 
818  index++;
819  }
820  }
821 
822  return VuoMesh_makeFromCPUBuffers(vertexCount,
823  positions, normals, textureCoordinates, NULL,
824  triangleCount, elements, VuoMesh_IndividualTriangles);
825 }
826 
830 static unsigned long VuoMesh_getCompleteElementCountInternal(unsigned long elementCount, VuoMesh_ElementAssemblyMethod elementAssemblyMethod)
831 {
832  if (elementAssemblyMethod == VuoMesh_IndividualTriangles)
833  // Round down to a multiple of 3, since each triangle requires 3 vertices.
834  return (elementCount / 3) * 3;
835 
836  else if (elementAssemblyMethod == VuoMesh_TriangleStrip
837  || elementAssemblyMethod == VuoMesh_TriangleFan)
838  // Triangle strips and fans must have at least 1 complete triangle.
839  return elementCount < 3 ? 0 : elementCount;
840 
841  else if (elementAssemblyMethod == VuoMesh_IndividualLines)
842  // Round down to an even number of vertices, since each line requires a pair of vertices.
843  return (elementCount / 2) * 2;
844 
845  else if (elementAssemblyMethod == VuoMesh_LineStrip)
846  // Line strips must have at least 1 complete line.
847  return elementCount < 2 ? 0 : elementCount;
848 
849  else if (elementAssemblyMethod == VuoMesh_Points)
850  // Since all points are independent, any number of points is fine.
851  return elementCount;
852 
853  else
854  {
855  VUserLog("Error: Unknown submesh element assembly method: %d", elementAssemblyMethod);
856  return 0;
857  }
858 }
859 
865 {
866  unsigned long positionCount = VuoListGetCount_VuoPoint2d(positions);
867  VuoPoint2d *positionValues = VuoListGetData_VuoPoint2d(positions);
868 
869  positionCount = VuoMesh_getCompleteElementCountInternal(positionCount, elementAssemblyMethod);
870  if (!positionCount)
871  return NULL;
872 
873  unsigned long colorCount = VuoListGetCount_VuoColor(colors);
874  VuoColor *colorValues = VuoListGetData_VuoColor(colors);
875 
876  float *positionsFloat, *normals, *colorsFloat = NULL;
877  VuoMesh_allocateCPUBuffers(positionCount, &positionsFloat, &normals, NULL, colorCount ? &colorsFloat : NULL, 0, NULL);
878  for (unsigned long i = 0; i < positionCount; ++i)
879  {
880  VuoPoint2d xy = positionValues[i];
881  positionsFloat[i * 3 ] = xy.x;
882  positionsFloat[i * 3 + 1] = xy.y;
883  positionsFloat[i * 3 + 2] = 0;
884 
885  normals[i * 3 ] = 0;
886  normals[i * 3 + 1] = 0;
887  normals[i * 3 + 2] = 1;
888 
889  if (colorCount)
890  {
891  float progress = (float)i / MAX(1, positionCount - 1);
892  unsigned long colorIndex = round(progress * (colorCount - 1));
893  VuoColor c = colorValues[colorIndex];
894  colorsFloat[i * 4 ] = c.r * c.a;
895  colorsFloat[i * 4 + 1] = c.g * c.a;
896  colorsFloat[i * 4 + 2] = c.b * c.a;
897  colorsFloat[i * 4 + 3] = c.a;
898  }
899  }
900 
901  VuoMesh mesh = VuoMesh_makeFromCPUBuffers(positionCount,
902  positionsFloat, normals, NULL, colorsFloat,
903  0, NULL, elementAssemblyMethod);
904  VuoMesh_setPrimitiveSize(mesh, primitiveSize);
905  return mesh;
906 }
907 
913 {
914  unsigned long positionCount = VuoListGetCount_VuoPoint3d(positions);
915  VuoPoint3d *positionValues = VuoListGetData_VuoPoint3d(positions);
916 
917  positionCount = VuoMesh_getCompleteElementCountInternal(positionCount, elementAssemblyMethod);
918  if (!positionCount)
919  return NULL;
920 
921  unsigned long colorCount = VuoListGetCount_VuoColor(colors);
922  VuoColor *colorValues = VuoListGetData_VuoColor(colors);
923 
924  float *positionsFloat, *normals, *colorsFloat = NULL;
925  VuoMesh_allocateCPUBuffers(positionCount, &positionsFloat, &normals, NULL, colorCount ? &colorsFloat : NULL, 0, NULL);
926  for (unsigned long i = 0; i < positionCount; ++i)
927  {
928  VuoPoint3d_setArray(&positionsFloat[i * 3], positionValues[i]);
929 
930  normals[i * 3 ] = 0;
931  normals[i * 3 + 1] = 0;
932  normals[i * 3 + 2] = 1;
933 
934  if (colorCount)
935  {
936  float progress = (float)i / MAX(1, positionCount - 1);
937  unsigned long colorIndex = round(progress * (colorCount - 1));
938  VuoColor c = colorValues[colorIndex];
939  colorsFloat[i * 4 ] = c.r * c.a;
940  colorsFloat[i * 4 + 1] = c.g * c.a;
941  colorsFloat[i * 4 + 2] = c.b * c.a;
942  colorsFloat[i * 4 + 3] = c.a;
943  }
944  }
945 
946  VuoMesh mesh = VuoMesh_makeFromCPUBuffers(positionCount,
947  positionsFloat, normals, NULL, colorsFloat,
948  0, NULL, elementAssemblyMethod);
949  VuoMesh_setPrimitiveSize(mesh, primitiveSize);
950  return mesh;
951 }
952 
957 {
958  if (!mesh)
959  return NULL;
960 
961  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
962  VuoMesh_internal *copiedMesh = VuoMesh_makeInternal();
963  copiedMesh->vertexCount = m->vertexCount;
964  copiedMesh->elementCount = m->elementCount;
965 
966  if (m->positions)
967  {
968  unsigned long size = sizeof(float) * 3 * copiedMesh->vertexCount;
969  copiedMesh->positions = (float *)malloc(size);
970  memcpy(copiedMesh->positions, m->positions, size);
971  }
972  else
973  copiedMesh->positions = NULL;
974 
975  if (m->normals)
976  {
977  unsigned long size = sizeof(float) * 3 * copiedMesh->vertexCount;
978  copiedMesh->normals = (float *)malloc(size);
979  memcpy(copiedMesh->normals, m->normals, size);
980  }
981  else
982  copiedMesh->normals = NULL;
983 
984  if (m->textureCoordinates)
985  {
986  unsigned long size = sizeof(float) * 3 * copiedMesh->vertexCount;
987  copiedMesh->textureCoordinates = (float *)malloc(size);
988  memcpy(copiedMesh->textureCoordinates, m->textureCoordinates, size);
989  }
990  else
991  copiedMesh->textureCoordinates = NULL;
992 
993  if (m->colors)
994  {
995  unsigned long size = sizeof(float) * 3 * copiedMesh->vertexCount;
996  copiedMesh->colors = (float *)malloc(size);
997  memcpy(copiedMesh->colors, m->colors, size);
998  }
999  else
1000  copiedMesh->colors = NULL;
1001 
1002  copiedMesh->elementCount = m->elementCount;
1003  if (m->elements)
1004  {
1005  unsigned long elementByteCount = sizeof(unsigned int)*m->elementCount;
1006  copiedMesh->elements = (unsigned int *)malloc(elementByteCount);
1007  memcpy(copiedMesh->elements, m->elements, elementByteCount);
1008  }
1009  else
1010  copiedMesh->elements = NULL;
1011 
1012  copiedMesh->elementAssemblyMethod = m->elementAssemblyMethod;
1013  copiedMesh->primitiveSize = m->primitiveSize;
1014  copiedMesh->faceCulling = m->faceCulling;
1015 
1016  memcpy(&copiedMesh->glUpload, &m->glUpload, sizeof(copiedMesh->glUpload));
1017  VuoGlPool_retain(copiedMesh->glUpload.combinedBuffer);
1018  VuoGlPool_retain(copiedMesh->glUpload.elementBuffer);
1019 
1020  return (VuoMesh)copiedMesh;
1021 }
1022 
1034 {
1035  if (!mesh)
1036  return NULL;
1037 
1038  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1039  VuoMesh_internal *copiedMesh = VuoMesh_makeInternal();
1040 
1041  copiedMesh->vertexCount = m->vertexCount;
1042  copiedMesh->elementCount = m->elementCount;
1043 
1044  copiedMesh->elementAssemblyMethod = m->elementAssemblyMethod;
1045  copiedMesh->primitiveSize = m->primitiveSize;
1046  copiedMesh->faceCulling = m->faceCulling;
1047 
1048  memcpy(&copiedMesh->glUpload, &m->glUpload, sizeof(copiedMesh->glUpload));
1049  VuoGlPool_retain(copiedMesh->glUpload.combinedBuffer);
1050  VuoGlPool_retain(copiedMesh->glUpload.elementBuffer);
1051 
1052  return (VuoMesh)copiedMesh;
1053 }
1054 
1061 {
1062  if (!mesh)
1063  return;
1064 
1065  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1066  if (m->textureCoordinates)
1067  {
1068  free(m->textureCoordinates);
1069  m->textureCoordinates = NULL;
1070  }
1071 
1072  if (m->glUpload.combinedBuffer && m->glUpload.textureCoordinateOffset)
1073  {
1074  m->glUpload.textureCoordinateOffset = NULL;
1075  // The data is still allocated in GPU RAM as part of combinedBuffer;
1076  // just ignore that (rather than wasting time repacking the buffer).
1077  }
1078 }
1079 
1080 static void VuoMesh_download(VuoMesh_internal *m);
1081 
1091 void VuoMesh_getCPUBuffers(const VuoMesh mesh, unsigned int *vertexCount,
1092  float **positions, float **normals, float **textureCoordinates, float **colors,
1093  unsigned int *elementCount, unsigned int **elements)
1094 {
1095  if (!mesh)
1096  return;
1097 
1098  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1099  VuoMesh_download(m);
1100  if (vertexCount)
1101  *vertexCount = m->vertexCount;
1102  if (positions)
1103  *positions = m->positions;
1104  if (normals)
1105  *normals = m->normals;
1106  if (textureCoordinates)
1107  *textureCoordinates = m->textureCoordinates;
1108  if (colors)
1109  *colors = m->colors;
1110  if (elementCount)
1111  *elementCount = m->elementCount;
1112  if (elements)
1113  *elements = m->elements;
1114 }
1115 
1123 void VuoMesh_getGPUBuffers(const VuoMesh mesh, unsigned int *vertexCount,
1124  unsigned int *combinedBuffer,
1125  void **normalOffset, void **textureCoordinateOffset, void **colorOffset,
1126  unsigned int *elementCount, unsigned int *elementBuffer)
1127 {
1128  if (!mesh)
1129  return;
1130 
1131  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1132  VuoMesh_upload(m);
1133  if (vertexCount)
1134  *vertexCount = m->vertexCount;
1135  if (combinedBuffer)
1136  *combinedBuffer = m->glUpload.combinedBuffer;
1137  if (normalOffset)
1138  *normalOffset = m->glUpload.normalOffset;
1139  if (textureCoordinateOffset)
1140  *textureCoordinateOffset = m->glUpload.textureCoordinateOffset;
1141  if (colorOffset)
1142  *colorOffset = m->glUpload.colorOffset;
1143  if (elementCount)
1144  *elementCount = m->elementCount;
1145  if (elementBuffer)
1146  *elementBuffer = m->glUpload.elementBuffer;
1147 }
1148 
1155 {
1156  if (!mesh)
1158 
1159  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1160  return m->elementAssemblyMethod;
1161 }
1162 
1169 {
1170  if (!mesh)
1171  return VuoMesh_CullNone;
1172 
1173  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1174  return m->faceCulling;
1175 }
1176 
1182 unsigned int VuoMesh_getFaceCullingGL(const VuoMesh mesh)
1183 {
1184  if (!mesh)
1185  return GL_NONE;
1186 
1187  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1188  if (m->faceCulling == VuoMesh_CullBackfaces)
1189  return GL_BACK;
1190  else if (m->faceCulling == VuoMesh_CullFrontfaces)
1191  return GL_FRONT;
1192  else // if (m->faceCulling == VuoMesh_CullNone)
1193  return GL_NONE;
1194 }
1195 
1202 {
1203  if (!mesh)
1204  return 0;
1205 
1206  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1207  return m->primitiveSize;
1208 }
1209 
1215 unsigned int VuoMesh_getElementBufferSize(const VuoMesh mesh)
1216 {
1217  if (!mesh)
1218  return 0;
1219 
1220  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1221  return m->glUpload.elementBufferSize;
1222 }
1223 
1232 void VuoMesh_setCPUBuffers(VuoMesh mesh, unsigned int vertexCount,
1233  float *positions, float *normals, float *textureCoordinates, float *colors,
1234  unsigned int elementCount, unsigned int *elements)
1235 {
1236  if (!mesh)
1237  return;
1238 
1239  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1240 
1241  m->vertexCount = vertexCount;
1242 
1243  if (m->positions != positions)
1244  {
1245  free(m->positions);
1246  m->positions = positions;
1247  }
1248 
1249  if (m->normals != normals)
1250  {
1251  free(m->normals);
1252  m->normals = normals;
1253  }
1254 
1255  if (m->textureCoordinates != textureCoordinates)
1256  {
1257  free(m->textureCoordinates);
1258  m->textureCoordinates = textureCoordinates;
1259  }
1260 
1261  if (m->colors != colors)
1262  {
1263  free(m->colors);
1264  m->colors = colors;
1265  }
1266 
1267  m->elementCount = elementCount;
1268 
1269  if (m->elements != elements)
1270  {
1271  free(m->elements);
1272  m->elements = elements;
1273  }
1274 
1275  VuoGlPool_release(VuoGlPool_ArrayBuffer, m->glUpload.combinedBufferSize, m->glUpload.combinedBuffer);
1276  VuoGlPool_release(VuoGlPool_ElementArrayBuffer, m->glUpload.elementBufferSize, m->glUpload.elementBuffer);
1277  m->glUpload.combinedBufferSize = 0;
1278  m->glUpload.combinedBuffer = 0;
1279  m->glUpload.elementBufferSize = 0;
1280  m->glUpload.elementBuffer = 0;
1281 
1282  VuoMesh_upload(m);
1283 }
1284 
1291 {
1292  if (!mesh)
1293  return;
1294 
1295  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1296  m->faceCulling = faceCulling;
1297 }
1298 
1304 void VuoMesh_setPrimitiveSize(VuoMesh mesh, VuoReal primitiveSize)
1305 {
1306  if (!mesh)
1307  return;
1308 
1309  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1310  m->primitiveSize = primitiveSize;
1311 }
1312 
1318 {
1319  if (strcmp(elementAssemblyMethodString,"individualTriangles")==0)
1321  else if (strcmp(elementAssemblyMethodString,"triangleStrip")==0)
1322  return VuoMesh_TriangleStrip;
1323  else if (strcmp(elementAssemblyMethodString,"triangleFan")==0)
1324  return VuoMesh_TriangleFan;
1325  else if (strcmp(elementAssemblyMethodString,"individualLines")==0)
1326  return VuoMesh_IndividualLines;
1327  else if (strcmp(elementAssemblyMethodString,"lineStrip")==0)
1328  return VuoMesh_LineStrip;
1329  else if (strcmp(elementAssemblyMethodString,"points")==0)
1330  return VuoMesh_Points;
1331 
1333 }
1334 
1341 {
1342  switch (elementAssemblyMethod)
1343  {
1345  return "individualTriangles";
1346  case VuoMesh_TriangleStrip:
1347  return "triangleStrip";
1348  case VuoMesh_TriangleFan:
1349  return "triangleFan";
1351  return "individualLines";
1352  case VuoMesh_LineStrip:
1353  return "lineStrip";
1354  case VuoMesh_Points:
1355  return "points";
1356  }
1357 }
1358 
1362 static void VuoMesh_download(VuoMesh_internal *m)
1363 {
1364  if (!m->glUpload.combinedBuffer
1365  || (m->positions
1366  && (m->normals || !m->glUpload.normalOffset)
1367  && (m->textureCoordinates || !m->glUpload.textureCoordinateOffset)
1368  && (m->colors || !m->glUpload.colorOffset)
1369  && (m->elements || !m->glUpload.elementBuffer)))
1370  return;
1371 
1372  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1373  glBindBuffer(GL_ARRAY_BUFFER, m->glUpload.combinedBuffer);
1374 
1375  // @@@ is this necessary?
1376 // glFlush();
1377 
1378  float *vertexData = (float *)malloc(m->glUpload.combinedBufferSize);
1379  glGetBufferSubData(GL_ARRAY_BUFFER, 0, m->glUpload.combinedBufferSize, vertexData);
1380  glBindBuffer(GL_ARRAY_BUFFER, 0);
1381 
1382  if (!m->positions)
1383  {
1384  m->positions = (float *)malloc(sizeof(float) * 3 * m->vertexCount);
1385  memcpy(m->positions, vertexData, sizeof(float) * 3 * m->vertexCount);
1386  }
1387 
1388  if (!m->normals && m->glUpload.normalOffset)
1389  {
1390  m->normals = (float *)malloc(sizeof(float) * 3 * m->vertexCount);
1391  memcpy(m->normals, (char *)vertexData + (int)m->glUpload.normalOffset, sizeof(float) * 3 * m->vertexCount);
1392  }
1393 
1394  if (!m->textureCoordinates && m->glUpload.textureCoordinateOffset)
1395  {
1396  m->textureCoordinates = (float *)malloc(sizeof(float) * 2 * m->vertexCount);
1397  memcpy(m->textureCoordinates, (char *)vertexData + (int)m->glUpload.textureCoordinateOffset, sizeof(float) * 2 * m->vertexCount);
1398  }
1399 
1400  if (!m->colors && m->glUpload.colorOffset)
1401  {
1402  m->colors = (float *)malloc(sizeof(float) * 4 * m->vertexCount);
1403  memcpy(m->colors, (char *)vertexData + (int)m->glUpload.colorOffset, sizeof(float) * 4 * m->vertexCount);
1404  }
1405 
1406  if (!m->elements && m->glUpload.elementBuffer)
1407  {
1408  m->elements = malloc(m->glUpload.elementBufferSize);
1409  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->glUpload.elementBuffer);
1410  glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, m->glUpload.elementBufferSize, m->elements);
1411  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1412  }
1413  });
1414 }
1415 
1419 VuoBox VuoMesh_bounds(const VuoMesh mesh, float matrix[16])
1420 {
1421  if (!mesh)
1422  return VuoBox_make((VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0});
1423 
1424  VuoPoint3d min, max;
1425  bool init = false;
1426 
1427  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1428  unsigned int vertexCount = m->vertexCount;
1429 
1430  VuoMesh_download(m);
1431 
1432  if(vertexCount > 0 && !init)
1433  {
1434  min = max = VuoTransform_transformPoint((float*)matrix, VuoPoint3d_makeFromArray(&m->positions[0]));
1435  init = true;
1436  }
1437 
1438  for(int n = 0; n < vertexCount; n++)
1439  {
1440  VuoPoint3d p = VuoTransform_transformPoint((float*)matrix, VuoPoint3d_makeFromArray(&m->positions[n * 3]));
1441 
1442  min.x = MIN(p.x, min.x);
1443  min.y = MIN(p.y, min.y);
1444  min.z = MIN(p.z, min.z);
1445 
1446  max.x = MAX(p.x, max.x);
1447  max.y = MAX(p.y, max.y);
1448  max.z = MAX(p.z, max.z);
1449  }
1450 
1451 // VLog("%fx%fx%f @ %f,%f,%f",bounds->size.x,bounds->size.y,bounds->size.z,bounds->center.x,bounds->center.y,bounds->center.z);
1452 
1453 
1454  if(init)
1455  return VuoBox_make((min + max) / (VuoPoint3d)(2.), max - min);
1456  else
1457  return VuoBox_make( (VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0} );
1458 }
1459 
1464 {
1465  if (!mesh)
1466  return false;
1467 
1468  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1469  return m->vertexCount > 0;
1470 }
1471 
1479 {
1480  json_object *o = NULL;
1481 
1482  if (json_object_object_get_ex(js, "pointer", &o))
1483  return (VuoMesh)json_object_get_int64(o);
1484 
1485  return NULL;
1486 }
1487 
1493 {
1494  if (!value)
1495  return NULL;
1496 
1497  json_object *js = json_object_new_object();
1498  json_object_object_add(js, "pointer", json_object_new_int64((int64_t)value));
1499  return js;
1500 }
1501 
1507 {
1508  return VuoMesh_getJson(value);
1509 }
1510 
1515 char *VuoMesh_getSummary(const VuoMesh mesh)
1516 {
1517  if (!mesh)
1518  return strdup("Empty mesh");
1519 
1520  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1521 
1522  if (!m->vertexCount)
1523  return strdup("Mesh without any vertices");
1524 
1525  unsigned long objectCount = VuoMesh_getSplitPrimitiveCount(mesh);
1526  const char * objectString = "";
1527  const char * assemblyMethod = " (unknown element assembly method)";
1528  if (m->elementAssemblyMethod == VuoMesh_IndividualTriangles)
1529  {
1530  assemblyMethod = ", ";
1531  objectString = "triangle";
1533  }
1534  else if (m->elementAssemblyMethod == VuoMesh_TriangleStrip)
1535  {
1536  assemblyMethod = " in a strip of ";
1537  objectString = "triangle";
1538  }
1539  else if (m->elementAssemblyMethod == VuoMesh_TriangleFan)
1540  {
1541  assemblyMethod = " in a fan of ";
1542  objectString = "triangle";
1543  }
1544  else if (m->elementAssemblyMethod == VuoMesh_IndividualLines)
1545  {
1546  assemblyMethod = ", ";
1547  objectString = "line";
1548  }
1549  else if (m->elementAssemblyMethod == VuoMesh_LineStrip)
1550  {
1551  assemblyMethod = " in a strip of ";
1552  objectString = "line";
1553  }
1554  else if (m->elementAssemblyMethod == VuoMesh_Points)
1555  {
1556  assemblyMethod = ", ";
1557  objectString = "point";
1558  }
1559 
1560  const char * vertexCountString = m->vertexCount==1 ? "vertex" : "vertices";
1561  const char * objectStringPlural = objectCount==1 ? "" : "s";
1562 
1563  char *firstPosition = NULL;
1564  if (m->positions)
1565  firstPosition = VuoText_format("<div>with first position (%s)</div>", VuoPoint3d_getSummary(VuoPoint3d_makeFromArray(&m->positions[0])));
1566 
1567  char *summary = VuoText_format("<div>%u %s%s%lu %s%s</div>%s<div>%s positions</div><div>%s normals</div><div>%s texture coordinates</div><div>%s vertex colors</div>",
1568  m->vertexCount, vertexCountString, assemblyMethod, objectCount, objectString, objectStringPlural,
1569  firstPosition ? firstPosition : "",
1570  (m->positions || m->glUpload.combinedBuffer ) ? "✓" : "◻",
1571  (m->normals || m->glUpload.normalOffset ) ? "✓" : "◻",
1572  (m->textureCoordinates || m->glUpload.textureCoordinateOffset) ? "✓" : "◻",
1573  (m->colors || m->glUpload.colorOffset ) ? "✓" : "◻");
1574 
1575  free(firstPosition);
1576  return summary;
1577 }