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