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