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