Vuo 2.4.4
Loading...
Searching...
No Matches
VuoSceneObject.cc
Go to the documentation of this file.
1
10#include <list>
11
13#include <OpenGL/CGLMacro.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include "VuoSceneObject.h"
18#include "VuoMeshUtility.h"
19
21#ifdef VUO_COMPILER
22extern "C"
23{
25 "title" : "Scene Object",
26 "description" : "A 3D Object: visible (mesh), or virtual (group, light, camera).",
27 "keywords" : [ ],
28 "version" : "1.0.0",
29 "dependencies" : [
30 "csgjs",
31 "VuoBlendMode",
32 "VuoBoolean",
33 "VuoCubemap",
34 "VuoFont",
35 "VuoMesh",
36 "VuoMeshUtility",
37 "VuoPoint3d",
38 "VuoShader",
39 "VuoText",
40 "VuoTransform",
41 "VuoList_VuoImage",
42 "VuoList_VuoSceneObject"
43 ]
44 });
45}
46#endif
48
56typedef struct
57{
59
60 // Data for all scene objects
61 uint64_t id;
62 VuoText name;
63 VuoTransform transform;
64
65 // Mesh
66 VuoMesh mesh;
67 VuoShader shader;
68 bool isRealSize;
69 bool preservePhysicalSize;
70 VuoBlendMode blendMode;
71
72 union
73 {
74 VuoList_VuoSceneObject childObjects;
75
76 struct
77 {
78 float fieldOfView;
79 float width;
80 float distanceMin;
81 float distanceMax;
82 float confocalDistance;
83 float intraocularDistance;
84 float vignetteWidth;
85 float vignetteSharpness;
86 } camera;
87
88 struct
89 {
90 VuoColor color;
91 float brightness;
92 float range;
93 float cone;
94 float sharpness;
95 } light;
96
97 struct
98 {
99 VuoText text;
100 VuoFont font;
101 bool scaleWithScene;
102 float wrapWidth;
103 } text;
104 };
105} VuoSceneObject_internal;
106
112{
113 static uint64_t id = 0;
114 return __sync_add_and_fetch(&id, 1);
115}
116
122void VuoSceneObject_free(void *sceneObject)
123{
124 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
125
126 VuoRelease(so->name);
127 VuoRelease(so->mesh);
128 VuoRelease(so->shader);
129 if (so->type == VuoSceneObjectSubType_Group)
130 VuoRelease(so->childObjects);
131 else if (so->type == VuoSceneObjectSubType_Text)
132 {
133 VuoRelease(so->text.text);
134 VuoFont_release(so->text.font);
135 }
136
137 free(so);
138}
139
144{
145 VuoSceneObject_internal *so = (VuoSceneObject_internal *)calloc(1, sizeof(VuoSceneObject_internal));
147
148 so->id = 0;
149 so->type = VuoSceneObjectSubType_Empty;
150
151 so->mesh = NULL;
152 so->shader = NULL;
153 so->isRealSize = false;
154 so->preservePhysicalSize = false;
155 so->blendMode = VuoBlendMode_Normal;
156
157 so->name = NULL;
158 so->transform = VuoTransform_makeIdentity();
159
160 return (VuoSceneObject)so;
161}
162
167{
168 VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
169
170 so->type = VuoSceneObjectSubType_Group;
171
173
174 so->transform = transform;
175
176 return (VuoSceneObject)so;
177}
178
183{
184 VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
185
186 so->type = VuoSceneObjectSubType_Mesh;
187
189
190 if (mesh && !shader)
192 else
194
195 so->transform = transform;
196
197 return (VuoSceneObject)so;
198}
199
214VuoSceneObject VuoSceneObject_makeQuad(VuoShader shader, VuoPoint3d center, VuoPoint3d rotation, VuoReal width, VuoReal height)
215{
218 shader,
220 center,
221 VuoPoint3d_multiply(rotation, M_PI/180.),
222 VuoPoint3d_make(width,height,1)
223 ));
224}
225
240VuoSceneObject VuoSceneObject_makeQuadWithNormals(VuoShader shader, VuoPoint3d center, VuoPoint3d rotation, VuoReal width, VuoReal height)
241{
244 shader,
246 center,
247 VuoPoint3d_multiply(rotation, M_PI/180.),
248 VuoPoint3d_make(width,height,1)
249 ));
250}
251
257VuoSceneObject VuoSceneObject_makeImage(VuoImage image, VuoPoint3d center, VuoPoint3d rotation, VuoReal size, VuoOrientation fixed, VuoReal alpha)
258{
259 if (!image)
260 return nullptr;
261
262 float width, height;
263 if (fixed == VuoOrientation_Horizontal)
264 {
265 width = size;
266 height = image->pixelsHigh * size / image->pixelsWide;
267 }
268 else
269 {
270 width = image->pixelsWide * size / image->pixelsHigh;
271 height = size;
272 }
273
275 VuoShader_makeUnlitImageShader(image, alpha),
276 center,
277 rotation,
278 width,
279 height
280 );
281
282 return object;
283}
284
290VuoSceneObject VuoSceneObject_makeLitImage(VuoImage image, VuoPoint3d center, VuoPoint3d rotation, VuoReal size, VuoOrientation fixed, VuoReal alpha, VuoColor highlightColor, VuoReal shininess)
291{
292 if (!image)
293 return nullptr;
294
295 float width, height;
296 if (fixed == VuoOrientation_Horizontal)
297 {
298 width = size;
299 height = image->pixelsHigh * size / image->pixelsWide;
300 }
301 else
302 {
303 width = image->pixelsWide * size / image->pixelsHigh;
304 height = size;
305 }
306
308 VuoShader_makeLitImageShader(image, alpha, highlightColor, shininess),
309 center,
310 rotation,
311 width,
312 height
313 );
314}
315
319VuoSceneObject VuoSceneObject_makeCube(VuoTransform transform, VuoShader frontShader, VuoShader leftShader, VuoShader rightShader, VuoShader backShader, VuoShader topShader, VuoShader bottomShader)
320{
322
323 VuoMesh quadMesh = VuoMesh_makeQuad();
324
325 // Front Face
326 {
328 quadMesh,
329 frontShader,
331 VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
332 }
333
334 // Left Face
335 {
337 quadMesh,
338 leftShader,
340 VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
341 }
342
343 // Right Face
344 {
346 quadMesh,
347 rightShader,
349 VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
350 }
351
352 // Back Face
353 {
355 quadMesh,
356 backShader,
358 VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
359 }
360
361 // Top Face
362 {
364 quadMesh,
365 topShader,
367 VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
368 }
369
370 // Bottom Face
371 {
373 quadMesh,
374 bottomShader,
376 VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
377 }
378
379 return VuoSceneObject_makeGroup(cubeChildObjects, transform);
380}
381
382
389{
390 return VuoSceneObject_makeMesh(VuoMesh_makeCube(), shader, transform);
391}
392
402
412
422
438
445 VuoShader front, VuoShader left, VuoShader right, VuoShader back, VuoShader top, VuoShader bottom)
446{
448
449 unsigned int _rows = MAX(2, MIN(512, rows));
450 unsigned int _columns = MAX(2, MIN(512, columns));
451 unsigned int _slices = MAX(2, MIN(512, slices));
452
453 VuoMesh frontBackMesh = VuoMesh_makePlane(_columns, _rows);
454 VuoMesh leftRightMesh = VuoMesh_makePlane(_slices, _rows);
455 VuoMesh topBottomMesh = VuoMesh_makePlane(_columns, _slices);
456
457 // Front Face
458 {
460 frontBackMesh,
461 front,
463 VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
464 }
465
466 // Left Face
467 {
469 leftRightMesh,
470 left,
472 VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
473 }
474
475 // Right Face
476 {
478 leftRightMesh,
479 right,
481 VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
482 }
483
484 // Back Face
485 {
487 frontBackMesh,
488 back,
490 VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
491 }
492
493 // Top Face
494 {
496 topBottomMesh,
497 top,
499 VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
500 }
501
502 // Bottom Face
503 {
505 topBottomMesh,
506 bottom,
508 VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
509 }
510
511 return VuoSceneObject_makeGroup(cubeChildObjects, transform);
512}
513
522VuoSceneObject VuoSceneObject_makeText(VuoText text, VuoFont font, VuoBoolean scaleWithScene, float wrapWidth)
523{
524 VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
525 so->type = VuoSceneObjectSubType_Text;
528 so->text.scaleWithScene = scaleWithScene;
529 so->text.wrapWidth = wrapWidth;
530 return (VuoSceneObject)so;
531}
532
536VuoSceneObject VuoSceneObject_makePerspectiveCamera(VuoText name, VuoTransform transform, float fieldOfView, float distanceMin, float distanceMax)
537{
538 VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
539 so->type = VuoSceneObjectSubType_PerspectiveCamera;
541 so->transform = transform;
542 so->camera.fieldOfView = fieldOfView;
543 so->camera.distanceMin = distanceMin;
544 so->camera.distanceMax = distanceMax;
545 return (VuoSceneObject)so;
546}
547
551VuoSceneObject VuoSceneObject_makeStereoCamera(VuoText name, VuoTransform transform, VuoReal fieldOfView, VuoReal distanceMin, VuoReal distanceMax, VuoReal confocalDistance, VuoReal intraocularDistance)
552{
553 VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
554 so->type = VuoSceneObjectSubType_StereoCamera;
556 so->transform = transform;
557 so->camera.fieldOfView = fieldOfView;
558 so->camera.distanceMin = distanceMin;
559 so->camera.distanceMax = distanceMax;
560 so->camera.confocalDistance = confocalDistance;
561 so->camera.intraocularDistance = intraocularDistance;
562 return (VuoSceneObject)so;
563}
564
568VuoSceneObject VuoSceneObject_makeOrthographicCamera(VuoText name, VuoTransform transform, float width, float distanceMin, float distanceMax)
569{
570 VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
571 so->type = VuoSceneObjectSubType_OrthographicCamera;
573 so->transform = transform;
574 so->camera.width = width;
575 so->camera.distanceMin = distanceMin;
576 so->camera.distanceMax = distanceMax;
577 return (VuoSceneObject)so;
578}
579
583VuoSceneObject VuoSceneObject_makeFisheyeCamera(VuoText name, VuoTransform transform, VuoReal fieldOfView, float distanceMin, float distanceMax, VuoReal vignetteWidth, VuoReal vignetteSharpness)
584{
585 VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
586 so->type = VuoSceneObjectSubType_FisheyeCamera;
588 so->transform = transform;
589 so->camera.fieldOfView = fieldOfView;
590
591 // "Realtime Dome Imaging and Interaction" by Bailey/Clothier/Gebbie 2006
592 // https://web.archive.org/web/20090116013458/http://web.engr.oregonstate.edu/~mjb/WebMjb/Papers/asmedome.pdf
593 // (which Vuo's fisheye camera is based on) specifies an underlying orthographic projection
594 // with clip planes z=0 and z=1000, but doesn't specify where those numbers came from.
595 // Instead, allow user-configurable Z clip planes, like we do for other cameras.
596 so->camera.distanceMin = distanceMin;
597 so->camera.distanceMax = distanceMax;
598
599 so->camera.vignetteWidth = vignetteWidth;
600 so->camera.vignetteSharpness = vignetteSharpness;
601
602 return (VuoSceneObject)so;
603}
604
609{
611 VuoPoint3d_make(0,0,1),
612 VuoPoint3d_make(0,0,0),
613 VuoPoint3d_make(1,1,1)
614 );
616 VuoText_make("default camera"),
617 transform,
618 90,
619 0.1,
620 10.0
621 );
622}
623
633bool VuoSceneObject_find(VuoSceneObject sceneObject, VuoText nameToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
634{
635 if (!sceneObject)
636 return false;
637
638 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
639
640 if (VuoText_areEqual(so->name, nameToMatch))
641 {
642 *foundObject = (VuoSceneObject)so;
643 return true;
644 }
645
646 if (so->type == VuoSceneObjectSubType_Group)
647 {
648 VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
649
650 unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
651 for (unsigned long i = 1; i <= childObjectCount; ++i)
652 {
653 VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
654 if (VuoSceneObject_find(childObject, nameToMatch, ancestorObjects, foundObject))
655 return true;
656 }
657
659 }
660
661 return false;
662}
663
674bool VuoSceneObject_findById(VuoSceneObject sceneObject, uint64_t idToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
675{
676 if (!sceneObject)
677 return false;
678
679 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
680
681 if (so->id == idToMatch)
682 {
683 *foundObject = (VuoSceneObject)so;
684 return true;
685 }
686
687 if (so->type == VuoSceneObjectSubType_Group)
688 {
689 VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
690
691 unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
692 for (unsigned long i = 1; i <= childObjectCount; ++i)
693 {
694 VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
695 if (VuoSceneObject_findById(childObject, idToMatch, ancestorObjects, foundObject))
696 return true;
697 }
698
700 }
701
702 return false;
703}
704
716{
717 if (!sceneObject)
718 return false;
719
720 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
721
722 if (so->type == typeToMatch)
723 {
724 *foundObject = (VuoSceneObject)so;
725 return true;
726 }
727
728 if (so->type == VuoSceneObjectSubType_Group)
729 {
730 VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
731
732 unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
733
734 for (unsigned long i = 1; i <= childObjectCount; ++i)
735 {
736 VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
737
738 if (VuoSceneObject_findWithType(childObject, typeToMatch, ancestorObjects, foundObject))
739 return true;
740 }
741
743 }
744
745 return false;
746}
747
756bool VuoSceneObject_findCamera(VuoSceneObject sceneObject, VuoText nameToMatch, VuoSceneObject *foundCamera)
757{
758 if (!sceneObject)
759 return false;
760
761 __block bool didFindCamera = false;
762 VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
763 VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
764 if ((co->type == VuoSceneObjectSubType_PerspectiveCamera
765 || co->type == VuoSceneObjectSubType_StereoCamera
766 || co->type == VuoSceneObjectSubType_OrthographicCamera
767 || co->type == VuoSceneObjectSubType_FisheyeCamera)
768 && (!nameToMatch || (co->name && nameToMatch && strstr(co->name, nameToMatch))))
769 {
770 *foundCamera = currentObject;
771 VuoSceneObject_setTransform(*foundCamera, VuoTransform_makeFromMatrix4x4(modelviewMatrix));
772 didFindCamera = true;
773 return false;
774 }
775 return true;
776 });
777
778 return didFindCamera;
779}
780
785{
786 if (!sceneObject)
787 return false;
788
789 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
790 return so->type != VuoSceneObjectSubType_Empty;
791}
792
797{
798 if (strcmp(typeString,"empty")==0)
799 return VuoSceneObjectSubType_Empty;
800 else if (strcmp(typeString,"group")==0)
801 return VuoSceneObjectSubType_Group;
802 else if (strcmp(typeString,"mesh")==0)
803 return VuoSceneObjectSubType_Mesh;
804 else if (strcmp(typeString,"camera-perspective")==0)
805 return VuoSceneObjectSubType_PerspectiveCamera;
806 else if (strcmp(typeString,"camera-stereo")==0)
807 return VuoSceneObjectSubType_StereoCamera;
808 else if (strcmp(typeString,"camera-orthographic")==0)
809 return VuoSceneObjectSubType_OrthographicCamera;
810 else if (strcmp(typeString,"camera-fisheye")==0)
811 return VuoSceneObjectSubType_FisheyeCamera;
812 else if (strcmp(typeString,"light-ambient")==0)
813 return VuoSceneObjectSubType_AmbientLight;
814 else if (strcmp(typeString,"light-point")==0)
815 return VuoSceneObjectSubType_PointLight;
816 else if (strcmp(typeString,"light-spot")==0)
817 return VuoSceneObjectSubType_Spotlight;
818 else if (strcmp(typeString,"text")==0)
819 return VuoSceneObjectSubType_Text;
820
821 return VuoSceneObjectSubType_Empty;
822}
823
828{
829 switch (type)
830 {
831 case VuoSceneObjectSubType_Group:
832 return "group";
833 case VuoSceneObjectSubType_Mesh:
834 return "mesh";
835 case VuoSceneObjectSubType_PerspectiveCamera:
836 return "camera-perspective";
837 case VuoSceneObjectSubType_StereoCamera:
838 return "camera-stereo";
839 case VuoSceneObjectSubType_OrthographicCamera:
840 return "camera-orthographic";
841 case VuoSceneObjectSubType_FisheyeCamera:
842 return "camera-fisheye";
843 case VuoSceneObjectSubType_AmbientLight:
844 return "light-ambient";
845 case VuoSceneObjectSubType_PointLight:
846 return "light-point";
847 case VuoSceneObjectSubType_Spotlight:
848 return "light-spot";
849 case VuoSceneObjectSubType_Text:
850 return "text";
851 // VuoSceneObjectSubType_Empty
852 default:
853 return "empty";
854 }
855}
856
862{
863 VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
864 so->type = VuoSceneObjectSubType_AmbientLight;
866 so->light.color = color;
867 so->light.brightness = brightness;
868 return (VuoSceneObject)so;
869}
870
881VuoSceneObject VuoSceneObject_makePointLight(VuoColor color, float brightness, VuoPoint3d position, float range, float sharpness)
882{
883 VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
884 so->type = VuoSceneObjectSubType_PointLight;
885 VuoText t = VuoText_make("Point Light");
887 so->light.color = color;
888 so->light.brightness = brightness;
889 so->light.range = range;
890 so->light.sharpness = MAX(MIN(sharpness,1),0);
891 so->transform = VuoTransform_makeEuler(position, VuoPoint3d_make(0,0,0), VuoPoint3d_make(1,1,1));
892 return (VuoSceneObject)so;
893}
894
906VuoSceneObject VuoSceneObject_makeSpotlight(VuoColor color, float brightness, VuoTransform transform, float cone, float range, float sharpness)
907{
908 VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
909 so->type = VuoSceneObjectSubType_Spotlight;
911 so->light.color = color;
912 so->light.brightness = brightness;
913 so->light.cone = cone;
914 so->light.range = range;
915 so->light.sharpness = MAX(MIN(sharpness,1),0);
916 so->transform = transform;
917 return (VuoSceneObject)so;
918}
919
928void VuoSceneObject_findLights(VuoSceneObject sceneObject, VuoColor *ambientColor, float *ambientBrightness, VuoList_VuoSceneObject *pointLights, VuoList_VuoSceneObject *spotLights)
929{
930 __block VuoList_VuoColor ambientColors = VuoListCreate_VuoColor();
931 VuoRetain(ambientColors);
932
933 *ambientBrightness = 0;
934 *pointLights = VuoListCreate_VuoSceneObject();
935 *spotLights = VuoListCreate_VuoSceneObject();
936
937 VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
938 VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
939 if (co->type == VuoSceneObjectSubType_AmbientLight)
940 {
941 VuoListAppendValue_VuoColor(ambientColors, co->light.color);
942 *ambientBrightness += co->light.brightness;
943 }
944 else if (co->type == VuoSceneObjectSubType_PointLight)
945 {
948 VuoListAppendValue_VuoSceneObject(*pointLights, l);
949 }
950 else if (co->type == VuoSceneObjectSubType_Spotlight)
951 {
954 VuoListAppendValue_VuoSceneObject(*spotLights, l);
955 }
956 return true;
957 });
958
959 if (!VuoListGetCount_VuoColor(ambientColors)
960 && !VuoListGetCount_VuoSceneObject(*pointLights)
961 && !VuoListGetCount_VuoSceneObject(*spotLights))
962 {
963 *ambientColor = VuoColor_makeWithRGBA(1,1,1,1);
964 *ambientBrightness = 0.05;
965
966 // https://en.wikipedia.org/wiki/Three-point_lighting
967
969 VuoListAppendValue_VuoSceneObject(*pointLights, keyLight);
970
972 VuoListAppendValue_VuoSceneObject(*pointLights, fillLight);
973
974 VuoSceneObject backLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .15, VuoPoint3d_make(1,.75,-.5), 5, 0);
975 VuoListAppendValue_VuoSceneObject(*pointLights, backLight);
976 }
977 else
978 *ambientColor = VuoColor_average(ambientColors);
979
980 VuoRelease(ambientColors);
981}
982
986typedef struct
987{
988 long objectCount;
989 const VuoSceneObject *objects;
990 float modelviewMatrix[16];
992
1005void VuoSceneObject_visit(const VuoSceneObject object, bool (^function)(const VuoSceneObject currentObject, float modelviewMatrix[16]))
1006{
1007 if (!object)
1008 return;
1009
1010 VuoSceneObject_treeState rootState;
1011 rootState.objectCount = 1;
1012 rootState.objects = &object;
1013 VuoTransform_getMatrix(VuoTransform_makeIdentity(), rootState.modelviewMatrix);
1014
1015 std::list<VuoSceneObject_treeState> objectsToVisit(1, rootState);
1016 while (!objectsToVisit.empty())
1017 {
1018 VuoSceneObject_treeState currentState = objectsToVisit.front();
1019 objectsToVisit.pop_front();
1020
1021 for (long i = 0; i < currentState.objectCount; ++i)
1022 {
1023 VuoSceneObject_internal *currentObject = (VuoSceneObject_internal *)currentState.objects[i];
1024 if (!currentObject)
1025 continue;
1026
1027 float localModelviewMatrix[16];
1028 VuoTransform_getMatrix(currentObject->transform, localModelviewMatrix);
1029 float compositeModelviewMatrix[16];
1030 VuoTransform_multiplyMatrices4x4(localModelviewMatrix, currentState.modelviewMatrix, compositeModelviewMatrix);
1031
1032 if (!function((VuoSceneObject)currentObject, compositeModelviewMatrix))
1033 return;
1034
1035 if (currentObject->type == VuoSceneObjectSubType_Group)
1036 {
1037 // Prepend this object's childObjects to the objectsToVisit queue.
1038 long childObjectCount = VuoListGetCount_VuoSceneObject(currentObject->childObjects);
1039 if (childObjectCount)
1040 {
1041 VuoSceneObject_treeState childState;
1042 childState.objectCount = childObjectCount;
1043 childState.objects = VuoListGetData_VuoSceneObject(currentObject->childObjects);
1044 memcpy(childState.modelviewMatrix, compositeModelviewMatrix, sizeof(float[16]));
1045 objectsToVisit.push_front(childState);
1046 }
1047 }
1048 }
1049 }
1050}
1051
1055static void VuoSceneObject_applyInternal(VuoSceneObject sceneObject, void (^function)(VuoSceneObject currentObject, float modelviewMatrix[16]), float modelviewMatrix[16])
1056{
1057 if (!sceneObject)
1058 return;
1059
1060 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1061
1062 float localModelviewMatrix[16];
1063 VuoTransform_getMatrix(so->transform, localModelviewMatrix);
1064 float compositeModelviewMatrix[16];
1065 VuoTransform_multiplyMatrices4x4(localModelviewMatrix, modelviewMatrix, compositeModelviewMatrix);
1066
1067 function(sceneObject, compositeModelviewMatrix);
1068
1069 if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
1070 {
1071 unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
1072 for (unsigned long i = 1; i <= childObjectCount; ++i)
1073 {
1074 VuoSceneObject o = VuoListGetValue_VuoSceneObject(so->childObjects, i);
1075 VuoSceneObject_applyInternal(o, function, compositeModelviewMatrix);
1076 VuoListSetValue_VuoSceneObject(so->childObjects, o, i, false);
1077 }
1078 }
1079}
1080
1090void VuoSceneObject_apply(VuoSceneObject object, void (^function)(VuoSceneObject currentObject, float modelviewMatrix[16]))
1091{
1092 if (!object)
1093 return;
1094
1095 float localModelviewMatrix[16];
1096 VuoTransform_getMatrix(VuoTransform_makeIdentity(), localModelviewMatrix);
1097
1098 VuoSceneObject_applyInternal(object, function, localModelviewMatrix);
1099}
1100
1107{
1108 if (!object)
1109 return;
1110
1111 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1112 so->transform = VuoTransform_composite(so->transform, transform);
1113}
1114
1120void VuoSceneObject_translate(VuoSceneObject object, VuoPoint3d translation)
1121{
1122 if (!object)
1123 return;
1124
1125 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1126 so->transform.translation += translation;
1127}
1128
1134void VuoSceneObject_scale(VuoSceneObject object, VuoPoint3d scale)
1135{
1136 if (!object)
1137 return;
1138
1139 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1140 so->transform.scale *= scale;
1141}
1142
1149{
1150 if (!object)
1151 return nullptr;
1152
1153 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1154 return so->name;
1155}
1156
1166{
1167 if (!object)
1168 return nullptr;
1169
1170 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1171 if (so->type != VuoSceneObjectSubType_Group)
1172 return nullptr;
1173
1174 return so->childObjects;
1175}
1176
1183{
1184 if (!object)
1185 return VuoBlendMode_Normal;
1186
1187 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1188 return so->blendMode;
1189}
1190
1197{
1198 if (!object)
1199 return VuoSceneObjectSubType_Empty;
1200
1201 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1202 return so->type;
1203}
1204
1212{
1213 if (!object)
1214 return 0;
1215
1216 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1217 return so->id;
1218}
1219
1229{
1230 if (!object)
1231 return nullptr;
1232
1233 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1234 return so->shader;
1235}
1236
1244{
1245 if (!object)
1246 return false;
1247
1248 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1249 return so->isRealSize;
1250}
1251
1260{
1261 if (!object)
1262 return false;
1263
1264 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1265 return so->preservePhysicalSize;
1266}
1267
1277{
1278 if (!object)
1279 return nullptr;
1280
1281 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1282 return so->mesh;
1283}
1284
1291{
1292 if (!object)
1293 return nullptr;
1294
1295 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1296 return so->text.text;
1297}
1298
1305{
1306 if (!object)
1307 return VuoFont_makeDefault();
1308
1309 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1310 return so->text.font;
1311}
1312
1321{
1322 if (!object)
1323 return false;
1324
1325 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1326 return so->text.scaleWithScene;
1327}
1328
1335{
1336 if (!object)
1337 return 0;
1338
1339 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1340 return so->text.wrapWidth;
1341}
1342
1349{
1350 if (!object)
1351 return 0;
1352
1353 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1354 return so->camera.fieldOfView;
1355}
1356
1363{
1364 if (!object)
1365 return 0;
1366
1367 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1368 return so->camera.width;
1369}
1370
1377{
1378 if (!object)
1379 return 0;
1380
1381 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1382 return so->camera.distanceMin;
1383}
1384
1391{
1392 if (!object)
1393 return 0;
1394
1395 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1396 return so->camera.distanceMax;
1397}
1398
1405{
1406 if (!object)
1407 return 0;
1408
1409 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1410 return so->camera.vignetteWidth;
1411}
1412
1419{
1420 if (!object)
1421 return 0;
1422
1423 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1424 return so->camera.vignetteSharpness;
1425}
1426
1433{
1434 if (!object)
1435 return 0;
1436
1437 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1438 return so->camera.intraocularDistance;
1439}
1440
1447{
1448 if (!object)
1449 return 0;
1450
1451 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1452 return so->camera.confocalDistance;
1453}
1454
1461{
1462 if (!object)
1463 return (VuoColor){0,0,0,0};
1464
1465 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1466 return so->light.color;
1467}
1468
1475{
1476 if (!object)
1477 return 0;
1478
1479 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1480 return so->light.brightness;
1481}
1482
1489{
1490 if (!object)
1491 return 0;
1492
1493 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1494 return so->light.range;
1495}
1496
1503{
1504 if (!object)
1505 return 0;
1506
1507 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1508 return so->light.sharpness;
1509}
1510
1517{
1518 if (!object)
1519 return 0;
1520
1521 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1522 return so->light.cone;
1523}
1524
1531{
1532 if (!object)
1534
1535 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1536 return so->transform;
1537}
1538
1545{
1546 if (!object)
1547 return (VuoPoint3d){0,0,0};
1548
1549 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1550 return so->transform.translation;
1551}
1552
1559{
1560 if (!object)
1561 return;
1562
1563 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1564 so->type = type;
1565}
1566
1573void VuoSceneObject_setId(VuoSceneObject object, uint64_t id)
1574{
1575 if (!object)
1576 return;
1577
1578 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1579 so->id = id;
1580}
1581
1588{
1589 if (!object)
1590 return;
1591
1592 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1593 VuoRetain(name);
1594 VuoRelease(so->name);
1595 so->name = name;
1596}
1597
1604{
1605 if (!object)
1606 return;
1607
1608 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1609 if (so->type != VuoSceneObjectSubType_Group)
1610 return;
1611
1612 VuoRetain(childObjects);
1613 VuoRelease(so->childObjects);
1614 so->childObjects = childObjects;
1615}
1616
1623{
1624 if (!object)
1625 return;
1626
1627 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1628 VuoRetain(mesh);
1629 VuoRelease(so->mesh);
1630 so->mesh = mesh;
1631}
1632
1639{
1640 if (!object)
1641 return;
1642
1643 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1644 so->transform = transform;
1645}
1646
1652void VuoSceneObject_setTranslation(VuoSceneObject object, VuoPoint3d translation)
1653{
1654 if (!object)
1655 return;
1656
1657 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1658 so->transform.translation = translation;
1659}
1660
1666void VuoSceneObject_setScale(VuoSceneObject object, VuoPoint3d scale)
1667{
1668 if (!object)
1669 return;
1670
1671 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1672 so->transform.scale = scale;
1673}
1674
1681{
1682 if (!object)
1683 return;
1684
1685 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1686 VuoRetain(shader);
1687 VuoRelease(so->shader);
1688 so->shader = shader;
1689}
1690
1695{
1696 if (!object)
1697 return;
1698
1699 VuoSceneObject_apply(object, ^(VuoSceneObject currentObject, float modelviewMatrix[16]){
1701 VuoMesh_setFaceCulling(m, faceCulling);
1702 VuoSceneObject_setMesh(currentObject, m);
1703 });
1704}
1705
1719{
1720 if (!object)
1721 return;
1722
1723 VuoSceneObject_apply(object, ^(VuoSceneObject currentObject, float modelviewMatrix[16]){
1724 VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
1725 co->blendMode = blendMode;
1726 });
1727}
1728
1735void VuoSceneObject_setRealSize(VuoSceneObject object, bool isRealSize)
1736{
1737 if (!object)
1738 return;
1739
1740 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1741 so->isRealSize = isRealSize;
1742}
1743
1750void VuoSceneObject_setPreservePhysicalSize(VuoSceneObject object, bool shouldPreservePhysicalSize)
1751{
1752 if (!object)
1753 return;
1754
1755 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1756 so->preservePhysicalSize = shouldPreservePhysicalSize;
1757}
1758
1765{
1766 if (!object)
1767 return;
1768
1769 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1770 VuoRetain(text);
1771 VuoRelease(so->text.text);
1772 so->text.text = text;
1773}
1774
1781{
1782 if (!object)
1783 return;
1784
1785 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1786 VuoFont_retain(font);
1787 VuoFont_release(so->text.font);
1788 so->text.font = font;
1789}
1790
1797{
1798 if (!object)
1799 return;
1800
1801 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1802 so->camera.fieldOfView = fieldOfView;
1803}
1804
1811{
1812 if (!object)
1813 return;
1814
1815 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1816 so->camera.distanceMin = distanceMin;
1817}
1818
1825{
1826 if (!object)
1827 return;
1828
1829 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1830 so->camera.distanceMax = distanceMax;
1831}
1832
1844{
1845 if (!object)
1846 return nullptr;
1847
1848 VuoSceneObject_internal *o = (VuoSceneObject_internal *)object;
1849
1850 VuoSceneObject_internal *co = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
1851
1852 co->type = o->type;
1853 co->id = o->id;
1855 co->transform = o->transform;
1857 VuoSceneObject_setShader((VuoSceneObject)co, o->shader); // @todo
1858 co->isRealSize = o->isRealSize;
1859 co->preservePhysicalSize = o->preservePhysicalSize;
1860 co->blendMode = o->blendMode;
1861
1862 if (o->type == VuoSceneObjectSubType_Group && o->childObjects)
1863 {
1864 co->childObjects = VuoListCreate_VuoSceneObject();
1865 VuoRetain(co->childObjects);
1866 VuoListForeach_VuoSceneObject(o->childObjects, ^(const VuoSceneObject object){
1867 VuoListAppendValue_VuoSceneObject(co->childObjects, VuoSceneObject_copy(object));
1868 return true;
1869 });
1870 }
1871
1872 if (o->type == VuoSceneObjectSubType_PerspectiveCamera
1873 || o->type == VuoSceneObjectSubType_StereoCamera
1874 || o->type == VuoSceneObjectSubType_OrthographicCamera
1875 || o->type == VuoSceneObjectSubType_FisheyeCamera)
1876 {
1877 co->camera.fieldOfView = o->camera.fieldOfView;
1878 co->camera.width = o->camera.width;
1879 co->camera.distanceMin = o->camera.distanceMin;
1880 co->camera.distanceMax = o->camera.distanceMax;
1881 co->camera.confocalDistance = o->camera.confocalDistance;
1882 co->camera.intraocularDistance = o->camera.intraocularDistance;
1883 co->camera.vignetteWidth = o->camera.vignetteWidth;
1884 co->camera.vignetteSharpness = o->camera.vignetteSharpness;
1885 }
1886 else if (o->type == VuoSceneObjectSubType_AmbientLight
1887 || o->type == VuoSceneObjectSubType_PointLight
1888 || o->type == VuoSceneObjectSubType_Spotlight)
1889 {
1890 co->light.color = o->light.color;
1891 co->light.brightness = o->light.brightness;
1892 co->light.range = o->light.range;
1893 co->light.cone = o->light.cone;
1894 co->light.sharpness = o->light.sharpness;
1895 }
1896 else if (o->type == VuoSceneObjectSubType_Text)
1897 {
1898 VuoSceneObject_setText((VuoSceneObject)co, o->text.text);
1899 VuoSceneObject_setTextFont((VuoSceneObject)co, o->text.font);
1900 co->text.scaleWithScene = o->text.scaleWithScene;
1901 co->text.wrapWidth = o->text.wrapWidth;
1902 }
1903
1904 return (VuoSceneObject)co;
1905}
1906
1911{
1912 if (!so)
1913 return VuoBox_make((VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0});
1914
1915 __block bool haveGlobalBounds = false;
1916 __block VuoBox globalBounds;
1917
1918 VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
1919 VuoBox bounds;
1920 bool foundBounds = VuoSceneObject_meshBounds(currentObject, &bounds, modelviewMatrix);
1921 if (foundBounds)
1922 {
1923 globalBounds = haveGlobalBounds ? VuoBox_encapsulate(globalBounds, bounds) : bounds;
1924 haveGlobalBounds = true;
1925 }
1926 return true;
1927 });
1928
1929 if (haveGlobalBounds)
1930 return globalBounds;
1931 else
1932 return VuoBox_make( (VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0} );
1933}
1934
1938bool VuoSceneObject_meshBounds(const VuoSceneObject sceneObject, VuoBox *bounds, float matrix[16])
1939{
1940 if (!sceneObject)
1941 return false;
1942
1943 if (VuoSceneObject_getVertexCount(sceneObject) < 1)
1944 return false;
1945
1946 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1947 if (so->isRealSize
1948 || so->type == VuoSceneObjectSubType_Text)
1949 {
1950 // We don't know what the actual rendered size of the realSize layer will be,
1951 // but we can at least include its center point.
1952 *bounds = VuoBox_make(VuoPoint3d_make(matrix[12], matrix[13], matrix[14]), VuoPoint3d_make(0,0,0));
1953 }
1954 else
1955 {
1956 *bounds = VuoMesh_bounds(so->mesh, matrix);
1957
1958 if (so->shader)
1959 {
1960 bounds->size.x *= so->shader->objectScale;
1961 bounds->size.y *= so->shader->objectScale;
1962 bounds->size.z *= so->shader->objectScale;
1963 }
1964 }
1965
1966 return true;
1967}
1968
1973{
1974 if (!sceneObject)
1975 return;
1976
1977 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1978 VuoBox bounds = VuoSceneObject_bounds(sceneObject);
1979 so->transform.translation = VuoPoint3d_subtract(so->transform.translation, bounds.center);
1980}
1981
1988{
1989 if (!sceneObject)
1990 return;
1991 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1992 VuoBox bounds = VuoSceneObject_bounds(sceneObject);
1993
1994 float scale = fmax(fmax(bounds.size.x, bounds.size.y), bounds.size.z);
1995 if (fabs(scale) < 0.00001)
1996 return;
1997
1998 so->transform.scale = VuoPoint3d_multiply(so->transform.scale, 1./scale);
1999 so->transform.translation = VuoPoint3d_multiply(so->transform.translation, 1./scale);
2000}
2001
2028{
2029 json_object *o = NULL;
2030
2031 int id = 0;
2032 if (json_object_object_get_ex(js, "id", &o))
2033 id = json_object_get_int64(o);
2034
2035 VuoSceneObjectSubType type = VuoSceneObjectSubType_Empty;
2036 if (json_object_object_get_ex(js, "type", &o))
2037 type = VuoSceneObject_typeFromCString(json_object_get_string(o));
2038
2039 VuoMesh mesh = NULL;
2040 if (json_object_object_get_ex(js, "mesh", &o))
2041 mesh = VuoMesh_makeFromJson(o);
2042
2043 VuoShader shader = NULL;
2044 if (json_object_object_get_ex(js, "shader", &o))
2045 shader = VuoShader_makeFromJson(o);
2046
2047 bool isRealSize = false;
2048 if (json_object_object_get_ex(js, "isRealSize", &o))
2049 isRealSize = VuoBoolean_makeFromJson(o);
2050
2051 bool preservePhysicalSize = false;
2052 if (json_object_object_get_ex(js, "preservePhysicalSize", &o))
2053 preservePhysicalSize = VuoBoolean_makeFromJson(o);
2054
2056 if (json_object_object_get_ex(js, "blendMode", &o))
2057 blendMode = VuoBlendMode_makeFromJson(o);
2058
2059 VuoList_VuoSceneObject childObjects = NULL;
2060 if (json_object_object_get_ex(js, "childObjects", &o))
2061 childObjects = VuoList_VuoSceneObject_makeFromJson(o);
2062
2063 float cameraFieldOfView;
2064 if (json_object_object_get_ex(js, "cameraFieldOfView", &o))
2065 cameraFieldOfView = json_object_get_double(o);
2066
2067 float cameraWidth;
2068 if (json_object_object_get_ex(js, "cameraWidth", &o))
2069 cameraWidth = json_object_get_double(o);
2070
2071 float cameraDistanceMin;
2072 if (json_object_object_get_ex(js, "cameraDistanceMin", &o))
2073 cameraDistanceMin = json_object_get_double(o);
2074
2075 float cameraDistanceMax;
2076 if (json_object_object_get_ex(js, "cameraDistanceMax", &o))
2077 cameraDistanceMax = json_object_get_double(o);
2078
2079 float cameraConfocalDistance;
2080 if (json_object_object_get_ex(js, "cameraConfocalDistance", &o))
2081 cameraConfocalDistance = json_object_get_double(o);
2082
2083 float cameraIntraocularDistance;
2084 if (json_object_object_get_ex(js, "cameraIntraocularDistance", &o))
2085 cameraIntraocularDistance = json_object_get_double(o);
2086
2087 float cameraVignetteWidth;
2088 if (json_object_object_get_ex(js, "cameraVignetteWidth", &o))
2089 cameraVignetteWidth = json_object_get_double(o);
2090
2091 float cameraVignetteSharpness;
2092 if (json_object_object_get_ex(js, "cameraVignetteSharpness", &o))
2093 cameraVignetteSharpness = json_object_get_double(o);
2094
2095 VuoColor lightColor;
2096 if (json_object_object_get_ex(js, "lightColor", &o))
2097 lightColor = VuoColor_makeFromJson(o);
2098
2099 float lightBrightness;
2100 if (json_object_object_get_ex(js, "lightBrightness", &o))
2101 lightBrightness = json_object_get_double(o);
2102
2103 float lightCone;
2104 if (json_object_object_get_ex(js, "lightCone", &o))
2105 lightCone = json_object_get_double(o);
2106
2107 float lightRange;
2108 if (json_object_object_get_ex(js, "lightRange", &o))
2109 lightRange = json_object_get_double(o);
2110
2111 float lightSharpness;
2112 if (json_object_object_get_ex(js, "lightSharpness", &o))
2113 lightSharpness = json_object_get_double(o);
2114
2115 VuoText name = NULL;
2116 if (json_object_object_get_ex(js, "name", &o))
2117 name = VuoText_makeFromJson(o);
2118
2119 json_object_object_get_ex(js, "transform", &o);
2121
2122 VuoText text = NULL;
2123 if (json_object_object_get_ex(js, "text", &o))
2124 text = VuoText_makeFromJson(o);
2125
2126 VuoFont font;
2127 if (json_object_object_get_ex(js, "textFont", &o))
2128 font = VuoFont_makeFromJson(o);
2129
2130 bool scaleWithScene = false;
2131 if (json_object_object_get_ex(js, "textScaleWithScene", &o))
2132 scaleWithScene = VuoBoolean_makeFromJson(o);
2133
2134 float wrapWidth = INFINITY;
2135 if (json_object_object_get_ex(js, "textWrapWidth", &o))
2136 wrapWidth = json_object_get_double(o);
2137
2138 VuoSceneObject obj;
2139 switch (type)
2140 {
2141 case VuoSceneObjectSubType_Empty:
2142 obj = nullptr;
2143 break;
2144 case VuoSceneObjectSubType_Group:
2145 obj = VuoSceneObject_makeGroup(childObjects, transform);
2146 break;
2147 case VuoSceneObjectSubType_Mesh:
2148 {
2149 obj = VuoSceneObject_makeMesh(mesh, shader, transform);
2150 VuoSceneObject_internal *so = (VuoSceneObject_internal *)obj;
2151 so->isRealSize = isRealSize;
2152 so->preservePhysicalSize = preservePhysicalSize;
2153 so->blendMode = blendMode;
2154 VuoSceneObject_setName(obj, name);
2155 break;
2156 }
2157 case VuoSceneObjectSubType_PerspectiveCamera:
2159 name,
2160 transform,
2161 cameraFieldOfView,
2162 cameraDistanceMin,
2163 cameraDistanceMax
2164 );
2165 break;
2166 case VuoSceneObjectSubType_StereoCamera:
2168 name,
2169 transform,
2170 cameraFieldOfView,
2171 cameraDistanceMin,
2172 cameraDistanceMax,
2173 cameraConfocalDistance,
2174 cameraIntraocularDistance
2175 );
2176 break;
2177 case VuoSceneObjectSubType_OrthographicCamera:
2179 name,
2180 transform,
2181 cameraWidth,
2182 cameraDistanceMin,
2183 cameraDistanceMax
2184 );
2185 break;
2186 case VuoSceneObjectSubType_FisheyeCamera:
2188 name,
2189 transform,
2190 cameraFieldOfView,
2191 cameraDistanceMin,
2192 cameraDistanceMax,
2193 cameraVignetteWidth,
2194 cameraVignetteSharpness
2195 );
2196 break;
2197 case VuoSceneObjectSubType_AmbientLight:
2198 obj = VuoSceneObject_makeAmbientLight(lightColor, lightBrightness);
2199 break;
2200 case VuoSceneObjectSubType_PointLight:
2201 obj = VuoSceneObject_makePointLight(lightColor, lightBrightness, transform.translation, lightRange, lightSharpness);
2202 break;
2203 case VuoSceneObjectSubType_Spotlight:
2204 obj = VuoSceneObject_makeSpotlight(lightColor, lightBrightness, transform, lightCone, lightRange, lightSharpness);
2205 break;
2206 case VuoSceneObjectSubType_Text:
2207 obj = VuoSceneObject_makeText(text, font, scaleWithScene, wrapWidth);
2208 VuoSceneObject_setTransform(obj, transform);
2209 VuoSceneObject_setMesh(obj, mesh);
2210 break;
2211 }
2212
2213 VuoSceneObject_setId(obj, id);
2214
2215 return obj;
2216}
2217
2221json_object *VuoSceneObject_getJson(const VuoSceneObject sceneObject)
2222{
2223 if (!sceneObject)
2224 return nullptr;
2225
2226 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2227
2228 json_object *js = json_object_new_object();
2229
2230 json_object_object_add(js, "id", json_object_new_int64(so->id));
2231 json_object_object_add(js, "type", json_object_new_string(VuoSceneObject_cStringForType(so->type)));
2232
2233 switch (so->type)
2234 {
2235 case VuoSceneObjectSubType_Empty:
2236 break;
2237
2238 case VuoSceneObjectSubType_Mesh:
2239 if (so->mesh)
2240 json_object_object_add(js, "mesh", VuoMesh_getJson(so->mesh));
2241
2242 if (so->shader)
2243 json_object_object_add(js, "shader", VuoShader_getJson(so->shader));
2244
2245 json_object_object_add(js, "isRealSize", VuoBoolean_getJson(so->isRealSize));
2246
2247 json_object_object_add(js, "preservePhysicalSize", VuoBoolean_getJson(so->preservePhysicalSize));
2248
2249 if (so->blendMode != VuoBlendMode_Normal)
2250 json_object_object_add(js, "blendMode", VuoBlendMode_getJson(so->blendMode));
2251 break;
2252
2253 case VuoSceneObjectSubType_Group:
2254 if (so->childObjects)
2255 json_object_object_add(js, "childObjects", VuoList_VuoSceneObject_getJson(so->childObjects));
2256 break;
2257
2258 case VuoSceneObjectSubType_PerspectiveCamera:
2259 case VuoSceneObjectSubType_StereoCamera:
2260 case VuoSceneObjectSubType_OrthographicCamera:
2261 case VuoSceneObjectSubType_FisheyeCamera:
2262 {
2263 if (so->type != VuoSceneObjectSubType_FisheyeCamera)
2264 {
2265 json_object_object_add(js, "cameraDistanceMin", json_object_new_double(so->camera.distanceMin));
2266 json_object_object_add(js, "cameraDistanceMax", json_object_new_double(so->camera.distanceMax));
2267 }
2268
2269 if (so->type == VuoSceneObjectSubType_PerspectiveCamera
2270 || so->type == VuoSceneObjectSubType_StereoCamera
2271 || so->type == VuoSceneObjectSubType_FisheyeCamera)
2272 json_object_object_add(js, "cameraFieldOfView", json_object_new_double(so->camera.fieldOfView));
2273
2274 if (so->type == VuoSceneObjectSubType_StereoCamera)
2275 {
2276 json_object_object_add(js, "cameraConfocalDistance", json_object_new_double(so->camera.confocalDistance));
2277 json_object_object_add(js, "cameraIntraocularDistance", json_object_new_double(so->camera.intraocularDistance));
2278 }
2279
2280 if (so->type == VuoSceneObjectSubType_OrthographicCamera)
2281 json_object_object_add(js, "cameraWidth", json_object_new_double(so->camera.width));
2282
2283 if (so->type == VuoSceneObjectSubType_FisheyeCamera)
2284 {
2285 json_object_object_add(js, "cameraVignetteWidth", json_object_new_double(so->camera.vignetteWidth));
2286 json_object_object_add(js, "cameraVignetteSharpness", json_object_new_double(so->camera.vignetteSharpness));
2287 }
2288
2289 break;
2290 }
2291
2292 case VuoSceneObjectSubType_AmbientLight:
2293 case VuoSceneObjectSubType_PointLight:
2294 case VuoSceneObjectSubType_Spotlight:
2295 {
2296 json_object_object_add(js, "lightColor", VuoColor_getJson(so->light.color));
2297 json_object_object_add(js, "lightBrightness", json_object_new_double(so->light.brightness));
2298
2299 if (so->type == VuoSceneObjectSubType_PointLight
2300 || so->type == VuoSceneObjectSubType_Spotlight)
2301 {
2302 json_object_object_add(js, "lightRange", json_object_new_double(so->light.range));
2303 json_object_object_add(js, "lightSharpness", json_object_new_double(so->light.sharpness));
2304 }
2305 if (so->type == VuoSceneObjectSubType_Spotlight)
2306 json_object_object_add(js, "lightCone", json_object_new_double(so->light.cone));
2307
2308 break;
2309 }
2310
2311 case VuoSceneObjectSubType_Text:
2312 {
2313 if (so->text.text)
2314 json_object_object_add(js, "text", VuoText_getJson(so->text.text));
2315
2316 json_object_object_add(js, "textFont", VuoFont_getJson(so->text.font));
2317
2318 if (so->mesh)
2319 json_object_object_add(js, "mesh", VuoMesh_getJson(so->mesh));
2320
2321 json_object_object_add(js, "textScaleWithScene", VuoBoolean_getJson(so->text.scaleWithScene));
2322 json_object_object_add(js, "textWrapWidth", VuoReal_getJson(so->text.wrapWidth));
2323
2324 break;
2325 }
2326 }
2327
2328 if (so->name)
2329 json_object_object_add(js, "name", VuoText_getJson(so->name));
2330
2331 if (so->type != VuoSceneObjectSubType_AmbientLight)
2332 json_object_object_add(js, "transform", VuoTransform_getJson(so->transform));
2333
2334 return js;
2335}
2336
2340unsigned long VuoSceneObject_getVertexCount(const VuoSceneObject sceneObject)
2341{
2342 if (!sceneObject)
2343 return 0;
2344
2345 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2346 if (!so->mesh)
2347 return 0;
2348
2349 unsigned int vertexCount;
2350 VuoMesh_getCPUBuffers(so->mesh, &vertexCount, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
2351 return vertexCount;
2352}
2353
2357unsigned long VuoSceneObject_getElementCount(const VuoSceneObject sceneObject)
2358{
2359 if (!sceneObject)
2360 return 0;
2361
2362 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2363 if (!so->mesh)
2364 return 0;
2365
2366 unsigned int elementCount;
2367 VuoMesh_getCPUBuffers(so->mesh, nullptr, nullptr, nullptr, nullptr, nullptr, &elementCount, nullptr);
2368 return elementCount;
2369}
2370
2376void VuoSceneObject_getStatistics(const VuoSceneObject sceneObject, unsigned long *descendantCount, unsigned long *totalVertexCount, unsigned long *totalElementCount)
2377{
2378 if (!sceneObject)
2379 return;
2380
2381 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2382 unsigned long childObjectCount = 0;
2383 if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2384 childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2385 *descendantCount += childObjectCount;
2386 *totalVertexCount += VuoSceneObject_getVertexCount(sceneObject);
2387 *totalElementCount += VuoSceneObject_getElementCount(sceneObject);
2388
2389 if (so->type == VuoSceneObjectSubType_Group)
2390 for (unsigned long i = 1; i <= childObjectCount; ++i)
2391 VuoSceneObject_getStatistics(VuoListGetValue_VuoSceneObject(so->childObjects, i), descendantCount, totalVertexCount, totalElementCount);
2392}
2393
2398{
2399 if (!sceneObject)
2400 return nullptr;
2401
2402 // Exploit json_object's set-containing-only-unique-items data structure.
2403 __block json_object *names = json_object_new_object();
2404 VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2405 VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2406 if (co->shader)
2407 json_object_object_add(names, co->shader->name, NULL);
2408 return true;
2409 });
2410
2412 json_object_object_foreach(names, key, val)
2414 json_object_put(names);
2415 return nameList;
2416}
2417
2422{
2423 if (!VuoSceneObject_isPopulated(sceneObject))
2424 return strdup("No object");
2425
2426 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2427
2428 if (so->type == VuoSceneObjectSubType_Text)
2429 {
2430 char *fontSummary = VuoFont_getSummary(so->text.font);
2431 char *textSummary = VuoText_format("<div>\"%s\"</div><div>%sat (%g,%g)</div><div>ID %llu</div>", so->text.text, fontSummary, so->transform.translation.x, so->transform.translation.y, so->id);
2432 free(fontSummary);
2433 return textSummary;
2434 }
2435
2436 if (so->type == VuoSceneObjectSubType_PerspectiveCamera
2437 || so->type == VuoSceneObjectSubType_StereoCamera
2438 || so->type == VuoSceneObjectSubType_OrthographicCamera
2439 || so->type == VuoSceneObjectSubType_FisheyeCamera)
2440 {
2441 char *type = strdup(VuoSceneObject_cStringForType(so->type));
2442 type[0] = toupper(type[0]);
2443
2444 float cameraViewValue = 0;
2445 const char *cameraViewString = "";
2446 if (so->type == VuoSceneObjectSubType_PerspectiveCamera)
2447 {
2448 cameraViewValue = so->camera.fieldOfView;
2449 cameraViewString = "° field of view";
2450 }
2451 else if (so->type == VuoSceneObjectSubType_StereoCamera)
2452 {
2453 cameraViewValue = so->camera.fieldOfView;
2454 cameraViewString = "° field of view (stereoscopic)";
2455 }
2456 else if (so->type == VuoSceneObjectSubType_OrthographicCamera)
2457 {
2458 cameraViewValue = so->camera.width;
2459 cameraViewString = " unit width";
2460 }
2461 else if (so->type == VuoSceneObjectSubType_FisheyeCamera)
2462 {
2463 cameraViewValue = so->camera.fieldOfView;
2464 cameraViewString = "° field of view (fisheye)";
2465 }
2466
2467 char *translationString = VuoPoint3d_getSummary(so->transform.translation);
2468
2469 const char *rotationLabel;
2470 char *rotationString;
2471 if (so->transform.type == VuoTransformTypeEuler)
2472 {
2473 rotationLabel = "Rotated";
2474 rotationString = VuoPoint3d_getSummary(VuoPoint3d_multiply(so->transform.rotationSource.euler, -180.f/M_PI));
2475 }
2476 else
2477 {
2478 rotationLabel = "Target";
2479 rotationString = VuoPoint3d_getSummary(so->transform.rotationSource.target);
2480 }
2481
2482 char *valueAsString = VuoText_format("<div>%s named \"%s\"</div>\n<div>At (%s)</div>\n<div>%s (%s)</div>\n<div>%g%s</div>\n<div>Shows objects between depth %g and %g</div>",
2483 type, so->name ? so->name : "",
2484 translationString,
2485 rotationLabel, rotationString,
2486 cameraViewValue, cameraViewString,
2487 so->camera.distanceMin, so->camera.distanceMax);
2488 free(rotationString);
2489 free(translationString);
2490 free(type);
2491 return valueAsString;
2492 }
2493
2494 if (so->type == VuoSceneObjectSubType_AmbientLight
2495 || so->type == VuoSceneObjectSubType_PointLight
2496 || so->type == VuoSceneObjectSubType_Spotlight)
2497 {
2498 char *type = strdup(VuoSceneObject_cStringForType(so->type));
2499 type[0] = toupper(type[0]);
2500
2501 char *colorString = VuoColor_getShortSummary(so->light.color);
2502
2503 char *positionRangeString;
2504 if (so->type == VuoSceneObjectSubType_PointLight
2505 || so->type == VuoSceneObjectSubType_Spotlight)
2506 {
2507 char *positionString = VuoPoint3d_getSummary(so->transform.translation);
2508
2509 positionRangeString = VuoText_format("\n<div>Position (%s)</div>\n<div>Range %g units (%g sharpness)</div>",
2510 positionString, so->light.range, so->light.sharpness);
2511
2512 free(positionString);
2513 }
2514 else
2515 positionRangeString = strdup("");
2516
2517 char *directionConeString;
2518 if (so->type == VuoSceneObjectSubType_Spotlight)
2519 {
2520 VuoPoint3d direction = VuoTransform_getDirection(so->transform);
2521 char *directionString = VuoPoint3d_getSummary(direction);
2522
2523 directionConeString = VuoText_format("\n<div>Direction (%s)</div>\n<div>Cone %g°</div>",
2524 directionString, so->light.cone * 180./M_PI);
2525
2526 free(directionString);
2527 }
2528 else
2529 directionConeString = strdup("");
2530
2531 char *valueAsString = VuoText_format("<div>%s</div>\n<div>Color %s</div>\n<div>Brightness %g</div>%s%s",
2532 type, colorString, so->light.brightness, positionRangeString, directionConeString);
2533
2534 free(directionConeString);
2535 free(positionRangeString);
2536 free(colorString);
2537 free(type);
2538
2539 return valueAsString;
2540 }
2541
2542 unsigned long vertexCount = VuoSceneObject_getVertexCount(sceneObject);
2543 unsigned long elementCount = VuoSceneObject_getElementCount(sceneObject);
2544
2545 char *transform = VuoTransform_getSummary(so->transform);
2546
2547 unsigned long childObjectCount = 0;
2548 if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2549 childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2550 const char *childObjectPlural = childObjectCount == 1 ? "" : "s";
2551
2552 char *descendants;
2553 if (childObjectCount)
2554 {
2555 unsigned long descendantCount = 0;
2556 unsigned long totalVertexCount = 0;
2557 unsigned long totalElementCount = 0;
2558 VuoSceneObject_getStatistics(sceneObject, &descendantCount, &totalVertexCount, &totalElementCount);
2559 const char *descendantPlural = descendantCount == 1 ? "" : "s";
2560
2561 descendants = VuoText_format("\n<div>%ld descendant%s</div>\n<div>Total, including descendants:</div>\n<div>%ld vertices, %ld elements</div>",
2562 descendantCount, descendantPlural, totalVertexCount, totalElementCount);
2563 }
2564 else
2565 descendants = strdup("");
2566
2567 VuoList_VuoText shaderNames = VuoSceneObject_findShaderNames(sceneObject);
2568 VuoRetain(shaderNames);
2569 char *shaderNamesSummary;
2570 if (VuoListGetCount_VuoText(shaderNames))
2571 {
2572 VuoInteger shaderNameCount = VuoListGetCount_VuoText(shaderNames);
2573 const char *header = "\n<div>Shaders:<ul>";
2575 VuoInteger shaderNameLength = strlen(header);
2576 for (VuoInteger i = 1; i <= shaderNameCount; ++i)
2577 shaderNameLength += strlen("\n<li>") + strlen(VuoListGetValue_VuoText(shaderNames, i)) + strlen("</li>");
2578 shaderNameLength += strlen("</ul></div>");
2579
2580 shaderNamesSummary = (char *)malloc(shaderNameLength + 1);
2581 char *t = shaderNamesSummary;
2582 t = strcpy(t, header) + strlen(header);
2583 for (VuoInteger i = 1; i <= shaderNameCount; ++i)
2584 {
2585 t = strcpy(t, "\n<li>") + strlen("\n<li>");
2586 t = strcpy(t, VuoListGetValue_VuoText(shaderNames, i)) + strlen(VuoListGetValue_VuoText(shaderNames, i));
2587 t = strcpy(t, "</li>") + strlen("</li>");
2588 }
2589 t = strcpy(t, "</ul></div>");
2590 }
2591 else
2592 shaderNamesSummary = strdup("");
2593 VuoRelease(shaderNames);
2594
2595 char *name = NULL;
2596 if (so->name)
2597 name = VuoText_format("<div>Object named \"%s\"</div>\n", so->name);
2598
2599 char *valueAsString = VuoText_format("%s<div>%ld vertices, %ld elements</div>\n<div>%s</div>\n<div>ID %lld</div>\n<div>%ld child object%s</div>%s%s",
2600 name ? name : "",
2601 vertexCount, elementCount,
2602 transform,
2603 so->id,
2604 childObjectCount, childObjectPlural,
2605 descendants, shaderNamesSummary);
2606
2607 free(name);
2608 free(descendants);
2609 free(transform);
2610 free(shaderNamesSummary);
2611
2612 return valueAsString;
2613}
2614
2618static void VuoSceneObject_dump_internal(const VuoSceneObject sceneObject, unsigned int level)
2619{
2620 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2621
2622 for (unsigned int i=0; i<level; ++i)
2623 fprintf(stderr, "\t");
2624
2625 if (!sceneObject)
2626 {
2627 fprintf(stderr, "no object\n");
2628 return;
2629 }
2630
2631 fprintf(stderr, "%s \"%s\" (%s) ", VuoSceneObject_cStringForType(so->type), so->name ? so->name : "(no name)", VuoTransform_getSummary(so->transform));
2632 if (so->type == VuoSceneObjectSubType_Mesh)
2633 fprintf(stderr, "%lu vertices, %lu elements, shader '%s' (%p)", VuoSceneObject_getVertexCount(sceneObject), VuoSceneObject_getElementCount(sceneObject), so->shader ? so->shader->name : "", so->shader);
2634 fprintf(stderr, "\n");
2635
2636 if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2637 {
2638 unsigned int childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2639 for (unsigned int i=1; i<=childObjectCount; ++i)
2640 VuoSceneObject_dump_internal(VuoListGetValue_VuoSceneObject(so->childObjects, i), level+1);
2641 }
2642}
2643
2651
2663{
2664 if (!so)
2665 return nullptr;
2666
2667 // Count the vertices.
2668
2669 __block unsigned long triangleVertexCount = 0;
2670 __block unsigned long trianglePrimitiveCount = 0;
2671 __block VuoMesh_FaceCulling triangleFaceCulling = VuoMesh_CullBackfaces;
2672
2673 __block unsigned long lineVertexCount = 0;
2674 __block unsigned long linePrimitiveCount = 0;
2675 __block double linePrimitiveSize = 0;
2676
2677 __block unsigned long pointVertexCount = 0;
2678 __block unsigned long pointPrimitiveCount = 0;
2679 __block double pointPrimitiveSize = 0;
2680
2681 VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2682 VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2683 if (!co->mesh)
2684 return true;
2685
2686 VuoMesh_ElementAssemblyMethod elementAssemblyMethod = VuoMesh_getElementAssemblyMethod(co->mesh);
2687
2688 unsigned int vertexCount;
2689 VuoMesh_getCPUBuffers(co->mesh, &vertexCount, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
2690
2691 if (elementAssemblyMethod == VuoMesh_IndividualTriangles
2692 || elementAssemblyMethod == VuoMesh_TriangleStrip
2693 || elementAssemblyMethod == VuoMesh_TriangleFan)
2694 {
2695 triangleVertexCount += vertexCount;
2696 trianglePrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2697 triangleFaceCulling = VuoMesh_getFaceCulling(co->mesh);
2698 }
2699 else if (elementAssemblyMethod == VuoMesh_IndividualLines
2700 || elementAssemblyMethod == VuoMesh_LineStrip)
2701 {
2702 lineVertexCount += vertexCount;
2703 linePrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2704 linePrimitiveSize = VuoMesh_getPrimitiveSize(co->mesh);
2705 }
2706 else if (elementAssemblyMethod == VuoMesh_Points)
2707 {
2708 pointVertexCount += vertexCount;
2709 pointPrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2710 pointPrimitiveSize = VuoMesh_getPrimitiveSize(co->mesh);
2711 }
2712
2713 return true;
2714 });
2715// VLog("triangles: %ldv %ldp lines: %ldv %ldp points: %ldv %ldp", triangleVertexCount, trianglePrimitiveCount, lineVertexCount, linePrimitiveCount, pointVertexCount, pointPrimitiveCount);
2716
2717 if (!trianglePrimitiveCount && !linePrimitiveCount && !pointPrimitiveCount)
2718 return nullptr;
2719
2720 // Allocate the buffers.
2721 unsigned int *triangleElements = nullptr;
2722 float *trianglePositions = nullptr, *triangleNormals = nullptr, *triangleTextureCoordinates = nullptr;
2723 unsigned int *lineElements = nullptr;
2724 float *linePositions = nullptr, *lineNormals = nullptr, *lineTextureCoordinates = nullptr;
2725 unsigned int *pointElements = nullptr;
2726 float *pointPositions = nullptr, *pointNormals = nullptr, *pointTextureCoordinates = nullptr;
2727 if (trianglePrimitiveCount)
2728 VuoMesh_allocateCPUBuffers(triangleVertexCount, &trianglePositions, &triangleNormals, &triangleTextureCoordinates, nullptr, trianglePrimitiveCount * 3, &triangleElements);
2729 if (linePrimitiveCount)
2730 VuoMesh_allocateCPUBuffers(lineVertexCount, &linePositions, &lineNormals, &lineTextureCoordinates, nullptr, linePrimitiveCount * 2, &lineElements);
2731 if (pointPrimitiveCount)
2732 VuoMesh_allocateCPUBuffers(pointVertexCount, &pointPositions, &pointNormals, &pointTextureCoordinates, nullptr, pointPrimitiveCount, &pointElements);
2733
2734 // Copy the vertex attributes.
2735 __block unsigned long triangleVertexIndex = 0;
2736 __block unsigned long triangleElementIndex = 0;
2737 __block unsigned long lineVertexIndex = 0;
2738 __block unsigned long lineElementIndex = 0;
2739 __block unsigned long pointVertexIndex = 0;
2740 __block unsigned long pointElementIndex = 0;
2741 __block bool anyTextureCoordinates = false;
2742 VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2743 VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2744 if (!co->mesh)
2745 return true;
2746
2747 VuoMesh_ElementAssemblyMethod elementAssemblyMethod = VuoMesh_getElementAssemblyMethod(co->mesh);
2748
2749 unsigned int vertexCount, elementCount, *elements;
2750 float *positions, *normals, *textureCoordinates;
2751 VuoMesh_getCPUBuffers(co->mesh, &vertexCount, &positions, &normals, &textureCoordinates, nullptr, &elementCount, &elements);
2752
2753 if (textureCoordinates)
2754 anyTextureCoordinates = true;
2755
2756 if (elementAssemblyMethod == VuoMesh_IndividualTriangles
2757 || elementAssemblyMethod == VuoMesh_TriangleStrip
2758 || elementAssemblyMethod == VuoMesh_TriangleFan)
2759 {
2760 unsigned long indexOffset = triangleVertexIndex;
2761 for (unsigned int n = 0; n < vertexCount; ++n)
2762 {
2763 VuoPoint3d p = VuoPoint3d_makeFromArray(&positions[n * 3]);
2764 VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2765 VuoPoint3d_setArray(&trianglePositions[triangleVertexIndex * 3], pt);
2766
2767 if (normals)
2768 {
2769 VuoPoint3d r = VuoPoint3d_makeFromArray(&normals[n * 3]);
2770 VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2771 VuoPoint3d_setArray(&triangleNormals[triangleVertexIndex * 3], rt);
2772 }
2773
2774 if (textureCoordinates)
2775 VuoPoint2d_setArray(&triangleTextureCoordinates[triangleVertexIndex * 2], VuoPoint2d_makeFromArray(&textureCoordinates[n * 2]));
2776
2777 ++triangleVertexIndex;
2778 }
2779
2780 if (elementAssemblyMethod == VuoMesh_IndividualTriangles)
2781 {
2782 if (elementCount)
2783 for (unsigned int n = 0; n < elementCount; ++n)
2784 triangleElements[triangleElementIndex++] = indexOffset + elements[n];
2785 else
2786 for (unsigned int n = 0; n < vertexCount; ++n)
2787 triangleElements[triangleElementIndex++] = indexOffset + n;
2788 }
2789 else if (elementAssemblyMethod == VuoMesh_TriangleStrip)
2790 {
2791 // Expand the triangle strip to individual triangles.
2792 if (elementCount)
2793 for (unsigned int n = 2; n < elementCount; ++n)
2794 if (n%2 == 0)
2795 {
2796 triangleElements[triangleElementIndex++] = indexOffset + elements[n-2];
2797 triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2798 triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2799 }
2800 else
2801 {
2802 triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2803 triangleElements[triangleElementIndex++] = indexOffset + elements[n-2];
2804 triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2805 }
2806 else
2807 for (unsigned int n = 0; n < vertexCount; ++n)
2808 if (n%2 == 0)
2809 {
2810 triangleElements[triangleElementIndex++] = indexOffset + n-2;
2811 triangleElements[triangleElementIndex++] = indexOffset + n-1;
2812 triangleElements[triangleElementIndex++] = indexOffset + n ;
2813 }
2814 else
2815 {
2816 triangleElements[triangleElementIndex++] = indexOffset + n-1;
2817 triangleElements[triangleElementIndex++] = indexOffset + n-2;
2818 triangleElements[triangleElementIndex++] = indexOffset + n ;
2819 }
2820 }
2821 else if (elementAssemblyMethod == VuoMesh_TriangleFan)
2822 {
2823 // Expand the triangle fan to individual triangles.
2824 if (elementCount)
2825 for (unsigned int n = 2; n < elementCount; ++n)
2826 {
2827 triangleElements[triangleElementIndex++] = indexOffset + elements[0 ];
2828 triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2829 triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2830 }
2831 else
2832 for (unsigned int n = 2; n < vertexCount; ++n)
2833 {
2834 triangleElements[triangleElementIndex++] = indexOffset + 0;
2835 triangleElements[triangleElementIndex++] = indexOffset + n-1;
2836 triangleElements[triangleElementIndex++] = indexOffset + n;
2837 }
2838 }
2839 }
2840 else if (elementAssemblyMethod == VuoMesh_IndividualLines
2841 || elementAssemblyMethod == VuoMesh_LineStrip)
2842 {
2843 unsigned long indexOffset = lineVertexIndex;
2844 for (unsigned int n = 0; n < vertexCount; ++n)
2845 {
2846 VuoPoint3d p = VuoPoint3d_makeFromArray(&positions[n * 3]);
2847 VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2848 VuoPoint3d_setArray(&linePositions[lineVertexIndex * 3], pt);
2849
2850 if (normals)
2851 {
2852 VuoPoint3d r = VuoPoint3d_makeFromArray(&normals[n * 3]);
2853 VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2854 VuoPoint3d_setArray(&lineNormals[lineVertexIndex * 3], rt);
2855 }
2856
2857 if (textureCoordinates)
2858 VuoPoint2d_setArray(&lineTextureCoordinates[lineVertexIndex], VuoPoint2d_makeFromArray(&textureCoordinates[n * 2]));
2859
2860 ++lineVertexIndex;
2861 }
2862
2863 if (elementAssemblyMethod == VuoMesh_IndividualLines)
2864 {
2865 if (elementCount)
2866 for (unsigned int n = 0; n < elementCount; ++n)
2867 lineElements[lineElementIndex++] = indexOffset + elements[n];
2868 else
2869 for (unsigned int n = 0; n < vertexCount; ++n)
2870 lineElements[lineElementIndex++] = indexOffset + n;
2871 }
2872 else if (elementAssemblyMethod == VuoMesh_LineStrip)
2873 {
2874 // Expand the line strip to individual lines.
2875 if (elementCount)
2876 for (unsigned int n = 1; n < elementCount; ++n)
2877 {
2878 lineElements[lineElementIndex++] = indexOffset + elements[n-1];
2879 lineElements[lineElementIndex++] = indexOffset + elements[n ];
2880 }
2881 else
2882 for (unsigned int n = 1; n < vertexCount; ++n)
2883 {
2884 lineElements[lineElementIndex++] = indexOffset + n-1;
2885 lineElements[lineElementIndex++] = indexOffset + n;
2886 }
2887 }
2888 }
2889 else if (elementAssemblyMethod == VuoMesh_Points)
2890 {
2891 unsigned long indexOffset = pointVertexIndex;
2892 for (unsigned int n = 0; n < vertexCount; ++n)
2893 {
2894 VuoPoint3d p = VuoPoint3d_makeFromArray(&positions[n * 3]);
2895 VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2896 VuoPoint3d_setArray(&pointPositions[pointVertexIndex * 3], pt);
2897
2898 if (normals)
2899 {
2900 VuoPoint3d r = VuoPoint3d_makeFromArray(&normals[n * 3]);
2901 VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2902 VuoPoint3d_setArray(&pointNormals[pointVertexIndex * 3], rt);
2903 }
2904
2905 if (textureCoordinates)
2906 VuoPoint2d_setArray(&pointTextureCoordinates[pointVertexIndex * 2], VuoPoint2d_makeFromArray(&textureCoordinates[n * 2]));
2907
2908 ++pointVertexIndex;
2909 }
2910
2911 if (elementCount)
2913 for (unsigned int n = 0; n < elementCount; ++n)
2914 pointElements[pointElementIndex++] = indexOffset + elements[n];
2915 else
2916 {
2917 for (unsigned int n = 0; n < vertexCount; ++n)
2918 pointElements[pointElementIndex++] = indexOffset + n;
2919 }
2920 }
2921
2922 return true;
2923 });
2924
2925
2926 VuoMesh triangleMesh = nullptr;
2927 VuoMesh lineMesh = nullptr;
2928 VuoMesh pointMesh = nullptr;
2929 if (trianglePrimitiveCount)
2930 {
2931 if (!anyTextureCoordinates)
2932 {
2933 free(triangleTextureCoordinates);
2934 triangleTextureCoordinates = nullptr;
2935 }
2936 triangleMesh = VuoMesh_makeFromCPUBuffers(triangleVertexCount, trianglePositions, triangleNormals, triangleTextureCoordinates, nullptr, trianglePrimitiveCount * 3, triangleElements, VuoMesh_IndividualTriangles);
2937 VuoMesh_setFaceCulling(triangleMesh, triangleFaceCulling);
2938 }
2939 if (linePrimitiveCount)
2940 {
2941 if (!anyTextureCoordinates)
2942 {
2943 free(lineTextureCoordinates);
2944 lineTextureCoordinates = nullptr;
2945 }
2946 lineMesh = VuoMesh_makeFromCPUBuffers(lineVertexCount, linePositions, lineNormals, lineTextureCoordinates, nullptr, linePrimitiveCount * 2, lineElements, VuoMesh_IndividualLines);
2947 VuoMesh_setPrimitiveSize(lineMesh, linePrimitiveSize);
2948 }
2949 if (pointPrimitiveCount)
2950 {
2951 if (!anyTextureCoordinates)
2952 {
2953 free(pointTextureCoordinates);
2954 pointTextureCoordinates = nullptr;
2955 }
2956 pointMesh = VuoMesh_makeFromCPUBuffers(pointVertexCount, pointPositions, pointNormals, pointTextureCoordinates, nullptr, pointPrimitiveCount, pointElements, VuoMesh_Points);
2957 VuoMesh_setPrimitiveSize(pointMesh, pointPrimitiveSize);
2958 }
2959
2960 if (triangleMesh && !lineMesh && !pointMesh)
2961 return VuoSceneObject_makeMesh(triangleMesh, NULL, VuoTransform_makeIdentity());
2962 else if (!triangleMesh && lineMesh && !pointMesh)
2963 return VuoSceneObject_makeMesh(lineMesh, NULL, VuoTransform_makeIdentity());
2964 else if (!triangleMesh && !lineMesh && pointMesh)
2965 return VuoSceneObject_makeMesh(pointMesh, NULL, VuoTransform_makeIdentity());
2966 else
2967 {
2969 if (triangleMesh)
2971 if (lineMesh)
2973 if (pointMesh)
2976 }
2977
2978 return NULL;
2979}
2980
2981#define CSGJS_HEADER_ONLY
2982#include "csgjs.cc"
2983
2988{
2989 if (!so)
2990 return csgjs_model();
2991
2993 VuoSceneObject_internal *f = (VuoSceneObject_internal *)flat;
2994 if (!f->mesh)
2995 return csgjs_model();
2996
2997 VuoLocal(flat);
2998
3000 return csgjs_model();
3001
3002 unsigned int vertexCount, elementCount, *elements;
3003 float *positions, *normals, *textureCoordinates;
3004 VuoMesh_getCPUBuffers(f->mesh, &vertexCount, &positions, &normals, &textureCoordinates, nullptr, &elementCount, &elements);
3005
3006 csgjs_model cm;
3007 for (unsigned int n = 0; n < vertexCount; ++n)
3008 {
3009 csgjs_vertex v;
3010 v.pos = csgjs_vector(positions[n * 3], positions[n * 3 + 1], positions[n * 3 + 2]);
3011 if (normals)
3012 v.normal = csgjs_vector(normals[n * 3], normals[n * 3 + 1], normals[n * 3 + 2]);
3013 if (textureCoordinates)
3014 v.uv = csgjs_vector(textureCoordinates[n * 2], textureCoordinates[n * 2 + 1], 0);
3015 cm.vertices.push_back(v);
3016 }
3017 for (unsigned int n = 0; n < elementCount; ++n)
3018 cm.indices.push_back(elements[n]);
3019
3020 return cm;
3021}
3022
3027{
3028 unsigned int vertexCount = cm.vertices.size();
3029 unsigned int elementCount = cm.indices.size();
3030 unsigned int *elements;
3031 float *positions, *normals, *textureCoordinates;
3032 VuoMesh_allocateCPUBuffers(vertexCount, &positions, &normals, &textureCoordinates, nullptr, elementCount, &elements);
3033
3034 const csgjs_vertex *vertex = &cm.vertices[0];
3035 for (unsigned int n = 0; n < vertexCount; ++n)
3036 {
3037 positions[n * 3 ] = vertex[n].pos.x;
3038 positions[n * 3 + 1] = vertex[n].pos.y;
3039 positions[n * 3 + 2] = vertex[n].pos.z;
3040 normals[n * 3 ] = vertex[n].normal.x;
3041 normals[n * 3 + 1] = vertex[n].normal.y;
3042 normals[n * 3 + 2] = vertex[n].normal.z;
3043 textureCoordinates[n * 2 ] = vertex[n].uv.x;
3044 textureCoordinates[n * 2 + 1] = vertex[n].uv.y;
3045 }
3046
3047 const int *index = &cm.indices[0];
3048 for (unsigned int n = 0; n < elementCount; ++n)
3049 elements[n] = index[n];
3050
3051 VuoMesh mesh = VuoMesh_makeFromCPUBuffers(vertexCount, positions, normals, textureCoordinates, nullptr, elementCount, elements, VuoMesh_IndividualTriangles);
3052
3054}
3055
3060static float convertQualityToEpsilon(float quality)
3061{
3062 return pow(10, -VuoReal_clamp(quality, 0, 1) * 5.);
3063}
3064
3069{
3070 float epsilon = convertQualityToEpsilon(quality);
3071
3072 unsigned long objectCount = VuoListGetCount_VuoSceneObject(objects);
3073 if (objectCount == 0)
3074 return nullptr;
3075 if (objectCount == 1)
3076 return VuoListGetValue_VuoSceneObject(objects, 1);
3077
3078 dispatch_queue_t queue = dispatch_queue_create("org.vuo.sceneobject.union", DISPATCH_QUEUE_CONCURRENT);
3079
3080 csgjs_model *models = new csgjs_model[objectCount];
3081 for (unsigned long i = 0; i < objectCount; ++i)
3082 dispatch_async(queue, ^{
3084 });
3085
3086 dispatch_barrier_sync(queue, ^{});
3087 dispatch_release(queue);
3088
3089 csgjs_model cu = models[0];
3090 for (unsigned long i = 1; i < objectCount; ++i)
3091 cu = csgjs_union(cu, models[i], epsilon);
3092 delete[] models;
3094}
3095
3100{
3101 float epsilon = convertQualityToEpsilon(quality);
3102
3103 dispatch_semaphore_t finished = dispatch_semaphore_create(0);
3104
3105 __block csgjs_model ca;
3106 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3108 dispatch_semaphore_signal(finished);
3109 });
3110
3111 csgjs_model cb = VuoSceneObject_getCsgjsModel(b);
3112
3113 dispatch_semaphore_wait(finished, DISPATCH_TIME_FOREVER);
3114 dispatch_release(finished);
3115
3116 csgjs_model d = csgjs_difference(ca, cb, epsilon);
3118}
3119
3124{
3125 float epsilon = convertQualityToEpsilon(quality);
3126
3127 unsigned long objectCount = VuoListGetCount_VuoSceneObject(objects);
3128 if (objectCount == 0)
3129 return nullptr;
3130 if (objectCount == 1)
3131 return VuoListGetValue_VuoSceneObject(objects, 1);
3132
3133 dispatch_queue_t queue = dispatch_queue_create("org.vuo.sceneobject.intersect", DISPATCH_QUEUE_CONCURRENT);
3134
3135 csgjs_model *models = new csgjs_model[objectCount];
3136 for (unsigned long i = 0; i < objectCount; ++i)
3137 dispatch_async(queue, ^{
3139 });
3140
3141 dispatch_barrier_sync(queue, ^{});
3142 dispatch_release(queue);
3143
3144 csgjs_model ci = models[0];
3145 for (unsigned long i = 1; i < objectCount; ++i)
3146 ci = csgjs_intersection(ci, models[i], epsilon);
3147 delete[] models;
3149}