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  "VuoPoint2d",
32  "VuoPoint3d",
33  "VuoPoint4d",
34  "VuoList_VuoPoint2d",
35  "VuoList_VuoPoint3d",
36  "VuoText",
37  "VuoTransform",
38  "VuoGlContext",
39  "VuoGlPool"
40  ]
41  });
42 #endif
43 
48 typedef struct
49 {
50  unsigned int vertexCount;
51  VuoPoint4d *positions;
52  VuoPoint4d *normals;
53  VuoPoint4d *tangents;
54  VuoPoint4d *bitangents;
55  VuoPoint4d *textureCoordinates;
56 
57  unsigned int elementCount;
58 
61  unsigned int *elements;
62 
64  VuoMesh_ElementAssemblyMethod elementAssemblyMethod;
65 
70  VuoReal primitiveSize;
71 
72  VuoMesh_FaceCulling faceCulling;
73 
77  struct
78  {
79  unsigned int combinedBuffer;
80  unsigned int combinedBufferSize;
81  unsigned int combinedBufferStride;
82 
83  void *normalOffset;
84  void *tangentOffset;
85  void *bitangentOffset;
86  void *textureCoordinateOffset;
87 
88  unsigned int elementBuffer;
89  unsigned int elementBufferSize;
90  } glUpload;
91 } VuoMesh_internal;
92 
98 void VuoMesh_allocateCPUBuffers(unsigned int vertexCount,
99  VuoPoint4d **positions, VuoPoint4d **normals, VuoPoint4d **tangents, VuoPoint4d **bitangents, VuoPoint4d **textureCoordinates,
100  unsigned int elementCount, unsigned int **elements)
101 {
102  if (positions)
103  *positions = (VuoPoint4d *)malloc(sizeof(VuoPoint4d) * vertexCount);
104  if (normals)
105  *normals = (VuoPoint4d *)malloc(sizeof(VuoPoint4d) * vertexCount);
106  if (tangents)
107  *tangents = (VuoPoint4d *)malloc(sizeof(VuoPoint4d) * vertexCount);
108  if (bitangents)
109  *bitangents = (VuoPoint4d *)malloc(sizeof(VuoPoint4d) * vertexCount);
110  if (textureCoordinates)
111  *textureCoordinates = (VuoPoint4d *)malloc(sizeof(VuoPoint4d) * vertexCount);
112  if (elements)
113  *elements = elementCount ? (unsigned int *)malloc(sizeof(unsigned int) * elementCount) : NULL;
114 }
115 
119 static void VuoMesh_free(void *value)
120 {
121  VuoMesh_internal *m = (VuoMesh_internal *)value;
122 
123  free(m->positions);
124  free(m->normals);
125  free(m->tangents);
126  free(m->bitangents);
127  free(m->textureCoordinates);
128  free(m->elements);
129 
130  VuoGlPool_release(VuoGlPool_ArrayBuffer, m->glUpload.combinedBufferSize, m->glUpload.combinedBuffer);
131  VuoGlPool_release(VuoGlPool_ElementArrayBuffer, m->glUpload.elementBufferSize, m->glUpload.elementBuffer);
132 
133  free(m);
134 }
135 
139 static VuoMesh_internal *VuoMesh_makeInternal(void)
140 {
141  VuoMesh_internal *m = (VuoMesh_internal *)calloc(1, sizeof(VuoMesh_internal));
143  m->faceCulling = VuoMesh_CullBackfaces;
144  return m;
145 }
146 
150 static VuoMesh_internal *VuoMesh_makeSingletonInternal(void)
151 {
152  VuoMesh_internal *m = (VuoMesh_internal *)calloc(1, sizeof(VuoMesh_internal));
154  m->faceCulling = VuoMesh_CullBackfaces;
155  return m;
156 }
157 
158 static void VuoMesh_upload(VuoMesh_internal *m);
159 
169 VuoMesh VuoMesh_makeFromCPUBuffers(unsigned int vertexCount,
170  VuoPoint4d *positions, VuoPoint4d *normals, VuoPoint4d *tangents, VuoPoint4d *bitangents, VuoPoint4d *textureCoordinates,
171  unsigned int elementCount, unsigned int *elements, VuoMesh_ElementAssemblyMethod elementAssemblyMethod)
172 {
173  VuoMesh_internal *m = VuoMesh_makeInternal();
174 
175  m->vertexCount = vertexCount;
176  m->positions = positions;
177  m->normals = normals;
178  m->tangents = tangents;
179  m->bitangents = bitangents;
180  m->textureCoordinates = textureCoordinates;
181  m->elementCount = elementCount;
182  m->elements = elements;
183  m->elementAssemblyMethod = elementAssemblyMethod;
184 
185  VuoMesh_upload(m);
186  return (VuoMesh)m;
187 }
188 
192 VuoMesh VuoMesh_makeFromGPUBuffers(unsigned int vertexCount, unsigned int combinedBuffer, unsigned int combinedBufferSize, unsigned int combinedBufferStride, void *normalOffset, void *tangentOffset, void *bitangentOffset, void *textureCoordinateOffset, unsigned int elementCount, unsigned int elementBuffer, unsigned int elementBufferSize, VuoMesh_ElementAssemblyMethod elementAssemblyMethod)
193 {
194  VuoMesh_internal *m = VuoMesh_makeInternal();
195 
196  m->vertexCount = vertexCount;
197  m->elementCount = elementCount;
198  m->elementAssemblyMethod = elementAssemblyMethod;
199  m->glUpload.combinedBuffer = combinedBuffer;
200  m->glUpload.combinedBufferSize = combinedBufferSize;
201  m->glUpload.combinedBufferStride = combinedBufferStride;
202  m->glUpload.normalOffset = normalOffset;
203  m->glUpload.tangentOffset = tangentOffset;
204  m->glUpload.bitangentOffset = bitangentOffset;
205  m->glUpload.textureCoordinateOffset = textureCoordinateOffset;
206  m->glUpload.elementBuffer = elementBuffer;
207  m->glUpload.elementBufferSize = elementBufferSize;
208 
209  return (VuoMesh)m;
210 }
211 
215 unsigned long VuoMesh_getGlMode(VuoMesh mesh)
216 {
217  if (!mesh)
218  return GL_NONE;
219 
220  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
221 
222  if (m->elementAssemblyMethod == VuoMesh_IndividualTriangles)
223  return GL_TRIANGLES;
224  else if (m->elementAssemblyMethod == VuoMesh_TriangleStrip)
225  return GL_TRIANGLE_STRIP;
226  else if (m->elementAssemblyMethod == VuoMesh_TriangleFan)
227  return GL_TRIANGLE_FAN;
228  else if (m->elementAssemblyMethod == VuoMesh_IndividualLines)
229  return GL_LINES;
230  else if (m->elementAssemblyMethod == VuoMesh_LineStrip)
231  return GL_LINE_STRIP;
232  else if (m->elementAssemblyMethod == VuoMesh_Points)
233  return GL_POINTS;
234 
235  return GL_TRIANGLES;
236 }
237 
247 {
248  if (!mesh)
249  return 0;
250 
251  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
252 
253  if (m->elementAssemblyMethod == VuoMesh_IndividualTriangles)
254  return m->elementCount ? m->elementCount/3 : m->vertexCount/3;
255  else if (m->elementAssemblyMethod == VuoMesh_TriangleStrip)
256  return m->elementCount ? m->elementCount-2 : m->vertexCount-2;
257  else if (m->elementAssemblyMethod == VuoMesh_TriangleFan)
258  return m->elementCount ? m->elementCount-2 : m->vertexCount-2;
259  else if (m->elementAssemblyMethod == VuoMesh_IndividualLines)
260  return m->elementCount ? m->elementCount/2 : m->vertexCount/2;
261  else if (m->elementAssemblyMethod == VuoMesh_LineStrip)
262  return m->elementCount ? m->elementCount-1 : m->vertexCount-1;
263  else if (m->elementAssemblyMethod == VuoMesh_Points)
264  return m->elementCount ? m->elementCount : m->vertexCount;
265 
266  return 0;
267 }
268 
278 {
279  if (!mesh)
280  return 0;
281 
282  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
283 
284  if (m->elementCount == 0 && m->vertexCount == 0)
285  return 0;
286 
287  if (m->elementAssemblyMethod == VuoMesh_IndividualTriangles)
288  return m->elementCount ? m->elementCount : m->vertexCount;
289  else if (m->elementAssemblyMethod == VuoMesh_TriangleStrip)
290  return m->elementCount ? (m->elementCount-2)*3 : (m->vertexCount-2)*3;
291  else if (m->elementAssemblyMethod == VuoMesh_TriangleFan)
292  return m->elementCount ? (m->elementCount-2)*3 : (m->vertexCount-2)*3;
293  else if (m->elementAssemblyMethod == VuoMesh_IndividualLines)
294  return m->elementCount ? m->elementCount : m->vertexCount;
295  else if (m->elementAssemblyMethod == VuoMesh_LineStrip)
296  return m->elementCount ? (m->elementCount-1)*2 : (m->vertexCount-1)*2;
297  else if (m->elementAssemblyMethod == VuoMesh_Points)
298  return m->elementCount ? m->elementCount : m->vertexCount;
299 
300  return 0;
301 }
302 
303 static unsigned long VuoMesh_getCompleteElementCountInternal(unsigned long elementCount, VuoMesh_ElementAssemblyMethod elementAssemblyMethod);
304 
314 unsigned long VuoMesh_getCompleteElementCount(const VuoMesh mesh)
315 {
316  if (!mesh)
317  return 0;
318 
319  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
320 
321  unsigned long elementCount = m->elementCount ? m->elementCount : m->vertexCount;
322  return VuoMesh_getCompleteElementCountInternal(elementCount, m->elementAssemblyMethod);
323 }
324 
328 static void VuoMesh_upload(VuoMesh_internal *m)
329 {
330  if (!m || m->glUpload.combinedBuffer)
331  return;
332 
333  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
334 
335  // Create a temporary Vertex Array Object, so we can bind the buffers in order to upload them.
336  GLuint vertexArray;
337  glGenVertexArrays(1, &vertexArray);
338  glBindVertexArray(vertexArray);
339 
340  // Combine the vertex attribute buffers together, so we only have to upload a single one per mesh:
341 
342 
343  // Decide on interleaved positions for vertex attributes.
344  // Needs to be interleaved because glTransformFeedbackVaryings is only able to write to 4 buffers, but we have more than 4 buffers.
345  unsigned int bufferCount = 0;
346 
347  ++bufferCount; // position
348 
349  unsigned int normalOffset = 0; // Offsets are VuoPoint4ds (not bytes).
350  if (m->normals)
351  normalOffset = bufferCount++;
352  m->glUpload.normalOffset = (void *)(normalOffset*sizeof(VuoPoint4d));
353 
354  unsigned int tangentOffset = 0;
355  if (m->tangents)
356  tangentOffset = bufferCount++;
357  m->glUpload.tangentOffset = (void *)(tangentOffset*sizeof(VuoPoint4d));
358 
359  unsigned int bitangentOffset = 0;
360  if (m->bitangents)
361  bitangentOffset = bufferCount++;
362  m->glUpload.bitangentOffset = (void *)(bitangentOffset*sizeof(VuoPoint4d));
363 
364  unsigned int textureCoordinateOffset = 0;
365  if (m->textureCoordinates)
366  textureCoordinateOffset = bufferCount++;
367  m->glUpload.textureCoordinateOffset = (void *)(textureCoordinateOffset*sizeof(VuoPoint4d));
368 
369  unsigned long singleBufferSize = sizeof(VuoPoint4d)*m->vertexCount;
370  VuoPoint4d *combinedData = (VuoPoint4d *)malloc(singleBufferSize*bufferCount);
371 
372 
373  // Combine vertex attributes into the interleaved client-side buffer.
374  for (unsigned long i = 0; i < m->vertexCount; ++i)
375  {
376  combinedData[i*bufferCount] = m->positions[i];
377  if (m->normals)
378  combinedData[i*bufferCount+normalOffset] = m->normals[i];
379  if (m->tangents)
380  combinedData[i*bufferCount+tangentOffset] = m->tangents[i];
381  if (m->bitangents)
382  combinedData[i*bufferCount+bitangentOffset] = m->bitangents[i];
383  if (m->textureCoordinates)
384  combinedData[i*bufferCount+textureCoordinateOffset] = m->textureCoordinates[i];
385  }
386 
387 
388  // Upload the combined buffer.
389  m->glUpload.combinedBufferSize = singleBufferSize*bufferCount;
390  m->glUpload.combinedBuffer = VuoGlPool_use(cgl_ctx, VuoGlPool_ArrayBuffer, m->glUpload.combinedBufferSize);
391  VuoGlPool_retain(m->glUpload.combinedBuffer);
392  glBindBuffer(GL_ARRAY_BUFFER, m->glUpload.combinedBuffer);
393  glBufferSubData(GL_ARRAY_BUFFER, 0, singleBufferSize * bufferCount, combinedData);
394  free(combinedData);
395 
396 
397  // Upload the Element Buffer and add it to the Vertex Array Object
398  m->glUpload.elementBufferSize = sizeof(unsigned int)*m->elementCount;
399  m->glUpload.elementBuffer = VuoGlPool_use(cgl_ctx, VuoGlPool_ElementArrayBuffer, m->glUpload.elementBufferSize);
400  VuoGlPool_retain(m->glUpload.elementBuffer);
401  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->glUpload.elementBuffer);
402  glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(unsigned int) * m->elementCount, m->elements);
403 
404  glBindBuffer(GL_ARRAY_BUFFER, 0);
405 // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // handled by glDeleteVertexArrays()
406 // glBindVertexArray(0); // handled by glDeleteVertexArrays()
407  glDeleteVertexArrays(1, &vertexArray);
408 
409  // Prepare for this mesh to be used on a different OpenGL context.
410  // @@@ is this still needed?
411  glFlushRenderAPPLE();
412 
413  });
414 }
415 
420 {
421  VuoMesh_internal *m = VuoMesh_makeSingletonInternal();
422  m->vertexCount = 4;
423  m->elementCount = 6;
424  VuoMesh_allocateCPUBuffers(m->vertexCount, &m->positions, &m->normals, &m->tangents, &m->bitangents, &m->textureCoordinates, m->elementCount, &m->elements);
425 
426  // Positions
427  m->positions[0] = (VuoPoint4d){-.5, -.5, 0, 1};
428  m->positions[1] = (VuoPoint4d){ .5, -.5, 0, 1};
429  m->positions[2] = (VuoPoint4d){-.5, .5, 0, 1};
430  m->positions[3] = (VuoPoint4d){ .5, .5, 0, 1};
431 
432  // Normals
433  for (int i = 0; i < m->vertexCount; ++i)
434  m->normals[i] = (VuoPoint4d){0, 0, 1, 1};
435 
436  // Tangents
437  for (int i = 0; i < m->vertexCount; ++i)
438  m->tangents[i] = (VuoPoint4d){1, 0, 0, 1};
439 
440  // Bitangents
441  for (int i = 0; i < m->vertexCount; ++i)
442  m->bitangents[i] = (VuoPoint4d){0, 1, 0, 1};
443 
444  // Texture Coordinates
445  m->textureCoordinates[0] = (VuoPoint4d){0, 0, 0, 1};
446  m->textureCoordinates[1] = (VuoPoint4d){1, 0, 0, 1};
447  m->textureCoordinates[2] = (VuoPoint4d){0, 1, 0, 1};
448  m->textureCoordinates[3] = (VuoPoint4d){1, 1, 0, 1};
449 
450  // Triangle elements
451  m->elementAssemblyMethod = VuoMesh_IndividualTriangles;
452  // Order the elements so that the diagonal edge of each triangle
453  // is last, so that vuo.shader.make.wireframe can optionally omit them.
454  m->elements[0] = 2;
455  m->elements[1] = 0;
456  m->elements[2] = 1;
457  m->elements[3] = 1;
458  m->elements[4] = 3;
459  m->elements[5] = 2;
460 
461  VuoMesh_upload(m);
462  return (VuoMesh)m;
463 }
464 
471 {
472  VuoMesh_internal *m = VuoMesh_makeSingletonInternal();
473  m->vertexCount = 4;
474  m->elementCount = 6;
475  VuoMesh_allocateCPUBuffers(m->vertexCount, &m->positions, NULL, NULL, NULL, &m->textureCoordinates, m->elementCount, &m->elements);
476 
477  // Positions
478  m->positions[0] = (VuoPoint4d){-.5, -.5, 0, 1};
479  m->positions[1] = (VuoPoint4d){ .5, -.5, 0, 1};
480  m->positions[2] = (VuoPoint4d){-.5, .5, 0, 1};
481  m->positions[3] = (VuoPoint4d){ .5, .5, 0, 1};
482 
483  // Texture Coordinates
484  m->textureCoordinates[0] = (VuoPoint4d){0, 0, 0, 1};
485  m->textureCoordinates[1] = (VuoPoint4d){1, 0, 0, 1};
486  m->textureCoordinates[2] = (VuoPoint4d){0, 1, 0, 1};
487  m->textureCoordinates[3] = (VuoPoint4d){1, 1, 0, 1};
488 
489  // Triangle elements
490  m->elementAssemblyMethod = VuoMesh_IndividualTriangles;
491  // Order the elements so that the diagonal edge of each triangle
492  // is last, so that vuo.shader.make.wireframe can optionally omit them.
493  m->elements[0] = 2;
494  m->elements[1] = 0;
495  m->elements[2] = 1;
496  m->elements[3] = 1;
497  m->elements[4] = 3;
498  m->elements[5] = 2;
499 
500  VuoMesh_upload(m);
501  return (VuoMesh)m;
502 }
503 
508 {
509  VuoMesh_internal *m = VuoMesh_makeSingletonInternal();
510  m->vertexCount = 3;
511  m->elementCount = 3;
512  VuoMesh_allocateCPUBuffers(m->vertexCount, &m->positions, &m->normals, &m->tangents, &m->bitangents, &m->textureCoordinates, m->elementCount, &m->elements);
513 
514  // Positions
515  for (int i = 0; i < m->vertexCount; ++i)
516  {
517  float angle = M_PI/2. + i * 2*M_PI/3.;
518  m->positions[i] = (VuoPoint4d){cos(angle)/sqrt(3), sin(angle)/sqrt(3), 0, 1};
519  }
520 
521  // Normals
522  for (int i = 0; i < m->vertexCount; ++i)
523  m->normals[i] = (VuoPoint4d){0, 0, 1, 1};
524 
525  // Tangents
526  for (int i = 0; i < m->vertexCount; ++i)
527  m->tangents[i] = (VuoPoint4d){1, 0, 0, 1};
528 
529  // Bitangents
530  for (int i = 0; i < m->vertexCount; ++i)
531  m->bitangents[i] = (VuoPoint4d){0, 1, 0, 1};
532 
533  // Texture Coordinates
534  m->textureCoordinates[0] = (VuoPoint4d){.5, 1, 0, 1};
535  m->textureCoordinates[1] = (VuoPoint4d){ 0, 0, 0, 1};
536  m->textureCoordinates[2] = (VuoPoint4d){ 1, 0, 0, 1};
537 
538  // Triangle Strip elements
539  m->elementAssemblyMethod = VuoMesh_TriangleStrip;
540  m->elements[0] = 0;
541  m->elements[1] = 1;
542  m->elements[2] = 2;
543 
544  VuoMesh_upload(m);
545  return (VuoMesh)m;
546 }
547 
554 {
555  static VuoMesh sharedQuadWithNormals;
556  static dispatch_once_t token = 0;
557  dispatch_once(&token, ^{
558  sharedQuadWithNormals = VuoMesh_makeQuadWithNormalsInternal();
559  VuoRetain(sharedQuadWithNormals);
560  });
561  return sharedQuadWithNormals;
562 }
563 
572 {
573  static VuoMesh sharedQuadWithoutNormals;
574  static dispatch_once_t token = 0;
575  dispatch_once(&token, ^{
576  sharedQuadWithoutNormals = VuoMesh_makeQuadWithoutNormalsInternal();
577  VuoRetain(sharedQuadWithoutNormals);
578  });
579  return sharedQuadWithoutNormals;
580 }
581 
588 {
589  static VuoMesh sharedEquilateralTriangle;
590  static dispatch_once_t token = 0;
591  dispatch_once(&token, ^{
592  sharedEquilateralTriangle = VuoMesh_makeEquilateralTriangleInternal();
593  VuoRetain(sharedEquilateralTriangle);
594  });
595  return sharedEquilateralTriangle;
596 }
597 
602 {
603  // Separate vertices for each face, so each face can have its own sharp normals and texture coordinates.
604 
605  VuoPoint4d positions[] = (VuoPoint4d[]){
606  // Front
607  {-.5, -.5, .5, 1},
608  { .5, -.5, .5, 1},
609  {-.5, .5, .5, 1},
610  { .5, .5, .5, 1},
611  // Right
612  { .5, -.5, .5, 1},
613  { .5, -.5, -.5, 1},
614  { .5, .5, .5, 1},
615  { .5, .5, -.5, 1},
616  // Bottom
617  {-.5, -.5, -.5, 1},
618  { .5, -.5, -.5, 1},
619  {-.5, -.5, .5, 1},
620  { .5, -.5, .5, 1},
621  // Left
622  {-.5, -.5, -.5, 1},
623  {-.5, -.5, .5, 1},
624  {-.5, .5, -.5, 1},
625  {-.5, .5, .5, 1},
626  // Top
627  {-.5, .5, .5, 1},
628  { .5, .5, .5, 1},
629  {-.5, .5, -.5, 1},
630  { .5, .5, -.5, 1},
631  // Back
632  { .5, -.5, -.5, 1},
633  {-.5, -.5, -.5, 1},
634  { .5, .5, -.5, 1},
635  {-.5, .5, -.5, 1},
636  };
637 
638  VuoPoint4d normals[] = (VuoPoint4d[]){
639  // Front
640  {0, 0, 1, 1},
641  {0, 0, 1, 1},
642  {0, 0, 1, 1},
643  {0, 0, 1, 1},
644  // Right
645  {1, 0, 0, 1},
646  {1, 0, 0, 1},
647  {1, 0, 0, 1},
648  {1, 0, 0, 1},
649  // Bottom
650  {0, -1, 0, 1},
651  {0, -1, 0, 1},
652  {0, -1, 0, 1},
653  {0, -1, 0, 1},
654  // Left
655  {-1, 0, 0, 1},
656  {-1, 0, 0, 1},
657  {-1, 0, 0, 1},
658  {-1, 0, 0, 1},
659  // Top
660  {0, 1, 0, 1},
661  {0, 1, 0, 1},
662  {0, 1, 0, 1},
663  {0, 1, 0, 1},
664  // Back
665  {0, 0, -1, 1},
666  {0, 0, -1, 1},
667  {0, 0, -1, 1},
668  {0, 0, -1, 1},
669  };
670 
671  VuoPoint4d tangents[] = (VuoPoint4d[]){
672  // Front
673  {1, 0, 0, 1},
674  {1, 0, 0, 1},
675  {1, 0, 0, 1},
676  {1, 0, 0, 1},
677  // Right
678  {0, 0, -1, 1},
679  {0, 0, -1, 1},
680  {0, 0, -1, 1},
681  {0, 0, -1, 1},
682  // Bottom
683  {1, 0, 0, 1},
684  {1, 0, 0, 1},
685  {1, 0, 0, 1},
686  {1, 0, 0, 1},
687  // Left
688  {0, 0, 1, 1},
689  {0, 0, 1, 1},
690  {0, 0, 1, 1},
691  {0, 0, 1, 1},
692  // Top
693  {1, 0, 0, 1},
694  {1, 0, 0, 1},
695  {1, 0, 0, 1},
696  {1, 0, 0, 1},
697  // Back
698  {-1, 0, 0, 1},
699  {-1, 0, 0, 1},
700  {-1, 0, 0, 1},
701  {-1, 0, 0, 1},
702  };
703 
704  VuoPoint4d bitangents[] = (VuoPoint4d[]){
705  // Front
706  {0, 1, 0, 1},
707  {0, 1, 0, 1},
708  {0, 1, 0, 1},
709  {0, 1, 0, 1},
710  // Right
711  {0, 1, 0, 1},
712  {0, 1, 0, 1},
713  {0, 1, 0, 1},
714  {0, 1, 0, 1},
715  // Bottom
716  {0, 0, 1, 1},
717  {0, 0, 1, 1},
718  {0, 0, 1, 1},
719  {0, 0, 1, 1},
720  // Left
721  {0, 1, 0, 1},
722  {0, 1, 0, 1},
723  {0, 1, 0, 1},
724  {0, 1, 0, 1},
725  // Top
726  {0, 0, -1, 1},
727  {0, 0, -1, 1},
728  {0, 0, -1, 1},
729  {0, 0, -1, 1},
730  // Back
731  {0, 1, 0, 1},
732  {0, 1, 0, 1},
733  {0, 1, 0, 1},
734  {0, 1, 0, 1},
735  };
736 
737  VuoPoint4d textureCoordinates[] = (VuoPoint4d[]){
738  // Front
739  {0, 0, 0, 1},
740  {1, 0, 0, 1},
741  {0, 1, 0, 1},
742  {1, 1, 0, 1},
743  // Right
744  {0, 0, 0, 1},
745  {1, 0, 0, 1},
746  {0, 1, 0, 1},
747  {1, 1, 0, 1},
748  // Bottom
749  {0, 0, 0, 1},
750  {1, 0, 0, 1},
751  {0, 1, 0, 1},
752  {1, 1, 0, 1},
753  // Left
754  {0, 0, 0, 1},
755  {1, 0, 0, 1},
756  {0, 1, 0, 1},
757  {1, 1, 0, 1},
758  // Top
759  {0, 0, 0, 1},
760  {1, 0, 0, 1},
761  {0, 1, 0, 1},
762  {1, 1, 0, 1},
763  // Back
764  {0, 0, 0, 1},
765  {1, 0, 0, 1},
766  {0, 1, 0, 1},
767  {1, 1, 0, 1},
768  };
769 
770  unsigned int elements[] = (unsigned int[]){
771  // Front
772  2, 0, 1,
773  1, 3, 2,
774  // Right
775  6, 4, 5,
776  5, 7, 6,
777  // Bottom
778  10, 8, 9,
779  9, 11, 10,
780  // Left
781  14, 12, 13,
782  13, 15, 14,
783  // Top
784  18, 16, 17,
785  17, 19, 18,
786  // Back
787  22, 20, 21,
788  21, 23, 22,
789  };
790 
791  VuoMesh_internal *m = VuoMesh_makeSingletonInternal();
792  m->vertexCount = 6 * 4;
793  m->elementCount = 6 * 6;
794  VuoMesh_allocateCPUBuffers(m->vertexCount, &m->positions, &m->normals, &m->tangents, &m->bitangents, &m->textureCoordinates, m->elementCount, &m->elements);
795 
796  memcpy(m->positions, positions, sizeof(positions));
797  memcpy(m->normals, normals, sizeof(normals));
798  memcpy(m->tangents, tangents, sizeof(tangents));
799  memcpy(m->bitangents, bitangents, sizeof(bitangents));
800  memcpy(m->textureCoordinates, textureCoordinates, sizeof(textureCoordinates));
801  memcpy(m->elements, elements, sizeof(elements));
802 
803  VuoMesh_upload(m);
804  return (VuoMesh)m;
805 }
806 
813 {
814  static VuoMesh sharedCube;
815  static dispatch_once_t token = 0;
816  dispatch_once(&token, ^{
817  sharedCube = VuoMesh_makeCubeInternal();
818  VuoRetain(sharedCube);
819  });
820  return sharedCube;
821 }
822 
826 static unsigned long VuoMesh_getCompleteElementCountInternal(unsigned long elementCount, VuoMesh_ElementAssemblyMethod elementAssemblyMethod)
827 {
828  if (elementAssemblyMethod == VuoMesh_IndividualTriangles)
829  // Round down to a multiple of 3, since each triangle requires 3 vertices.
830  return (elementCount / 3) * 3;
831 
832  else if (elementAssemblyMethod == VuoMesh_TriangleStrip
833  || elementAssemblyMethod == VuoMesh_TriangleFan)
834  // Triangle strips and fans must have at least 1 complete triangle.
835  return elementCount < 3 ? 0 : elementCount;
836 
837  else if (elementAssemblyMethod == VuoMesh_IndividualLines)
838  // Round down to an even number of vertices, since each line requires a pair of vertices.
839  return (elementCount / 2) * 2;
840 
841  else if (elementAssemblyMethod == VuoMesh_LineStrip)
842  // Line strips must have at least 1 complete line.
843  return elementCount < 2 ? 0 : elementCount;
844 
845  else if (elementAssemblyMethod == VuoMesh_Points)
846  // Since all points are independent, any number of points is fine.
847  return elementCount;
848 
849  else
850  {
851  VUserLog("Error: Unknown submesh element assembly method: %d", elementAssemblyMethod);
852  return 0;
853  }
854 }
855 
861 {
862  unsigned long count = VuoListGetCount_VuoPoint2d(positions);
863  VuoPoint2d *positionValues = VuoListGetData_VuoPoint2d(positions);
864 
865  count = VuoMesh_getCompleteElementCountInternal(count, elementAssemblyMethod);
866  if (!count)
867  return NULL;
868 
869  VuoPoint4d *positions4d = (VuoPoint4d *)malloc(sizeof(VuoPoint4d) * count);
870  unsigned int *elements = (unsigned int *)malloc(sizeof(unsigned int) * count);
871  for (unsigned long i = 0; i < count; ++i)
872  {
873  VuoPoint2d xy = positionValues[i];
874  positions4d[i] = (VuoPoint4d){xy.x, xy.y, 0, 1};
875  elements[i] = i;
876  }
877 
878  VuoMesh mesh = VuoMesh_makeFromCPUBuffers(count,
879  positions4d, NULL, NULL, NULL, NULL,
880  count, elements, elementAssemblyMethod);
881  VuoMesh_setPrimitiveSize(mesh, primitiveSize);
882  return mesh;
883 }
884 
890 {
891  unsigned long count = VuoListGetCount_VuoPoint3d(positions);
892  VuoPoint3d *positionValues = VuoListGetData_VuoPoint3d(positions);
893 
894  count = VuoMesh_getCompleteElementCountInternal(count, elementAssemblyMethod);
895  if (!count)
896  return NULL;
897 
898  VuoPoint4d *positions4d = (VuoPoint4d *)malloc(sizeof(VuoPoint4d) * count);
899  unsigned int *elements = (unsigned int *)malloc(sizeof(unsigned int) * count);
900  for (unsigned long i = 0; i < count; ++i)
901  {
902  VuoPoint3d xyz = positionValues[i];
903  positions4d[i] = (VuoPoint4d){xyz.x, xyz.y, xyz.z, 1};
904  elements[i] = i;
905  }
906 
907  VuoMesh mesh = VuoMesh_makeFromCPUBuffers(count,
908  positions4d, NULL, NULL, NULL, NULL,
909  count, elements, elementAssemblyMethod);
910  VuoMesh_setPrimitiveSize(mesh, primitiveSize);
911  return mesh;
912 }
913 
918 {
919  if (!mesh)
920  return NULL;
921 
922  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
923  VuoMesh_internal *copiedMesh = VuoMesh_makeInternal();
924  copiedMesh->vertexCount = m->vertexCount;
925  copiedMesh->elementCount = m->elementCount;
926 
927  unsigned long attributeByteCount = sizeof(VuoPoint4d) * copiedMesh->vertexCount;
928 
929  if (m->positions)
930  {
931  copiedMesh->positions = (VuoPoint4d *)malloc(attributeByteCount);
932  memcpy(copiedMesh->positions, m->positions, attributeByteCount);
933  }
934  else
935  copiedMesh->positions = NULL;
936 
937  if (m->normals)
938  {
939  copiedMesh->normals = (VuoPoint4d *)malloc(attributeByteCount);
940  memcpy(copiedMesh->normals, m->normals, attributeByteCount);
941  }
942  else
943  copiedMesh->normals = NULL;
944 
945  if (m->tangents)
946  {
947  copiedMesh->tangents = (VuoPoint4d *)malloc(attributeByteCount);
948  memcpy(copiedMesh->tangents, m->tangents, attributeByteCount);
949  }
950  else
951  copiedMesh->tangents = NULL;
952 
953  if (m->bitangents)
954  {
955  copiedMesh->bitangents = (VuoPoint4d *)malloc(attributeByteCount);
956  memcpy(copiedMesh->bitangents, m->bitangents, attributeByteCount);
957  }
958  else
959  copiedMesh->bitangents = NULL;
960 
961  if (m->textureCoordinates)
962  {
963  copiedMesh->textureCoordinates = (VuoPoint4d *)malloc(attributeByteCount);
964  memcpy(copiedMesh->textureCoordinates, m->textureCoordinates, attributeByteCount);
965  }
966  else
967  copiedMesh->textureCoordinates = NULL;
968 
969  copiedMesh->elementCount = m->elementCount;
970  if (m->elements)
971  {
972  unsigned long elementByteCount = sizeof(unsigned int)*m->elementCount;
973  copiedMesh->elements = (unsigned int *)malloc(elementByteCount);
974  memcpy(copiedMesh->elements, m->elements, elementByteCount);
975  }
976  else
977  copiedMesh->elements = NULL;
978 
979  copiedMesh->elementAssemblyMethod = m->elementAssemblyMethod;
980  copiedMesh->primitiveSize = m->primitiveSize;
981  copiedMesh->faceCulling = m->faceCulling;
982 
983  memcpy(&copiedMesh->glUpload, &m->glUpload, sizeof(copiedMesh->glUpload));
984  VuoGlPool_retain(copiedMesh->glUpload.combinedBuffer);
985  VuoGlPool_retain(copiedMesh->glUpload.elementBuffer);
986 
987  return (VuoMesh)copiedMesh;
988 }
989 
1001 {
1002  if (!mesh)
1003  return NULL;
1004 
1005  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1006  VuoMesh_internal *copiedMesh = VuoMesh_makeInternal();
1007 
1008  copiedMesh->vertexCount = m->vertexCount;
1009  copiedMesh->elementCount = m->elementCount;
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 
1028 {
1029  if (!mesh)
1030  return;
1031 
1032  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1033  if (m->textureCoordinates)
1034  {
1035  free(m->textureCoordinates);
1036  m->textureCoordinates = NULL;
1037  }
1038 
1039  if (m->glUpload.combinedBuffer && m->glUpload.textureCoordinateOffset)
1040  {
1041  // Clearing `textureCoordinateOffset` may change the stride if it's automatically calculated, so preserve the current stride.
1042  m->glUpload.combinedBufferStride = VuoMesh_getStride(mesh);
1043 
1044  m->glUpload.textureCoordinateOffset = NULL;
1045  // The data is still allocated in GPU RAM as part of combinedBuffer;
1046  // just ignore that (rather than wasting time repacking the buffer).
1047  }
1048 }
1049 
1050 static void VuoMesh_download(VuoMesh_internal *m);
1051 
1061 void VuoMesh_getCPUBuffers(const VuoMesh mesh, unsigned int *vertexCount,
1062  VuoPoint4d **positions, VuoPoint4d **normals, VuoPoint4d **tangents, VuoPoint4d **bitangents, VuoPoint4d **textureCoordinates,
1063  unsigned int *elementCount, unsigned int **elements)
1064 {
1065  if (!mesh)
1066  return;
1067 
1068  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1069  VuoMesh_download(m);
1070  if (vertexCount)
1071  *vertexCount = m->vertexCount;
1072  if (positions)
1073  *positions = m->positions;
1074  if (normals)
1075  *normals = m->normals;
1076  if (tangents)
1077  *tangents = m->tangents;
1078  if (bitangents)
1079  *bitangents = m->bitangents;
1080  if (textureCoordinates)
1081  *textureCoordinates = m->textureCoordinates;
1082  if (elementCount)
1083  *elementCount = m->elementCount;
1084  if (elements)
1085  *elements = m->elements;
1086 }
1087 
1095 void VuoMesh_getGPUBuffers(const VuoMesh mesh, unsigned int *vertexCount,
1096  unsigned int *combinedBuffer,
1097  void **normalOffset, void **tangentOffset, void **bitangentOffset, void **textureCoordinateOffset,
1098  unsigned int *elementCount, unsigned int *elementBuffer)
1099 {
1100  if (!mesh)
1101  return;
1102 
1103  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1104  VuoMesh_upload(m);
1105  if (vertexCount)
1106  *vertexCount = m->vertexCount;
1107  if (combinedBuffer)
1108  *combinedBuffer = m->glUpload.combinedBuffer;
1109  if (normalOffset)
1110  *normalOffset = m->glUpload.normalOffset;
1111  if (tangentOffset)
1112  *tangentOffset = m->glUpload.tangentOffset;
1113  if (bitangentOffset)
1114  *bitangentOffset = m->glUpload.bitangentOffset;
1115  if (textureCoordinateOffset)
1116  *textureCoordinateOffset = m->glUpload.textureCoordinateOffset;
1117  if (elementCount)
1118  *elementCount = m->elementCount;
1119  if (elementBuffer)
1120  *elementBuffer = m->glUpload.elementBuffer;
1121 }
1122 
1129 {
1130  if (!mesh)
1132 
1133  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1134  return m->elementAssemblyMethod;
1135 }
1136 
1143 {
1144  if (!mesh)
1145  return VuoMesh_CullNone;
1146 
1147  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1148  return m->faceCulling;
1149 }
1150 
1156 unsigned int VuoMesh_getFaceCullingGL(const VuoMesh mesh)
1157 {
1158  if (!mesh)
1159  return GL_NONE;
1160 
1161  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1162  if (m->faceCulling == VuoMesh_CullBackfaces)
1163  return GL_BACK;
1164  else if (m->faceCulling == VuoMesh_CullFrontfaces)
1165  return GL_FRONT;
1166  else // if (m->faceCulling == VuoMesh_CullNone)
1167  return GL_NONE;
1168 }
1169 
1176 {
1177  if (!mesh)
1178  return 0;
1179 
1180  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1181  return m->primitiveSize;
1182 }
1183 
1189 unsigned int VuoMesh_getElementBufferSize(const VuoMesh mesh)
1190 {
1191  if (!mesh)
1192  return 0;
1193 
1194  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1195  return m->glUpload.elementBufferSize;
1196 }
1197 
1206 void VuoMesh_setCPUBuffers(VuoMesh mesh, unsigned int vertexCount,
1207  VuoPoint4d *positions, VuoPoint4d *normals, VuoPoint4d *tangents, VuoPoint4d *bitangents, VuoPoint4d *textureCoordinates,
1208  unsigned int elementCount, unsigned int *elements)
1209 {
1210  if (!mesh)
1211  return;
1212 
1213  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1214 
1215  m->vertexCount = vertexCount;
1216 
1217  if (m->positions != positions)
1218  {
1219  free(m->positions);
1220  m->positions = positions;
1221  }
1222 
1223  if (m->normals != normals)
1224  {
1225  free(m->normals);
1226  m->normals = normals;
1227  }
1228 
1229  if (m->tangents != tangents)
1230  {
1231  free(m->tangents);
1232  m->tangents = tangents;
1233  }
1234 
1235  if (m->bitangents != bitangents)
1236  {
1237  free(m->bitangents);
1238  m->bitangents = bitangents;
1239  }
1240 
1241  if (m->textureCoordinates != textureCoordinates)
1242  {
1243  free(m->textureCoordinates);
1244  m->textureCoordinates = textureCoordinates;
1245  }
1246 
1247  m->elementCount = elementCount;
1248 
1249  if (m->elements != elements)
1250  {
1251  free(m->elements);
1252  m->elements = elements;
1253  }
1254 
1255  VuoGlPool_release(VuoGlPool_ArrayBuffer, m->glUpload.combinedBufferSize, m->glUpload.combinedBuffer);
1256  VuoGlPool_release(VuoGlPool_ElementArrayBuffer, m->glUpload.elementBufferSize, m->glUpload.elementBuffer);
1257  m->glUpload.combinedBufferSize = 0;
1258  m->glUpload.combinedBuffer = 0;
1259  m->glUpload.elementBufferSize = 0;
1260  m->glUpload.elementBuffer = 0;
1261 
1262  VuoMesh_upload(m);
1263 }
1264 
1271 {
1272  if (!mesh)
1273  return;
1274 
1275  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1276  m->faceCulling = faceCulling;
1277 }
1278 
1284 void VuoMesh_setPrimitiveSize(VuoMesh mesh, VuoReal primitiveSize)
1285 {
1286  if (!mesh)
1287  return;
1288 
1289  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1290  m->primitiveSize = primitiveSize;
1291 }
1292 
1298 {
1299  if (strcmp(elementAssemblyMethodString,"individualTriangles")==0)
1301  else if (strcmp(elementAssemblyMethodString,"triangleStrip")==0)
1302  return VuoMesh_TriangleStrip;
1303  else if (strcmp(elementAssemblyMethodString,"triangleFan")==0)
1304  return VuoMesh_TriangleFan;
1305  else if (strcmp(elementAssemblyMethodString,"individualLines")==0)
1306  return VuoMesh_IndividualLines;
1307  else if (strcmp(elementAssemblyMethodString,"lineStrip")==0)
1308  return VuoMesh_LineStrip;
1309  else if (strcmp(elementAssemblyMethodString,"points")==0)
1310  return VuoMesh_Points;
1311 
1313 }
1314 
1321 {
1322  switch (elementAssemblyMethod)
1323  {
1325  return "individualTriangles";
1326  case VuoMesh_TriangleStrip:
1327  return "triangleStrip";
1328  case VuoMesh_TriangleFan:
1329  return "triangleFan";
1331  return "individualLines";
1332  case VuoMesh_LineStrip:
1333  return "lineStrip";
1334  case VuoMesh_Points:
1335  return "points";
1336  }
1337 }
1338 
1342 unsigned long VuoMesh_getStride(const VuoMesh mesh)
1343 {
1344  if (!mesh)
1345  return 0;
1346 
1347  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1348  if (m->glUpload.combinedBufferStride)
1349  return m->glUpload.combinedBufferStride;
1350 
1351  int bufferCount = 0;
1352  ++bufferCount; // position
1353  if (m->glUpload.normalOffset)
1354  ++bufferCount;
1355  if (m->glUpload.tangentOffset)
1356  ++bufferCount;
1357  if (m->glUpload.bitangentOffset)
1358  ++bufferCount;
1359  if (m->glUpload.textureCoordinateOffset)
1360  ++bufferCount;
1361 
1362  return sizeof(VuoPoint4d) * bufferCount;
1363 }
1364 
1368 VuoPoint4d *extractElements(CGLContextObj cgl_ctx, VuoMesh_internal *m, unsigned int vertexCount, unsigned int bufferStrideInBytes, unsigned int bufferIndex)
1369 {
1370  unsigned int bufferStrideInFloats = bufferStrideInBytes / sizeof(GLfloat);
1371  GLfloat *feedback = (GLfloat *)malloc(m->glUpload.combinedBufferSize);
1372  glGetBufferSubData(GL_ARRAY_BUFFER, 0, m->glUpload.combinedBufferSize, feedback);
1373 
1374  VuoPoint4d *elements = (VuoPoint4d*)malloc(sizeof(VuoPoint4d)*vertexCount);
1375 
1376  for (int vertex = 0; vertex < vertexCount; vertex++)
1377  {
1378  elements[vertex] = (VuoPoint4d)
1379  {
1380  feedback[vertex*bufferStrideInFloats + 0 + (bufferIndex*4)],
1381  feedback[vertex*bufferStrideInFloats + 1 + (bufferIndex*4)],
1382  feedback[vertex*bufferStrideInFloats + 2 + (bufferIndex*4)],
1383  feedback[vertex*bufferStrideInFloats + 3 + (bufferIndex*4)]
1384  };
1385  }
1386  free(feedback);
1387 
1388  return elements;
1389 }
1390 
1394 static void VuoMesh_download(VuoMesh_internal *m)
1395 {
1396  if (!m->glUpload.combinedBuffer
1397  || (m->positions && m->normals && m->tangents && m->bitangents && m->textureCoordinates))
1398  return;
1399 
1400  unsigned int vertexCount = m->vertexCount;
1401  int stride = VuoMesh_getStride((VuoMesh)m);
1402  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1403  glBindBuffer(GL_ARRAY_BUFFER, m->glUpload.combinedBuffer);
1404 
1405  // @@@ is this necessary?
1406  glFlush();
1407 
1408  int bufferIndex = 0;
1409 
1410  if (!m->positions)
1411  m->positions = extractElements(cgl_ctx, m, vertexCount, stride, bufferIndex++);
1412 
1413  if (!m->normals && m->glUpload.normalOffset)
1414  m->normals = extractElements(cgl_ctx, m, vertexCount, stride, bufferIndex++);
1415 
1416  if (!m->tangents && m->glUpload.tangentOffset)
1417  m->tangents = extractElements(cgl_ctx, m, vertexCount, stride, bufferIndex++);
1418 
1419  if (!m->bitangents && m->glUpload.bitangentOffset)
1420  m->bitangents = extractElements(cgl_ctx, m, vertexCount, stride, bufferIndex++);
1421 
1422  if (!m->textureCoordinates && m->glUpload.textureCoordinateOffset)
1423  m->textureCoordinates = extractElements(cgl_ctx, m, vertexCount, stride, bufferIndex++);
1424 
1425  glBindBuffer(GL_ARRAY_BUFFER, 0);
1426 
1427  if (!m->elements && m->glUpload.elementBuffer)
1428  {
1429  m->elements = malloc(m->glUpload.elementBufferSize);
1430  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->glUpload.elementBuffer);
1431  glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, m->glUpload.elementBufferSize, m->elements);
1432  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1433  }
1434  });
1435 }
1436 
1440 VuoBox VuoMesh_bounds(const VuoMesh mesh, float matrix[16])
1441 {
1442  if (!mesh)
1443  return VuoBox_make((VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0});
1444 
1445  VuoPoint3d min, max;
1446  bool init = false;
1447 
1448  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1449  unsigned int vertexCount = m->vertexCount;
1450 
1451  VuoMesh_download(m);
1452 
1453  if(vertexCount > 0 && !init)
1454  {
1455  min = max = VuoTransform_transformPoint((float*)matrix, VuoPoint3d_make(m->positions[0].x, m->positions[0].y, m->positions[0].z));
1456  init = true;
1457  }
1458 
1459  for(int n = 0; n < vertexCount; n++)
1460  {
1461  VuoPoint3d p = VuoTransform_transformPoint((float*)matrix, VuoPoint3d_make(m->positions[n].x, m->positions[n].y, m->positions[n].z));
1462 
1463  min.x = MIN(p.x, min.x);
1464  min.y = MIN(p.y, min.y);
1465  min.z = MIN(p.z, min.z);
1466 
1467  max.x = MAX(p.x, max.x);
1468  max.y = MAX(p.y, max.y);
1469  max.z = MAX(p.z, max.z);
1470  }
1471 
1472  if(init)
1473  return VuoBox_make((min + max) / (VuoPoint3d)(2.), max - min);
1474  else
1475  return VuoBox_make( (VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0} );
1476 }
1477 
1482 {
1483  if (!mesh)
1484  return false;
1485 
1486  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1487  return m->vertexCount > 0;
1488 }
1489 
1497 {
1498  json_object *o = NULL;
1499 
1500  if (json_object_object_get_ex(js, "pointer", &o))
1501  return (VuoMesh)json_object_get_int64(o);
1502 
1503  return NULL;
1504 }
1505 
1511 {
1512  if (!value)
1513  return NULL;
1514 
1515  json_object *js = json_object_new_object();
1516  json_object_object_add(js, "pointer", json_object_new_int64((int64_t)value));
1517  return js;
1518 }
1519 
1525 {
1526  return VuoMesh_getJson(value);
1527 }
1528 
1533 char *VuoMesh_getSummary(const VuoMesh mesh)
1534 {
1535  if (!mesh)
1536  return strdup("Empty mesh");
1537 
1538  VuoMesh_internal *m = (VuoMesh_internal *)mesh;
1539 
1540  if (!m->vertexCount)
1541  return strdup("Mesh without any vertices");
1542 
1543  unsigned long objectCount = VuoMesh_getSplitPrimitiveCount(mesh);
1544  const char * objectString = "";
1545  const char * assemblyMethod = " (unknown element assembly method)";
1546  if (m->elementAssemblyMethod == VuoMesh_IndividualTriangles)
1547  {
1548  assemblyMethod = ", ";
1549  objectString = "triangle";
1551  }
1552  else if (m->elementAssemblyMethod == VuoMesh_TriangleStrip)
1553  {
1554  assemblyMethod = " in a strip of ";
1555  objectString = "triangle";
1556  }
1557  else if (m->elementAssemblyMethod == VuoMesh_TriangleFan)
1558  {
1559  assemblyMethod = " in a fan of ";
1560  objectString = "triangle";
1561  }
1562  else if (m->elementAssemblyMethod == VuoMesh_IndividualLines)
1563  {
1564  assemblyMethod = ", ";
1565  objectString = "line";
1566  }
1567  else if (m->elementAssemblyMethod == VuoMesh_LineStrip)
1568  {
1569  assemblyMethod = " in a strip of ";
1570  objectString = "line";
1571  }
1572  else if (m->elementAssemblyMethod == VuoMesh_Points)
1573  {
1574  assemblyMethod = ", ";
1575  objectString = "point";
1576  }
1577 
1578  const char * vertexCountString = m->vertexCount==1 ? "vertex" : "vertices";
1579  const char * objectStringPlural = objectCount==1 ? "" : "s";
1580 
1581  char *firstPosition = NULL;
1582  if (m->positions)
1583  firstPosition = VuoText_format("<div>with first position (%s)</div>", VuoPoint4d_getSummary(m->positions[0]));
1584 
1585  char *summary = VuoText_format("<div>%u %s%s%lu %s%s</div>%s<div>%s positions</div><div>%s normals</div><div>%s tangents</div><div>%s bitangents</div><div>%s texture coordinates</div>",
1586  m->vertexCount, vertexCountString, assemblyMethod, objectCount, objectString, objectStringPlural,
1587  firstPosition ? firstPosition : "",
1588  (m->positions || m->glUpload.combinedBuffer ) ? "✓" : "◻",
1589  (m->normals || m->glUpload.normalOffset ) ? "✓" : "◻",
1590  (m->tangents || m->glUpload.tangentOffset ) ? "✓" : "◻",
1591  (m->bitangents || m->glUpload.bitangentOffset ) ? "✓" : "◻",
1592  (m->textureCoordinates || m->glUpload.textureCoordinateOffset) ? "✓" : "◻");
1593 
1594  free(firstPosition);
1595  return summary;
1596 }