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 VuoSubmesh VuoSubmesh_make(unsigned int vertexCount, unsigned int elementCount)
49 {
50  VuoSubmesh sm;
51 
52  sm.vertexCount = vertexCount;
53  sm.positions = (VuoPoint4d *)malloc(sizeof(VuoPoint4d)*sm.vertexCount);
54  sm.normals = (VuoPoint4d *)malloc(sizeof(VuoPoint4d)*sm.vertexCount);
55  sm.tangents = (VuoPoint4d *)malloc(sizeof(VuoPoint4d)*sm.vertexCount);
56  sm.bitangents = (VuoPoint4d *)malloc(sizeof(VuoPoint4d)*sm.vertexCount);
57  sm.textureCoordinates = (VuoPoint4d *)malloc(sizeof(VuoPoint4d)*sm.vertexCount);
58  sm.elementCount = elementCount;
59  sm.elements = (unsigned int *)malloc(sizeof(unsigned int)*sm.elementCount);
61  sm.primitiveSize = 0;
62  sm.faceCullingMode = GL_BACK;
63  memset(&sm.glUpload, 0, sizeof(sm.glUpload));
64 
65  return sm;
66 }
67 
71 VuoSubmesh VuoSubmesh_makeFromBuffers(unsigned int vertexCount,
72  VuoPoint4d *positions, VuoPoint4d *normals, VuoPoint4d *tangents, VuoPoint4d *bitangents, VuoPoint4d *textureCoordinates,
73  unsigned int elementCount, unsigned int *elements, VuoMesh_ElementAssemblyMethod elementAssemblyMethod)
74 {
75  VuoSubmesh sm;
76 
77  sm.vertexCount = vertexCount;
78  sm.positions = positions;
79  sm.normals = normals;
80  sm.tangents = tangents;
81  sm.bitangents = bitangents;
82  sm.textureCoordinates = textureCoordinates;
83  sm.elementCount = elementCount;
84  sm.elements = elements;
85  sm.elementAssemblyMethod = elementAssemblyMethod;
86  sm.primitiveSize = 0;
87  sm.faceCullingMode = GL_BACK;
88  memset(&sm.glUpload, 0, sizeof(sm.glUpload));
89 
90  return sm;
91 }
92 
96 VuoSubmesh VuoSubmesh_makeGl(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)
97 {
98  VuoSubmesh sm;
99 
100  sm.vertexCount = vertexCount;
101  sm.positions = NULL;
102  sm.normals = NULL;
103  sm.tangents = NULL;
104  sm.bitangents = NULL;
105  sm.textureCoordinates = NULL;
106  sm.elementCount = elementCount;
107  sm.elements = NULL;
108  sm.elementAssemblyMethod = elementAssemblyMethod;
109  sm.primitiveSize = 0;
110  sm.faceCullingMode = GL_BACK;
111  sm.glUpload.combinedBuffer = combinedBuffer;
112  sm.glUpload.combinedBufferSize = combinedBufferSize;
113  sm.glUpload.combinedBufferStride = combinedBufferStride;
114  sm.glUpload.normalOffset = normalOffset;
115  sm.glUpload.tangentOffset = tangentOffset;
116  sm.glUpload.bitangentOffset = bitangentOffset;
117  sm.glUpload.textureCoordinateOffset = textureCoordinateOffset;
118  sm.glUpload.elementBuffer = elementBuffer;
119  sm.glUpload.elementBufferSize = elementBufferSize;
120 
121  return sm;
122 }
123 
127 unsigned long VuoSubmesh_getGlMode(VuoSubmesh submesh)
128 {
130  return GL_TRIANGLES;
131  else if (submesh.elementAssemblyMethod == VuoMesh_TriangleStrip)
132  return GL_TRIANGLE_STRIP;
133  else if (submesh.elementAssemblyMethod == VuoMesh_TriangleFan)
134  return GL_TRIANGLE_FAN;
136  return GL_LINES;
137  else if (submesh.elementAssemblyMethod == VuoMesh_LineStrip)
138  return GL_LINE_STRIP;
139  else if (submesh.elementAssemblyMethod == VuoMesh_Points)
140  return GL_POINTS;
141 
142  return GL_TRIANGLES;
143 }
144 
154 {
156  return submesh.elementCount ? submesh.elementCount/3 : submesh.vertexCount/3;
157  else if (submesh.elementAssemblyMethod == VuoMesh_TriangleStrip)
158  return submesh.elementCount ? submesh.elementCount-2 : submesh.vertexCount-2;
159  else if (submesh.elementAssemblyMethod == VuoMesh_TriangleFan)
160  return submesh.elementCount ? submesh.elementCount-2 : submesh.vertexCount-2;
162  return submesh.elementCount ? submesh.elementCount/2 : submesh.vertexCount/2;
163  else if (submesh.elementAssemblyMethod == VuoMesh_LineStrip)
164  return submesh.elementCount ? submesh.elementCount-1 : submesh.vertexCount-1;
165  else if (submesh.elementAssemblyMethod == VuoMesh_Points)
166  return submesh.elementCount ? submesh.elementCount : submesh.vertexCount;
167 
168  return 0;
169 }
170 
180 {
181  if (submesh.elementCount == 0 && submesh.vertexCount == 0)
182  return 0;
183 
185  return submesh.elementCount ? submesh.elementCount : submesh.vertexCount;
186  else if (submesh.elementAssemblyMethod == VuoMesh_TriangleStrip)
187  return submesh.elementCount ? (submesh.elementCount-2)*3 : (submesh.vertexCount-2)*3;
188  else if (submesh.elementAssemblyMethod == VuoMesh_TriangleFan)
189  return submesh.elementCount ? (submesh.elementCount-2)*3 : (submesh.vertexCount-2)*3;
191  return submesh.elementCount ? submesh.elementCount : submesh.vertexCount;
192  else if (submesh.elementAssemblyMethod == VuoMesh_LineStrip)
193  return submesh.elementCount ? (submesh.elementCount-1)*2 : (submesh.vertexCount-1)*2;
194  else if (submesh.elementAssemblyMethod == VuoMesh_Points)
195  return submesh.elementCount ? submesh.elementCount : submesh.vertexCount;
196 
197  return 0;
198 }
199 
209 unsigned long VuoSubmesh_getCompleteElementCount(const VuoSubmesh submesh)
210 {
211  unsigned long elementCount = submesh.elementCount ? submesh.elementCount : submesh.vertexCount;
212 
213  if (elementCount == 0)
214  return 0;
215 
216  // Verify that the input mesh has a valid number of elements.
218  {
219  if (elementCount % 3 != 0)
220  {
221  VUserLog("Warning: VuoMesh_IndividualTriangles requires elementCount %% 3 == 0, but this mesh has %ld. Rounding down.", elementCount);
222  return (elementCount/3)*3;
223  }
224  }
225  else if (submesh.elementAssemblyMethod == VuoMesh_TriangleStrip)
226  {
227  if (elementCount < 3)
228  {
229  VUserLog("Warning: VuoMesh_TriangleStrip requires elementCount >= 3, but this mesh only has %ld.", elementCount);
230  return 0;
231  }
232  }
233  else if (submesh.elementAssemblyMethod == VuoMesh_TriangleFan)
234  {
235  if (elementCount < 3)
236  {
237  VUserLog("Warning: VuoMesh_TriangleFan requires elementCount >= 3, but this mesh only has %ld.", elementCount);
238  return 0;
239  }
240  }
242  {
243  if (elementCount % 2 != 0)
244  {
245  VUserLog("Warning: VuoMesh_IndividualLines requires elementCount %% 2 == 0, but this mesh has %ld. Rounding down.", elementCount);
246  return (elementCount/2)*2;
247  }
248  }
249  else if (submesh.elementAssemblyMethod == VuoMesh_LineStrip)
250  {
251  if (elementCount < 2)
252  {
253  VUserLog("Warning: VuoMesh_LineStrip requires elementCount >= 2, but this mesh only has %ld.", elementCount);
254  return 0;
255  }
256  }
257  else if (submesh.elementAssemblyMethod == VuoMesh_Points)
258  {
259  // Since all points are independent, any number of points is fine.
260  }
261  else
262  {
263  VUserLog("Error: Unknown submesh element assembly method: %d", submesh.elementAssemblyMethod);
264  return 0;
265  }
266 
267  return elementCount;
268 }
269 
273 void VuoMesh_free(void *value)
274 {
275  VuoMesh m = (VuoMesh)value;
276 
277  for (unsigned int i = 0; i < m->submeshCount; ++i)
278  {
279  VuoSubmesh sm = m->submeshes[i];
280  free(sm.positions);
281  free(sm.normals);
282  free(sm.tangents);
283  free(sm.bitangents);
284  free(sm.textureCoordinates);
285  free(sm.elements);
286 
287  VuoGlPool_release(VuoGlPool_ArrayBuffer, sm.glUpload.combinedBufferSize, sm.glUpload.combinedBuffer);
288  VuoGlPool_release(VuoGlPool_ElementArrayBuffer, sm.glUpload.elementBufferSize, sm.glUpload.elementBuffer);
289  }
290 
291  free(m->submeshes);
292  free(m);
293 }
294 
300 VuoMesh VuoMesh_make(unsigned int submeshCount)
301 {
302  if (submeshCount == 0)
303  return NULL;
304 
305  VuoMesh m = (VuoMesh)malloc(submeshCount * sizeof(struct _VuoMesh));
307  m->submeshCount = submeshCount;
308  m->submeshes = (VuoSubmesh *)calloc(submeshCount, sizeof(VuoSubmesh));
309  return m;
310 }
311 
316 {
317  if (!mesh)
318  return;
319 
320  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
321 
322  // Create a temporary Vertex Array Object, so we can bind the buffers in order to upload them.
323  GLuint vertexArray;
324  glGenVertexArrays(1, &vertexArray);
325  glBindVertexArray(vertexArray);
326 
327  // For each mesh item in the sceneobject...
328  for (unsigned long i = 0; i < mesh->submeshCount; ++i)
329  {
330  VuoSubmesh meshItem = mesh->submeshes[i];
331 
332 
333  // Combine the vertex attribute buffers together, so we only have to upload a single one per submesh:
334 
335 
336  // Decide on interleaved positions for vertex attributes.
337  // Needs to be interleaved because glTransformFeedbackVaryings is only able to write to 4 buffers, but we have more than 4 buffers.
338  unsigned int bufferCount = 0;
339 
340  ++bufferCount; // position
341 
342  unsigned int normalOffset = 0; // Offsets are VuoPoint4ds (not bytes).
343  if (meshItem.normals)
344  normalOffset = bufferCount++;
345  mesh->submeshes[i].glUpload.normalOffset = (void *)(normalOffset*sizeof(VuoPoint4d));
346 
347  unsigned int tangentOffset = 0;
348  if (meshItem.tangents)
349  tangentOffset = bufferCount++;
350  mesh->submeshes[i].glUpload.tangentOffset = (void *)(tangentOffset*sizeof(VuoPoint4d));
351 
352  unsigned int bitangentOffset = 0;
353  if (meshItem.bitangents)
354  bitangentOffset = bufferCount++;
355  mesh->submeshes[i].glUpload.bitangentOffset = (void *)(bitangentOffset*sizeof(VuoPoint4d));
356 
357  unsigned int textureCoordinateOffset = 0;
358  if (meshItem.textureCoordinates)
359  textureCoordinateOffset = bufferCount++;
360  mesh->submeshes[i].glUpload.textureCoordinateOffset = (void *)(textureCoordinateOffset*sizeof(VuoPoint4d));
361 
362  unsigned long singleBufferSize = sizeof(VuoPoint4d)*meshItem.vertexCount;
363  VuoPoint4d *combinedData = (VuoPoint4d *)malloc(singleBufferSize*bufferCount);
364 
365 
366  // Combine vertex attributes into the interleaved client-side buffer.
367  for (unsigned long i = 0; i < meshItem.vertexCount; ++i)
368  {
369  combinedData[i*bufferCount] = meshItem.positions[i];
370  if (meshItem.normals)
371  combinedData[i*bufferCount+normalOffset] = meshItem.normals[i];
372  if (meshItem.tangents)
373  combinedData[i*bufferCount+tangentOffset] = meshItem.tangents[i];
374  if (meshItem.bitangents)
375  combinedData[i*bufferCount+bitangentOffset] = meshItem.bitangents[i];
376  if (meshItem.textureCoordinates)
377  combinedData[i*bufferCount+textureCoordinateOffset] = meshItem.textureCoordinates[i];
378  }
379 
380 
381  // Upload the combined buffer.
382  mesh->submeshes[i].glUpload.combinedBufferSize = singleBufferSize*bufferCount;
383  mesh->submeshes[i].glUpload.combinedBuffer = VuoGlPool_use(cgl_ctx, VuoGlPool_ArrayBuffer, mesh->submeshes[i].glUpload.combinedBufferSize);
384  VuoGlPool_retain(mesh->submeshes[i].glUpload.combinedBuffer);
385  glBindBuffer(GL_ARRAY_BUFFER, mesh->submeshes[i].glUpload.combinedBuffer);
386  glBufferSubData(GL_ARRAY_BUFFER, 0, singleBufferSize * bufferCount, combinedData);
387  free(combinedData);
388 
389 
390  // Upload the Element Buffer and add it to the Vertex Array Object
391  mesh->submeshes[i].glUpload.elementBufferSize = sizeof(unsigned int)*meshItem.elementCount;
392  mesh->submeshes[i].glUpload.elementBuffer = VuoGlPool_use(cgl_ctx, VuoGlPool_ElementArrayBuffer, mesh->submeshes[i].glUpload.elementBufferSize);
393  VuoGlPool_retain(mesh->submeshes[i].glUpload.elementBuffer);
394  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->submeshes[i].glUpload.elementBuffer);
395  glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(unsigned int) * meshItem.elementCount, meshItem.elements);
396  }
397 
398  glBindBuffer(GL_ARRAY_BUFFER, 0);
399 // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // handled by glDeleteVertexArrays()
400 // glBindVertexArray(0); // handled by glDeleteVertexArrays()
401  glDeleteVertexArrays(1, &vertexArray);
402 
403  // Prepare for this mesh to be used on a different OpenGL context.
404  glFlushRenderAPPLE();
405 
406  });
407 }
408 
414 {
415  VuoMesh m = VuoMesh_make(1);
416  m->submeshes[0] = submesh;
417  VuoMesh_upload(m);
418  return m;
419 }
420 
426 {
427  VuoMesh m = (VuoMesh)malloc(sizeof(struct _VuoMesh));
429  m->submeshCount = 1;
430  m->submeshes = (VuoSubmesh *)calloc(1, sizeof(VuoSubmesh));
431  m->submeshes[0] = submesh;
432  VuoMesh_upload(m);
433  return m;
434 }
435 
440 {
441  VuoSubmesh sm = VuoSubmesh_make(4, 6);
442 
443  // Positions
444  {
445  sm.positions[0] = VuoPoint4d_make(-.5,-.5,0,1);
446  sm.positions[1] = VuoPoint4d_make( .5,-.5,0,1);
447  sm.positions[2] = VuoPoint4d_make(-.5, .5,0,1);
448  sm.positions[3] = VuoPoint4d_make( .5, .5,0,1);
449  }
450 
451  // Normals
452  {
453  for (int i=0; i<sm.vertexCount; ++i)
454  sm.normals[i] = VuoPoint4d_make(0,0,1,1);
455  }
456 
457  // Tangents
458  {
459  for (int i=0; i<sm.vertexCount; ++i)
460  sm.tangents[i] = VuoPoint4d_make(1,0,0,1);
461  }
462 
463  // Bitangents
464  {
465  for (int i=0; i<sm.vertexCount; ++i)
466  sm.bitangents[i] = VuoPoint4d_make(0,1,0,1);
467  }
468 
469  // Texture Coordinates
470  {
471  sm.textureCoordinates[0] = VuoPoint4d_make(0,0,0,1);
472  sm.textureCoordinates[1] = VuoPoint4d_make(1,0,0,1);
473  sm.textureCoordinates[2] = VuoPoint4d_make(0,1,0,1);
474  sm.textureCoordinates[3] = VuoPoint4d_make(1,1,0,1);
475  }
476 
477  // Triangle elements
479  // Order the elements so that the diagonal edge of each triangle
480  // is last, so that vuo.shader.make.wireframe can optionally omit them.
481  sm.elements[0] = 2;
482  sm.elements[1] = 0;
483  sm.elements[2] = 1;
484  sm.elements[3] = 1;
485  sm.elements[4] = 3;
486  sm.elements[5] = 2;
487 
489 }
490 
497 {
498  unsigned int positionCount = 4;
499  VuoPoint4d *positions = (VuoPoint4d *)malloc(sizeof(VuoPoint4d) * positionCount);
500  positions[0] = VuoPoint4d_make(-.5,-.5,0,1);
501  positions[1] = VuoPoint4d_make( .5,-.5,0,1);
502  positions[2] = VuoPoint4d_make(-.5, .5,0,1);
503  positions[3] = VuoPoint4d_make( .5, .5,0,1);
504 
505  VuoPoint4d *textureCoordinates = (VuoPoint4d *)malloc(sizeof(VuoPoint4d) * positionCount);
506  textureCoordinates[0] = VuoPoint4d_make(0,0,0,1);
507  textureCoordinates[1] = VuoPoint4d_make(1,0,0,1);
508  textureCoordinates[2] = VuoPoint4d_make(0,1,0,1);
509  textureCoordinates[3] = VuoPoint4d_make(1,1,0,1);
510 
511  unsigned int elementCount = 6;
512  unsigned int *elements = (unsigned int *)malloc(sizeof(unsigned int) * elementCount);
513  // Order the elements so that the diagonal edge of each triangle
514  // is last, so that vuo.shader.make.wireframe can optionally omit them.
515  elements[0] = 2;
516  elements[1] = 0;
517  elements[2] = 1;
518  elements[3] = 1;
519  elements[4] = 3;
520  elements[5] = 2;
521 
522  VuoSubmesh sm = VuoSubmesh_makeFromBuffers(positionCount,
523  positions, NULL, NULL, NULL, textureCoordinates,
524  elementCount, elements, VuoMesh_IndividualTriangles);
526 }
527 
532 {
533  VuoSubmesh sm = VuoSubmesh_make(3, 3);
534 
535  // Positions
536  for (int i = 0; i < sm.vertexCount; ++i)
537  {
538  float angle = M_PI/2. + i * 2*M_PI/3.;
539  sm.positions[i] = VuoPoint4d_make(cos(angle)/sqrt(3), sin(angle)/sqrt(3), 0, 1);
540  }
541 
542  // Normals
543  for (int i=0; i<sm.vertexCount; ++i)
544  sm.normals[i] = VuoPoint4d_make(0,0,1,1);
545 
546  // Tangents
547  for (int i=0; i<sm.vertexCount; ++i)
548  sm.tangents[i] = VuoPoint4d_make(1,0,0,1);
549 
550  // Bitangents
551  for (int i=0; i<sm.vertexCount; ++i)
552  sm.bitangents[i] = VuoPoint4d_make(0,1,0,1);
553 
554  // Texture Coordinates
555  sm.textureCoordinates[0] = VuoPoint4d_make(.5,1,0,1);
556  sm.textureCoordinates[1] = VuoPoint4d_make(0,0,0,1);
557  sm.textureCoordinates[2] = VuoPoint4d_make(1,0,0,1);
558 
559  // Triangle Strip elements
561  sm.elements[0] = 0;
562  sm.elements[1] = 1;
563  sm.elements[2] = 2;
564 
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  VuoPoint4d positions[] = (VuoPoint4d[]){
626  // Front
627  {-.5, -.5, .5, 1},
628  { .5, -.5, .5, 1},
629  {-.5, .5, .5, 1},
630  { .5, .5, .5, 1},
631  // Right
632  { .5, -.5, .5, 1},
633  { .5, -.5, -.5, 1},
634  { .5, .5, .5, 1},
635  { .5, .5, -.5, 1},
636  // Bottom
637  {-.5, -.5, -.5, 1},
638  { .5, -.5, -.5, 1},
639  {-.5, -.5, .5, 1},
640  { .5, -.5, .5, 1},
641  // Left
642  {-.5, -.5, -.5, 1},
643  {-.5, -.5, .5, 1},
644  {-.5, .5, -.5, 1},
645  {-.5, .5, .5, 1},
646  // Top
647  {-.5, .5, .5, 1},
648  { .5, .5, .5, 1},
649  {-.5, .5, -.5, 1},
650  { .5, .5, -.5, 1},
651  // Back
652  { .5, -.5, -.5, 1},
653  {-.5, -.5, -.5, 1},
654  { .5, .5, -.5, 1},
655  {-.5, .5, -.5, 1},
656  };
657 
658  VuoPoint4d normals[] = (VuoPoint4d[]){
659  // Front
660  {0, 0, 1, 1},
661  {0, 0, 1, 1},
662  {0, 0, 1, 1},
663  {0, 0, 1, 1},
664  // Right
665  {1, 0, 0, 1},
666  {1, 0, 0, 1},
667  {1, 0, 0, 1},
668  {1, 0, 0, 1},
669  // Bottom
670  {0, -1, 0, 1},
671  {0, -1, 0, 1},
672  {0, -1, 0, 1},
673  {0, -1, 0, 1},
674  // Left
675  {-1, 0, 0, 1},
676  {-1, 0, 0, 1},
677  {-1, 0, 0, 1},
678  {-1, 0, 0, 1},
679  // Top
680  {0, 1, 0, 1},
681  {0, 1, 0, 1},
682  {0, 1, 0, 1},
683  {0, 1, 0, 1},
684  // Back
685  {0, 0, -1, 1},
686  {0, 0, -1, 1},
687  {0, 0, -1, 1},
688  {0, 0, -1, 1},
689  };
690 
691  VuoPoint4d tangents[] = (VuoPoint4d[]){
692  // Front
693  {1, 0, 0, 1},
694  {1, 0, 0, 1},
695  {1, 0, 0, 1},
696  {1, 0, 0, 1},
697  // Right
698  {0, 0, -1, 1},
699  {0, 0, -1, 1},
700  {0, 0, -1, 1},
701  {0, 0, -1, 1},
702  // Bottom
703  {1, 0, 0, 1},
704  {1, 0, 0, 1},
705  {1, 0, 0, 1},
706  {1, 0, 0, 1},
707  // Left
708  {0, 0, 1, 1},
709  {0, 0, 1, 1},
710  {0, 0, 1, 1},
711  {0, 0, 1, 1},
712  // Top
713  {1, 0, 0, 1},
714  {1, 0, 0, 1},
715  {1, 0, 0, 1},
716  {1, 0, 0, 1},
717  // Back
718  {-1, 0, 0, 1},
719  {-1, 0, 0, 1},
720  {-1, 0, 0, 1},
721  {-1, 0, 0, 1},
722  };
723 
724  VuoPoint4d bitangents[] = (VuoPoint4d[]){
725  // Front
726  {0, 1, 0, 1},
727  {0, 1, 0, 1},
728  {0, 1, 0, 1},
729  {0, 1, 0, 1},
730  // Right
731  {0, 1, 0, 1},
732  {0, 1, 0, 1},
733  {0, 1, 0, 1},
734  {0, 1, 0, 1},
735  // Bottom
736  {0, 0, 1, 1},
737  {0, 0, 1, 1},
738  {0, 0, 1, 1},
739  {0, 0, 1, 1},
740  // Left
741  {0, 1, 0, 1},
742  {0, 1, 0, 1},
743  {0, 1, 0, 1},
744  {0, 1, 0, 1},
745  // Top
746  {0, 0, -1, 1},
747  {0, 0, -1, 1},
748  {0, 0, -1, 1},
749  {0, 0, -1, 1},
750  // Back
751  {0, 1, 0, 1},
752  {0, 1, 0, 1},
753  {0, 1, 0, 1},
754  {0, 1, 0, 1},
755  };
756 
757  VuoPoint4d textureCoordinates[] = (VuoPoint4d[]){
758  // Front
759  {0, 0, 0, 1},
760  {1, 0, 0, 1},
761  {0, 1, 0, 1},
762  {1, 1, 0, 1},
763  // Right
764  {0, 0, 0, 1},
765  {1, 0, 0, 1},
766  {0, 1, 0, 1},
767  {1, 1, 0, 1},
768  // Bottom
769  {0, 0, 0, 1},
770  {1, 0, 0, 1},
771  {0, 1, 0, 1},
772  {1, 1, 0, 1},
773  // Left
774  {0, 0, 0, 1},
775  {1, 0, 0, 1},
776  {0, 1, 0, 1},
777  {1, 1, 0, 1},
778  // Top
779  {0, 0, 0, 1},
780  {1, 0, 0, 1},
781  {0, 1, 0, 1},
782  {1, 1, 0, 1},
783  // Back
784  {0, 0, 0, 1},
785  {1, 0, 0, 1},
786  {0, 1, 0, 1},
787  {1, 1, 0, 1},
788  };
789 
790  unsigned int elements[] = (unsigned int[]){
791  // Front
792  2, 0, 1,
793  1, 3, 2,
794  // Right
795  6, 4, 5,
796  5, 7, 6,
797  // Bottom
798  10, 8, 9,
799  9, 11, 10,
800  // Left
801  14, 12, 13,
802  13, 15, 14,
803  // Top
804  18, 16, 17,
805  17, 19, 18,
806  // Back
807  22, 20, 21,
808  21, 23, 22,
809  };
810 
811  VuoSubmesh sm = VuoSubmesh_make(6*4, 6*6);
812  memcpy(sm.positions, positions, sizeof(positions));
813  memcpy(sm.normals, normals, sizeof(normals));
814  memcpy(sm.tangents, tangents, sizeof(tangents));
815  memcpy(sm.bitangents, bitangents, sizeof(bitangents));
816  memcpy(sm.textureCoordinates, textureCoordinates, sizeof(textureCoordinates));
817  memcpy(sm.elements, elements, sizeof(elements));
818 
820 }
821 
828 {
829  static VuoMesh sharedCube;
830  static dispatch_once_t token = 0;
831  dispatch_once(&token, ^{
832  sharedCube = VuoMesh_makeCubeInternal();
833  VuoRetain(sharedCube);
834  });
835  return sharedCube;
836 }
837 
843 {
844  unsigned long count = VuoListGetCount_VuoPoint2d(positions);
845  VuoPoint2d *positionValues = VuoListGetData_VuoPoint2d(positions);
846  VuoPoint4d *positions4d = (VuoPoint4d *)malloc(sizeof(VuoPoint4d) * count);
847  unsigned int *elements = (unsigned int *)malloc(sizeof(unsigned int) * count);
848  for (unsigned long i = 0; i < count; ++i)
849  {
850  VuoPoint2d xy = positionValues[i];
851  positions4d[i] = (VuoPoint4d){xy.x, xy.y, 0, 1};
852  elements[i] = i;
853  }
854 
856  positions4d, NULL, NULL, NULL, NULL,
857  count, elements, elementAssemblyMethod);
858  sm.primitiveSize = primitiveSize;
860 }
861 
867 {
868  unsigned long count = VuoListGetCount_VuoPoint3d(positions);
869  VuoPoint3d *positionValues = VuoListGetData_VuoPoint3d(positions);
870  VuoPoint4d *positions4d = (VuoPoint4d *)malloc(sizeof(VuoPoint4d) * count);
871  unsigned int *elements = (unsigned int *)malloc(sizeof(unsigned int) * count);
872  for (unsigned long i = 0; i < count; ++i)
873  {
874  VuoPoint3d xyz = positionValues[i];
875  positions4d[i] = (VuoPoint4d){xyz.x, xyz.y, xyz.z, 1};
876  elements[i] = i;
877  }
878 
880  positions4d, NULL, NULL, NULL, NULL,
881  count, elements, elementAssemblyMethod);
882  sm.primitiveSize = primitiveSize;
884 }
885 
891 {
892  if (!mesh)
893  return NULL;
894 
895  VuoMesh copiedMesh = VuoMesh_make(mesh->submeshCount);
896  for (unsigned int i = 0; i < mesh->submeshCount; ++i)
897  {
898  VuoSubmesh submesh = mesh->submeshes[i];
899  VuoSubmesh copiedSubmesh;
900 
901  copiedSubmesh.vertexCount = submesh.vertexCount;
902  unsigned long attributeByteCount = sizeof(VuoPoint4d)*copiedSubmesh.vertexCount;
903 
904  if (submesh.positions)
905  {
906  copiedSubmesh.positions = (VuoPoint4d *)malloc(attributeByteCount);
907  memcpy(copiedSubmesh.positions, submesh.positions, attributeByteCount);
908  }
909  else
910  copiedSubmesh.positions = NULL;
911 
912  if (submesh.normals)
913  {
914  copiedSubmesh.normals = (VuoPoint4d *)malloc(attributeByteCount);
915  memcpy(copiedSubmesh.normals, submesh.normals, attributeByteCount);
916  }
917  else
918  copiedSubmesh.normals = NULL;
919 
920  if (submesh.tangents)
921  {
922  copiedSubmesh.tangents = (VuoPoint4d *)malloc(attributeByteCount);
923  memcpy(copiedSubmesh.tangents, submesh.tangents, attributeByteCount);
924  }
925  else
926  copiedSubmesh.tangents = NULL;
927 
928  if (submesh.bitangents)
929  {
930  copiedSubmesh.bitangents = (VuoPoint4d *)malloc(attributeByteCount);
931  memcpy(copiedSubmesh.bitangents, submesh.bitangents, attributeByteCount);
932  }
933  else
934  copiedSubmesh.bitangents = NULL;
935 
936  if (submesh.textureCoordinates)
937  {
938  copiedSubmesh.textureCoordinates = (VuoPoint4d *)malloc(attributeByteCount);
939  memcpy(copiedSubmesh.textureCoordinates, submesh.textureCoordinates, attributeByteCount);
940  }
941  else
942  copiedSubmesh.textureCoordinates = NULL;
943 
944  copiedSubmesh.elementCount = submesh.elementCount;
945  if (submesh.elements)
946  {
947  unsigned long elementByteCount = sizeof(unsigned int)*submesh.elementCount;
948  copiedSubmesh.elements = (unsigned int *)malloc(elementByteCount);
949  memcpy(copiedSubmesh.elements, submesh.elements, elementByteCount);
950  }
951  else
952  copiedSubmesh.elements = NULL;
953 
954  copiedSubmesh.elementAssemblyMethod = submesh.elementAssemblyMethod;
955  copiedSubmesh.primitiveSize = submesh.primitiveSize;
956  copiedSubmesh.faceCullingMode = submesh.faceCullingMode;
957 
958  memcpy(&copiedSubmesh.glUpload, &submesh.glUpload, sizeof(copiedSubmesh.glUpload));
959  VuoGlPool_retain(copiedSubmesh.glUpload.combinedBuffer);
960  VuoGlPool_retain(copiedSubmesh.glUpload.elementBuffer);
961 
962  copiedMesh->submeshes[i] = copiedSubmesh;
963  }
964  return copiedMesh;
965 }
966 
978 {
979  if (!mesh)
980  return NULL;
981 
982  VuoMesh copiedMesh = VuoMesh_make(mesh->submeshCount);
983  for (unsigned int i = 0; i < mesh->submeshCount; ++i)
984  {
985  VuoSubmesh submesh = mesh->submeshes[i];
986  VuoSubmesh copiedSubmesh;
987 
988  copiedSubmesh.vertexCount = submesh.vertexCount;
989  copiedSubmesh.positions = NULL;
990  copiedSubmesh.normals = NULL;
991  copiedSubmesh.tangents = NULL;
992  copiedSubmesh.bitangents = NULL;
993  copiedSubmesh.textureCoordinates = NULL;
994 
995  copiedSubmesh.elementCount = submesh.elementCount;
996  copiedSubmesh.elements = NULL;
997 
998  copiedSubmesh.elementAssemblyMethod = submesh.elementAssemblyMethod;
999  copiedSubmesh.primitiveSize = submesh.primitiveSize;
1000  copiedSubmesh.faceCullingMode = submesh.faceCullingMode;
1001 
1002  memcpy(&copiedSubmesh.glUpload, &submesh.glUpload, sizeof(copiedSubmesh.glUpload));
1003  VuoGlPool_retain(copiedSubmesh.glUpload.combinedBuffer);
1004  VuoGlPool_retain(copiedSubmesh.glUpload.elementBuffer);
1005 
1006  copiedMesh->submeshes[i] = copiedSubmesh;
1007  }
1008  return copiedMesh;
1009 }
1010 
1016 {
1017  if (strcmp(elementAssemblyMethodString,"individualTriangles")==0)
1019  else if (strcmp(elementAssemblyMethodString,"triangleStrip")==0)
1020  return VuoMesh_TriangleStrip;
1021  else if (strcmp(elementAssemblyMethodString,"triangleFan")==0)
1022  return VuoMesh_TriangleFan;
1023  else if (strcmp(elementAssemblyMethodString,"individualLines")==0)
1024  return VuoMesh_IndividualLines;
1025  else if (strcmp(elementAssemblyMethodString,"lineStrip")==0)
1026  return VuoMesh_LineStrip;
1027  else if (strcmp(elementAssemblyMethodString,"points")==0)
1028  return VuoMesh_Points;
1029 
1031 }
1032 
1039 {
1040  switch (elementAssemblyMethod)
1041  {
1043  return "individualTriangles";
1044  case VuoMesh_TriangleStrip:
1045  return "triangleStrip";
1046  case VuoMesh_TriangleFan:
1047  return "triangleFan";
1049  return "individualLines";
1050  case VuoMesh_LineStrip:
1051  return "lineStrip";
1052  case VuoMesh_Points:
1053  return "points";
1054  }
1055 }
1056 
1060 unsigned long VuoSubmesh_getStride(const VuoSubmesh submesh)
1061 {
1062  if (submesh.glUpload.combinedBufferStride)
1063  return submesh.glUpload.combinedBufferStride;
1064 
1065  int bufferCount = 0;
1066  ++bufferCount; // position
1067  if (submesh.glUpload.normalOffset)
1068  ++bufferCount;
1069  if (submesh.glUpload.tangentOffset)
1070  ++bufferCount;
1071  if (submesh.glUpload.bitangentOffset)
1072  ++bufferCount;
1073  if (submesh.glUpload.textureCoordinateOffset)
1074  ++bufferCount;
1075 
1076  return sizeof(VuoPoint4d) * bufferCount;
1077 }
1078 
1082 VuoPoint4d *extractElements(CGLContextObj cgl_ctx, VuoSubmesh *submesh, unsigned int vertexCount, unsigned int bufferStrideInBytes, unsigned int bufferIndex)
1083 {
1084  unsigned int bufferStrideInFloats = bufferStrideInBytes / sizeof(GLfloat);
1085  GLfloat *feedback = (GLfloat *)malloc(submesh->glUpload.combinedBufferSize);
1086  glGetBufferSubData(GL_ARRAY_BUFFER, 0, submesh->glUpload.combinedBufferSize, feedback);
1087 
1088  VuoPoint4d *elements = (VuoPoint4d*)malloc(sizeof(VuoPoint4d)*vertexCount);
1089 
1090  for (int vertex = 0; vertex < vertexCount; vertex++)
1091  {
1092  elements[vertex] = (VuoPoint4d)
1093  {
1094  feedback[vertex*bufferStrideInFloats + 0 + (bufferIndex*4)],
1095  feedback[vertex*bufferStrideInFloats + 1 + (bufferIndex*4)],
1096  feedback[vertex*bufferStrideInFloats + 2 + (bufferIndex*4)],
1097  feedback[vertex*bufferStrideInFloats + 3 + (bufferIndex*4)]
1098  };
1099  }
1100  free(feedback);
1101 
1102  return elements;
1103 }
1104 
1105 #ifndef DOXYGEN
1106 
1109 void VuoSubmeshMesh_download(VuoSubmesh *submesh) __attribute__ ((deprecated("VuoSubmeshMesh_download is deprecated, please use VuoSubmesh_download instead.")));
1110 void VuoSubmeshMesh_download(VuoSubmesh *submesh)
1111 {
1112  VuoSubmesh_download(submesh);
1113 }
1114 #endif
1115 
1122 {
1123  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1124 
1125  unsigned int vertexCount = submesh->vertexCount;
1126 
1127  // Attach the input combinedBuffer for rendering.
1128  glBindBuffer(GL_ARRAY_BUFFER, submesh->glUpload.combinedBuffer);
1129 
1130  int stride = VuoSubmesh_getStride(*submesh);
1131 
1132  glFlush();
1133 
1134  int bufferIndex = 0;
1135 
1136  if (!submesh->positions)
1137  submesh->positions = extractElements(cgl_ctx, submesh, vertexCount, stride, bufferIndex++);
1138 
1139  if (!submesh->normals && submesh->glUpload.normalOffset)
1140  submesh->normals = extractElements(cgl_ctx, submesh, vertexCount, stride, bufferIndex++);
1141 
1142  if (!submesh->tangents && submesh->glUpload.tangentOffset)
1143  submesh->tangents = extractElements(cgl_ctx, submesh, vertexCount, stride, bufferIndex++);
1144 
1145  if (!submesh->bitangents && submesh->glUpload.bitangentOffset)
1146  submesh->bitangents = extractElements(cgl_ctx, submesh, vertexCount, stride, bufferIndex++);
1147 
1148  if (!submesh->textureCoordinates && submesh->glUpload.textureCoordinateOffset)
1149  submesh->textureCoordinates = extractElements(cgl_ctx, submesh, vertexCount, stride, bufferIndex++);
1150 
1151  glBindBuffer(GL_ARRAY_BUFFER, 0);
1152 
1153  if (submesh->glUpload.elementBuffer && !submesh->elements)
1154  {
1155  submesh->elements = malloc(submesh->glUpload.elementBufferSize);
1156  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, submesh->glUpload.elementBuffer);
1157  glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, submesh->glUpload.elementBufferSize, submesh->elements);
1158  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1159  }
1160  });
1161 }
1162 
1166 VuoBox VuoMesh_bounds(const VuoMesh v, float matrix[16])
1167 {
1168  VuoPoint3d min, max;
1169  bool init = false;
1170 
1171  for(int i = 0; i < v->submeshCount; i++)
1172  {
1173  unsigned int vertexCount = v->submeshes[i].vertexCount;
1174 
1175  if(v->submeshes[i].positions == NULL)
1177 
1178  if(vertexCount > 0 && !init)
1179  {
1180  min = max = VuoTransform_transformPoint((float*)matrix, VuoPoint3d_make(v->submeshes[i].positions[0].x, v->submeshes[i].positions[0].y, v->submeshes[i].positions[0].z));
1181  init = true;
1182  }
1183 
1184  for(int n = 0; n < vertexCount; n++)
1185  {
1186  VuoPoint3d p = VuoTransform_transformPoint((float*)matrix, VuoPoint3d_make(v->submeshes[i].positions[n].x, v->submeshes[i].positions[n].y, v->submeshes[i].positions[n].z));
1187 
1188  min.x = MIN(p.x, min.x);
1189  min.y = MIN(p.y, min.y);
1190  min.z = MIN(p.z, min.z);
1191 
1192  max.x = MAX(p.x, max.x);
1193  max.y = MAX(p.y, max.y);
1194  max.z = MAX(p.z, max.z);
1195  }
1196  }
1197 
1198  if(init)
1199  return VuoBox_make((min + max) / (VuoPoint3d)(2.), max - min);
1200  else
1201  return VuoBox_make( (VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0} );
1202 }
1203 
1208 {
1209  if (!mesh)
1210  return false;
1211 
1212  for (unsigned long i = 0; i < mesh->submeshCount; ++i)
1213  {
1214  VuoSubmesh meshItem = mesh->submeshes[i];
1215  if (meshItem.vertexCount > 0)
1216  return true;
1217  }
1218 
1219  return false;
1220 }
1221 
1229 {
1230  json_object *o = NULL;
1231 
1232  if (json_object_object_get_ex(js, "pointer", &o))
1233  return (VuoMesh)json_object_get_int64(o);
1234 
1235  return NULL;
1236 }
1237 
1243 {
1244  if (!value)
1245  return NULL;
1246 
1247  json_object *js = json_object_new_object();
1248  json_object_object_add(js, "pointer", json_object_new_int64((int64_t)value));
1249  return js;
1250 }
1251 
1257 {
1258  return VuoMesh_getJson(value);
1259 }
1260 
1271 {
1272  if (value.vertexCount == 0)
1273  {
1274  char * zero = strdup("0 vertices");
1275  return zero;
1276  }
1277 
1278  unsigned long objectCount = VuoSubmesh_getSplitPrimitiveCount(value);
1279  const char * objectString = "";
1280  const char * assemblyMethod = " (unknown element assembly method)";
1282  {
1283  assemblyMethod = ", ";
1284  objectString = "triangle";
1286  }
1287  else if (value.elementAssemblyMethod == VuoMesh_TriangleStrip)
1288  {
1289  assemblyMethod = " in a strip of ";
1290  objectString = "triangle";
1291  }
1292  else if (value.elementAssemblyMethod == VuoMesh_TriangleFan)
1293  {
1294  assemblyMethod = " in a fan of ";
1295  objectString = "triangle";
1296  }
1298  {
1299  assemblyMethod = ", ";
1300  objectString = "line";
1301  }
1302  else if (value.elementAssemblyMethod == VuoMesh_LineStrip)
1303  {
1304  assemblyMethod = " in a strip of ";
1305  objectString = "line";
1306  }
1307  else if (value.elementAssemblyMethod == VuoMesh_Points)
1308  {
1309  assemblyMethod = ", ";
1310  objectString = "point";
1311  }
1312 
1313  const char * vertexCountString = value.vertexCount==1 ? "vertex" : "vertices";
1314  const char * objectStringPlural = objectCount==1 ? "" : "s";
1315 
1316  char *firstPosition = NULL;
1317  if (value.positions)
1318  firstPosition = VuoText_format("<div>with first position (%s)</div>", VuoPoint4d_getSummary(value.positions[0]));
1319 
1320  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>",
1321  value.vertexCount, vertexCountString, assemblyMethod, objectCount, objectString, objectStringPlural,
1322  firstPosition ? firstPosition : "",
1323  (value.positions || value.glUpload.combinedBuffer ) ? "✓" : "◻",
1324  (value.normals || value.glUpload.normalOffset ) ? "✓" : "◻",
1325  (value.tangents || value.glUpload.tangentOffset ) ? "✓" : "◻",
1326  (value.bitangents || value.glUpload.bitangentOffset ) ? "✓" : "◻",
1327  (value.textureCoordinates || value.glUpload.textureCoordinateOffset) ? "✓" : "◻");
1328 
1329  free(firstPosition);
1330  return summary;
1331 }
1332 
1337 char * VuoMesh_getSummary(const VuoMesh value)
1338 {
1339  if (!value || value->submeshCount == 0)
1340  return strdup("Empty mesh");
1341 
1342  char *prefix = "Mesh containing: <ul>";
1343  unsigned int prefixBytes = strlen(prefix);
1344 
1345  const int maxSubmeshCount = 20;
1346  unsigned int submeshCount = MIN(value->submeshCount, maxSubmeshCount);
1347  char **submeshes = (char **)malloc(submeshCount * sizeof(char *));
1348  unsigned int submeshBytes = 0;
1349  for (unsigned int i = 0; i < submeshCount; ++i)
1350  {
1351  char *s = VuoSubmesh_getSummary(value->submeshes[i]);
1352  submeshes[i] = (char *)malloc((strlen(s) + 4 + 5 + 1));
1353  submeshes[i][0] = 0;
1354  strncat(submeshes[i], "<li>", 4);
1355  strncat(submeshes[i], s, strlen(s));
1356  strncat(submeshes[i], "</li>", 5);
1357  submeshBytes += strlen(submeshes[i]);
1358  free(s);
1359  }
1360 
1361  char *suffix = (value->submeshCount > maxSubmeshCount ? "<li>...</li></ul>" : "</ul>");
1362  unsigned int suffixBytes = strlen(suffix);
1363 
1364  char *summary = (char *)malloc(prefixBytes + submeshBytes + suffixBytes + 1);
1365  summary[0] = 0;
1366  strncat(summary, prefix, prefixBytes);
1367  for (unsigned int i = 0; i < submeshCount; ++i)
1368  strncat(summary, submeshes[i], strlen(submeshes[i]));
1369  strncat(summary, suffix, suffixBytes);
1370 
1371  for (unsigned int i = 0; i < submeshCount; ++i)
1372  free(submeshes[i]);
1373  free(submeshes);
1374 
1375  return summary;
1376 }