Vuo  2.4.0
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 "type.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
399{
400 return VuoSceneObject_makeMesh(VuoMesh_makeCube(), shader, transform);
401}
402
409{
411}
412
419{
421}
422
429{
430 return VuoSceneObject_makeCubeMulti(transform, 2, 2, 2,
437}
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, 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 // 0 and 1000 come from "Realtime Dome Imaging and Interaction" by Bailey/Clothier/Gebbie 2006.
592 so->camera.distanceMin = 0;
593 so->camera.distanceMax = 1000;
594
595 so->camera.vignetteWidth = vignetteWidth;
596 so->camera.vignetteSharpness = vignetteSharpness;
597
598 return (VuoSceneObject)so;
599}
600
605{
607 VuoPoint3d_make(0,0,1),
608 VuoPoint3d_make(0,0,0),
609 VuoPoint3d_make(1,1,1)
610 );
612 VuoText_make("default camera"),
613 transform,
614 90,
615 0.1,
616 10.0
617 );
618}
619
629bool VuoSceneObject_find(VuoSceneObject sceneObject, VuoText nameToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
630{
631 if (!sceneObject)
632 return false;
633
634 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
635
636 if (VuoText_areEqual(so->name, nameToMatch))
637 {
638 *foundObject = (VuoSceneObject)so;
639 return true;
640 }
641
642 if (so->type == VuoSceneObjectSubType_Group)
643 {
644 VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
645
646 unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
647 for (unsigned long i = 1; i <= childObjectCount; ++i)
648 {
649 VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
650 if (VuoSceneObject_find(childObject, nameToMatch, ancestorObjects, foundObject))
651 return true;
652 }
653
655 }
656
657 return false;
658}
659
670bool VuoSceneObject_findById(VuoSceneObject sceneObject, uint64_t idToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
671{
672 if (!sceneObject)
673 return false;
674
675 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
676
677 if (so->id == idToMatch)
678 {
679 *foundObject = (VuoSceneObject)so;
680 return true;
681 }
682
683 if (so->type == VuoSceneObjectSubType_Group)
684 {
685 VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
686
687 unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
688 for (unsigned long i = 1; i <= childObjectCount; ++i)
689 {
690 VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
691 if (VuoSceneObject_findById(childObject, idToMatch, ancestorObjects, foundObject))
692 return true;
693 }
694
696 }
697
698 return false;
699}
700
712{
713 if (!sceneObject)
714 return false;
715
716 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
717
718 if (so->type == typeToMatch)
719 {
720 *foundObject = (VuoSceneObject)so;
721 return true;
722 }
723
724 if (so->type == VuoSceneObjectSubType_Group)
725 {
726 VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
727
728 unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
729
730 for (unsigned long i = 1; i <= childObjectCount; ++i)
731 {
732 VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
733
734 if (VuoSceneObject_findWithType(childObject, typeToMatch, ancestorObjects, foundObject))
735 return true;
736 }
737
739 }
740
741 return false;
742}
743
752bool VuoSceneObject_findCamera(VuoSceneObject sceneObject, VuoText nameToMatch, VuoSceneObject *foundCamera)
753{
754 if (!sceneObject)
755 return false;
756
757 __block bool didFindCamera = false;
758 VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
759 VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
760 if ((co->type == VuoSceneObjectSubType_PerspectiveCamera
761 || co->type == VuoSceneObjectSubType_StereoCamera
762 || co->type == VuoSceneObjectSubType_OrthographicCamera
763 || co->type == VuoSceneObjectSubType_FisheyeCamera)
764 && (!nameToMatch || (co->name && nameToMatch && strstr(co->name, nameToMatch))))
765 {
766 *foundCamera = currentObject;
767 VuoSceneObject_setTransform(*foundCamera, VuoTransform_makeFromMatrix4x4(modelviewMatrix));
768 didFindCamera = true;
769 return false;
770 }
771 return true;
772 });
773
774 return didFindCamera;
775}
776
781{
782 if (!sceneObject)
783 return false;
784
785 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
786 return so->type != VuoSceneObjectSubType_Empty;
787}
788
793{
794 if (strcmp(typeString,"empty")==0)
795 return VuoSceneObjectSubType_Empty;
796 else if (strcmp(typeString,"group")==0)
797 return VuoSceneObjectSubType_Group;
798 else if (strcmp(typeString,"mesh")==0)
799 return VuoSceneObjectSubType_Mesh;
800 else if (strcmp(typeString,"camera-perspective")==0)
801 return VuoSceneObjectSubType_PerspectiveCamera;
802 else if (strcmp(typeString,"camera-stereo")==0)
803 return VuoSceneObjectSubType_StereoCamera;
804 else if (strcmp(typeString,"camera-orthographic")==0)
805 return VuoSceneObjectSubType_OrthographicCamera;
806 else if (strcmp(typeString,"camera-fisheye")==0)
807 return VuoSceneObjectSubType_FisheyeCamera;
808 else if (strcmp(typeString,"light-ambient")==0)
809 return VuoSceneObjectSubType_AmbientLight;
810 else if (strcmp(typeString,"light-point")==0)
811 return VuoSceneObjectSubType_PointLight;
812 else if (strcmp(typeString,"light-spot")==0)
813 return VuoSceneObjectSubType_Spotlight;
814 else if (strcmp(typeString,"text")==0)
815 return VuoSceneObjectSubType_Text;
816
817 return VuoSceneObjectSubType_Empty;
818}
819
824{
825 switch (type)
826 {
827 case VuoSceneObjectSubType_Group:
828 return "group";
829 case VuoSceneObjectSubType_Mesh:
830 return "mesh";
831 case VuoSceneObjectSubType_PerspectiveCamera:
832 return "camera-perspective";
833 case VuoSceneObjectSubType_StereoCamera:
834 return "camera-stereo";
835 case VuoSceneObjectSubType_OrthographicCamera:
836 return "camera-orthographic";
837 case VuoSceneObjectSubType_FisheyeCamera:
838 return "camera-fisheye";
839 case VuoSceneObjectSubType_AmbientLight:
840 return "light-ambient";
841 case VuoSceneObjectSubType_PointLight:
842 return "light-point";
843 case VuoSceneObjectSubType_Spotlight:
844 return "light-spot";
845 case VuoSceneObjectSubType_Text:
846 return "text";
847 // VuoSceneObjectSubType_Empty
848 default:
849 return "empty";
850 }
851}
852
858{
859 VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
860 so->type = VuoSceneObjectSubType_AmbientLight;
862 so->light.color = color;
863 so->light.brightness = brightness;
864 return (VuoSceneObject)so;
865}
866
877VuoSceneObject VuoSceneObject_makePointLight(VuoColor color, float brightness, VuoPoint3d position, float range, float sharpness)
878{
879 VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
880 so->type = VuoSceneObjectSubType_PointLight;
881 VuoText t = VuoText_make("Point Light");
883 so->light.color = color;
884 so->light.brightness = brightness;
885 so->light.range = range;
886 so->light.sharpness = MAX(MIN(sharpness,1),0);
887 so->transform = VuoTransform_makeEuler(position, VuoPoint3d_make(0,0,0), VuoPoint3d_make(1,1,1));
888 return (VuoSceneObject)so;
889}
890
902VuoSceneObject VuoSceneObject_makeSpotlight(VuoColor color, float brightness, VuoTransform transform, float cone, float range, float sharpness)
903{
904 VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
905 so->type = VuoSceneObjectSubType_Spotlight;
907 so->light.color = color;
908 so->light.brightness = brightness;
909 so->light.cone = cone;
910 so->light.range = range;
911 so->light.sharpness = MAX(MIN(sharpness,1),0);
912 so->transform = transform;
913 return (VuoSceneObject)so;
914}
915
924void VuoSceneObject_findLights(VuoSceneObject sceneObject, VuoColor *ambientColor, float *ambientBrightness, VuoList_VuoSceneObject *pointLights, VuoList_VuoSceneObject *spotLights)
925{
926 __block VuoList_VuoColor ambientColors = VuoListCreate_VuoColor();
927 VuoRetain(ambientColors);
928
929 *ambientBrightness = 0;
930 *pointLights = VuoListCreate_VuoSceneObject();
931 *spotLights = VuoListCreate_VuoSceneObject();
932
933 VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
934 VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
935 if (co->type == VuoSceneObjectSubType_AmbientLight)
936 {
937 VuoListAppendValue_VuoColor(ambientColors, co->light.color);
938 *ambientBrightness += co->light.brightness;
939 }
940 else if (co->type == VuoSceneObjectSubType_PointLight)
941 {
944 VuoListAppendValue_VuoSceneObject(*pointLights, l);
945 }
946 else if (co->type == VuoSceneObjectSubType_Spotlight)
947 {
950 VuoListAppendValue_VuoSceneObject(*spotLights, l);
951 }
952 return true;
953 });
954
955 if (!VuoListGetCount_VuoColor(ambientColors)
956 && !VuoListGetCount_VuoSceneObject(*pointLights)
957 && !VuoListGetCount_VuoSceneObject(*spotLights))
958 {
959 *ambientColor = VuoColor_makeWithRGBA(1,1,1,1);
960 *ambientBrightness = 0.05;
961
962 // https://en.wikipedia.org/wiki/Three-point_lighting
963
965 VuoListAppendValue_VuoSceneObject(*pointLights, keyLight);
966
968 VuoListAppendValue_VuoSceneObject(*pointLights, fillLight);
969
970 VuoSceneObject backLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .15, VuoPoint3d_make(1,.75,-.5), 5, 0);
971 VuoListAppendValue_VuoSceneObject(*pointLights, backLight);
972 }
973 else
974 *ambientColor = VuoColor_average(ambientColors);
975
976 VuoRelease(ambientColors);
977}
978
982typedef struct
983{
984 long objectCount;
985 const VuoSceneObject *objects;
986 float modelviewMatrix[16];
988
1001void VuoSceneObject_visit(const VuoSceneObject object, bool (^function)(const VuoSceneObject currentObject, float modelviewMatrix[16]))
1002{
1003 if (!object)
1004 return;
1005
1006 VuoSceneObject_treeState rootState;
1007 rootState.objectCount = 1;
1008 rootState.objects = &object;
1009 VuoTransform_getMatrix(VuoTransform_makeIdentity(), rootState.modelviewMatrix);
1010
1011 std::list<VuoSceneObject_treeState> objectsToVisit(1, rootState);
1012 while (!objectsToVisit.empty())
1013 {
1014 VuoSceneObject_treeState currentState = objectsToVisit.front();
1015 objectsToVisit.pop_front();
1016
1017 for (long i = 0; i < currentState.objectCount; ++i)
1018 {
1019 VuoSceneObject_internal *currentObject = (VuoSceneObject_internal *)currentState.objects[i];
1020 if (!currentObject)
1021 continue;
1022
1023 float localModelviewMatrix[16];
1024 VuoTransform_getMatrix(currentObject->transform, localModelviewMatrix);
1025 float compositeModelviewMatrix[16];
1026 VuoTransform_multiplyMatrices4x4(localModelviewMatrix, currentState.modelviewMatrix, compositeModelviewMatrix);
1027
1028 if (!function((VuoSceneObject)currentObject, compositeModelviewMatrix))
1029 return;
1030
1031 if (currentObject->type == VuoSceneObjectSubType_Group)
1032 {
1033 // Prepend this object's childObjects to the objectsToVisit queue.
1034 long childObjectCount = VuoListGetCount_VuoSceneObject(currentObject->childObjects);
1035 if (childObjectCount)
1036 {
1037 VuoSceneObject_treeState childState;
1038 childState.objectCount = childObjectCount;
1039 childState.objects = VuoListGetData_VuoSceneObject(currentObject->childObjects);
1040 memcpy(childState.modelviewMatrix, compositeModelviewMatrix, sizeof(float[16]));
1041 objectsToVisit.push_front(childState);
1042 }
1043 }
1044 }
1045 }
1046}
1047
1051static void VuoSceneObject_applyInternal(VuoSceneObject sceneObject, void (^function)(VuoSceneObject currentObject, float modelviewMatrix[16]), float modelviewMatrix[16])
1052{
1053 if (!sceneObject)
1054 return;
1055
1056 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1057
1058 float localModelviewMatrix[16];
1059 VuoTransform_getMatrix(so->transform, localModelviewMatrix);
1060 float compositeModelviewMatrix[16];
1061 VuoTransform_multiplyMatrices4x4(localModelviewMatrix, modelviewMatrix, compositeModelviewMatrix);
1062
1063 function(sceneObject, compositeModelviewMatrix);
1064
1065 if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
1066 {
1067 unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
1068 for (unsigned long i = 1; i <= childObjectCount; ++i)
1069 {
1070 VuoSceneObject o = VuoListGetValue_VuoSceneObject(so->childObjects, i);
1071 VuoSceneObject_applyInternal(o, function, compositeModelviewMatrix);
1072 VuoListSetValue_VuoSceneObject(so->childObjects, o, i, false);
1073 }
1074 }
1075}
1076
1086void VuoSceneObject_apply(VuoSceneObject object, void (^function)(VuoSceneObject currentObject, float modelviewMatrix[16]))
1087{
1088 if (!object)
1089 return;
1090
1091 float localModelviewMatrix[16];
1092 VuoTransform_getMatrix(VuoTransform_makeIdentity(), localModelviewMatrix);
1093
1094 VuoSceneObject_applyInternal(object, function, localModelviewMatrix);
1095}
1096
1103{
1104 if (!object)
1105 return;
1106
1107 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1108 so->transform = VuoTransform_composite(so->transform, transform);
1109}
1110
1116void VuoSceneObject_translate(VuoSceneObject object, VuoPoint3d translation)
1117{
1118 if (!object)
1119 return;
1120
1121 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1122 so->transform.translation += translation;
1123}
1124
1130void VuoSceneObject_scale(VuoSceneObject object, VuoPoint3d scale)
1131{
1132 if (!object)
1133 return;
1134
1135 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1136 so->transform.scale *= scale;
1137}
1138
1145{
1146 if (!object)
1147 return nullptr;
1148
1149 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1150 return so->name;
1151}
1152
1162{
1163 if (!object)
1164 return nullptr;
1165
1166 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1167 if (so->type != VuoSceneObjectSubType_Group)
1168 return nullptr;
1169
1170 return so->childObjects;
1171}
1172
1179{
1180 if (!object)
1181 return VuoBlendMode_Normal;
1182
1183 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1184 return so->blendMode;
1185}
1186
1193{
1194 if (!object)
1195 return VuoSceneObjectSubType_Empty;
1196
1197 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1198 return so->type;
1199}
1200
1208{
1209 if (!object)
1210 return 0;
1211
1212 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1213 return so->id;
1214}
1215
1225{
1226 if (!object)
1227 return nullptr;
1228
1229 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1230 return so->shader;
1231}
1232
1240{
1241 if (!object)
1242 return false;
1243
1244 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1245 return so->isRealSize;
1246}
1247
1256{
1257 if (!object)
1258 return false;
1259
1260 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1261 return so->preservePhysicalSize;
1262}
1263
1273{
1274 if (!object)
1275 return nullptr;
1276
1277 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1278 return so->mesh;
1279}
1280
1287{
1288 if (!object)
1289 return nullptr;
1290
1291 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1292 return so->text.text;
1293}
1294
1301{
1302 if (!object)
1303 return VuoFont_makeDefault();
1304
1305 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1306 return so->text.font;
1307}
1308
1317{
1318 if (!object)
1319 return false;
1320
1321 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1322 return so->text.scaleWithScene;
1323}
1324
1331{
1332 if (!object)
1333 return 0;
1334
1335 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1336 return so->text.wrapWidth;
1337}
1338
1345{
1346 if (!object)
1347 return 0;
1348
1349 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1350 return so->camera.fieldOfView;
1351}
1352
1359{
1360 if (!object)
1361 return 0;
1362
1363 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1364 return so->camera.width;
1365}
1366
1373{
1374 if (!object)
1375 return 0;
1376
1377 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1378 return so->camera.distanceMin;
1379}
1380
1387{
1388 if (!object)
1389 return 0;
1390
1391 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1392 return so->camera.distanceMax;
1393}
1394
1401{
1402 if (!object)
1403 return 0;
1404
1405 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1406 return so->camera.vignetteWidth;
1407}
1408
1415{
1416 if (!object)
1417 return 0;
1418
1419 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1420 return so->camera.vignetteSharpness;
1421}
1422
1429{
1430 if (!object)
1431 return 0;
1432
1433 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1434 return so->camera.intraocularDistance;
1435}
1436
1443{
1444 if (!object)
1445 return 0;
1446
1447 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1448 return so->camera.confocalDistance;
1449}
1450
1457{
1458 if (!object)
1459 return (VuoColor){0,0,0,0};
1460
1461 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1462 return so->light.color;
1463}
1464
1471{
1472 if (!object)
1473 return 0;
1474
1475 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1476 return so->light.brightness;
1477}
1478
1485{
1486 if (!object)
1487 return 0;
1488
1489 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1490 return so->light.range;
1491}
1492
1499{
1500 if (!object)
1501 return 0;
1502
1503 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1504 return so->light.sharpness;
1505}
1506
1513{
1514 if (!object)
1515 return 0;
1516
1517 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1518 return so->light.cone;
1519}
1520
1527{
1528 if (!object)
1530
1531 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1532 return so->transform;
1533}
1534
1541{
1542 if (!object)
1543 return (VuoPoint3d){0,0,0};
1544
1545 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1546 return so->transform.translation;
1547}
1548
1555{
1556 if (!object)
1557 return;
1558
1559 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1560 so->type = type;
1561}
1562
1569void VuoSceneObject_setId(VuoSceneObject object, uint64_t id)
1570{
1571 if (!object)
1572 return;
1573
1574 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1575 so->id = id;
1576}
1577
1584{
1585 if (!object)
1586 return;
1587
1588 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1589 VuoRetain(name);
1590 VuoRelease(so->name);
1591 so->name = name;
1592}
1593
1600{
1601 if (!object)
1602 return;
1603
1604 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1605 if (so->type != VuoSceneObjectSubType_Group)
1606 return;
1607
1608 VuoRetain(childObjects);
1609 VuoRelease(so->childObjects);
1610 so->childObjects = childObjects;
1611}
1612
1619{
1620 if (!object)
1621 return;
1622
1623 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1624 VuoRetain(mesh);
1625 VuoRelease(so->mesh);
1626 so->mesh = mesh;
1627}
1628
1635{
1636 if (!object)
1637 return;
1638
1639 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1640 so->transform = transform;
1641}
1642
1648void VuoSceneObject_setTranslation(VuoSceneObject object, VuoPoint3d translation)
1649{
1650 if (!object)
1651 return;
1652
1653 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1654 so->transform.translation = translation;
1655}
1656
1662void VuoSceneObject_setScale(VuoSceneObject object, VuoPoint3d scale)
1663{
1664 if (!object)
1665 return;
1666
1667 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1668 so->transform.scale = scale;
1669}
1670
1677{
1678 if (!object)
1679 return;
1680
1681 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1682 VuoRetain(shader);
1683 VuoRelease(so->shader);
1684 so->shader = shader;
1685}
1686
1691{
1692 if (!object)
1693 return;
1694
1695 VuoSceneObject_apply(object, ^(VuoSceneObject currentObject, float modelviewMatrix[16]){
1697 VuoMesh_setFaceCulling(m, faceCulling);
1698 VuoSceneObject_setMesh(currentObject, m);
1699 });
1700}
1701
1715{
1716 if (!object)
1717 return;
1718
1719 VuoSceneObject_apply(object, ^(VuoSceneObject currentObject, float modelviewMatrix[16]){
1720 VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
1721 co->blendMode = blendMode;
1722 });
1723}
1724
1731void VuoSceneObject_setRealSize(VuoSceneObject object, bool isRealSize)
1732{
1733 if (!object)
1734 return;
1735
1736 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1737 so->isRealSize = isRealSize;
1738}
1739
1746void VuoSceneObject_setPreservePhysicalSize(VuoSceneObject object, bool shouldPreservePhysicalSize)
1747{
1748 if (!object)
1749 return;
1750
1751 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1752 so->preservePhysicalSize = shouldPreservePhysicalSize;
1753}
1754
1761{
1762 if (!object)
1763 return;
1764
1765 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1766 VuoRetain(text);
1767 VuoRelease(so->text.text);
1768 so->text.text = text;
1769}
1770
1777{
1778 if (!object)
1779 return;
1780
1781 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1782 VuoFont_retain(font);
1783 VuoFont_release(so->text.font);
1784 so->text.font = font;
1785}
1786
1793{
1794 if (!object)
1795 return;
1796
1797 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1798 so->camera.fieldOfView = fieldOfView;
1799}
1800
1807{
1808 if (!object)
1809 return;
1810
1811 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1812 so->camera.distanceMin = distanceMin;
1813}
1814
1821{
1822 if (!object)
1823 return;
1824
1825 VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1826 so->camera.distanceMax = distanceMax;
1827}
1828
1840{
1841 if (!object)
1842 return nullptr;
1843
1844 VuoSceneObject_internal *o = (VuoSceneObject_internal *)object;
1845
1846 VuoSceneObject_internal *co = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
1847
1848 co->type = o->type;
1849 co->id = o->id;
1851 co->transform = o->transform;
1853 VuoSceneObject_setShader((VuoSceneObject)co, o->shader); // @todo
1854 co->isRealSize = o->isRealSize;
1855 co->preservePhysicalSize = o->preservePhysicalSize;
1856 co->blendMode = o->blendMode;
1857
1858 if (o->type == VuoSceneObjectSubType_Group && o->childObjects)
1859 {
1860 co->childObjects = VuoListCreate_VuoSceneObject();
1861 VuoRetain(co->childObjects);
1862 VuoListForeach_VuoSceneObject(o->childObjects, ^(const VuoSceneObject object){
1863 VuoListAppendValue_VuoSceneObject(co->childObjects, VuoSceneObject_copy(object));
1864 return true;
1865 });
1866 }
1867
1868 if (o->type == VuoSceneObjectSubType_PerspectiveCamera
1869 || o->type == VuoSceneObjectSubType_StereoCamera
1870 || o->type == VuoSceneObjectSubType_OrthographicCamera
1871 || o->type == VuoSceneObjectSubType_FisheyeCamera)
1872 {
1873 co->camera.fieldOfView = o->camera.fieldOfView;
1874 co->camera.width = o->camera.width;
1875 co->camera.distanceMin = o->camera.distanceMin;
1876 co->camera.distanceMax = o->camera.distanceMax;
1877 co->camera.confocalDistance = o->camera.confocalDistance;
1878 co->camera.intraocularDistance = o->camera.intraocularDistance;
1879 co->camera.vignetteWidth = o->camera.vignetteWidth;
1880 co->camera.vignetteSharpness = o->camera.vignetteSharpness;
1881 }
1882 else if (o->type == VuoSceneObjectSubType_AmbientLight
1883 || o->type == VuoSceneObjectSubType_PointLight
1884 || o->type == VuoSceneObjectSubType_Spotlight)
1885 {
1886 co->light.color = o->light.color;
1887 co->light.brightness = o->light.brightness;
1888 co->light.range = o->light.range;
1889 co->light.cone = o->light.cone;
1890 co->light.sharpness = o->light.sharpness;
1891 }
1892 else if (o->type == VuoSceneObjectSubType_Text)
1893 {
1894 VuoSceneObject_setText((VuoSceneObject)co, o->text.text);
1895 VuoSceneObject_setTextFont((VuoSceneObject)co, o->text.font);
1896 co->text.scaleWithScene = o->text.scaleWithScene;
1897 co->text.wrapWidth = o->text.wrapWidth;
1898 }
1899
1900 return (VuoSceneObject)co;
1901}
1902
1907{
1908 if (!so)
1909 return VuoBox_make((VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0});
1910
1911 __block bool haveGlobalBounds = false;
1912 __block VuoBox globalBounds;
1913
1914 VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
1915 VuoBox bounds;
1916 bool foundBounds = VuoSceneObject_meshBounds(currentObject, &bounds, modelviewMatrix);
1917 if (foundBounds)
1918 {
1919 globalBounds = haveGlobalBounds ? VuoBox_encapsulate(globalBounds, bounds) : bounds;
1920 haveGlobalBounds = true;
1921 }
1922 return true;
1923 });
1924
1925 if (haveGlobalBounds)
1926 return globalBounds;
1927 else
1928 return VuoBox_make( (VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0} );
1929}
1930
1934bool VuoSceneObject_meshBounds(const VuoSceneObject sceneObject, VuoBox *bounds, float matrix[16])
1935{
1936 if (!sceneObject)
1937 return false;
1938
1939 if (VuoSceneObject_getVertexCount(sceneObject) < 1)
1940 return false;
1941
1942 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1943 if (so->isRealSize
1944 || so->type == VuoSceneObjectSubType_Text)
1945 {
1946 // We don't know what the actual rendered size of the realSize layer will be,
1947 // but we can at least include its center point.
1948 *bounds = VuoBox_make(VuoPoint3d_make(matrix[12], matrix[13], matrix[14]), VuoPoint3d_make(0,0,0));
1949 }
1950 else
1951 {
1952 *bounds = VuoMesh_bounds(so->mesh, matrix);
1953
1954 if (so->shader)
1955 {
1956 bounds->size.x *= so->shader->objectScale;
1957 bounds->size.y *= so->shader->objectScale;
1958 bounds->size.z *= so->shader->objectScale;
1959 }
1960 }
1961
1962 return true;
1963}
1964
1969{
1970 if (!sceneObject)
1971 return;
1972
1973 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1974 VuoBox bounds = VuoSceneObject_bounds(sceneObject);
1975 so->transform.translation = VuoPoint3d_subtract(so->transform.translation, bounds.center);
1976}
1977
1984{
1985 if (!sceneObject)
1986 return;
1987 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1988 VuoBox bounds = VuoSceneObject_bounds(sceneObject);
1989
1990 float scale = fmax(fmax(bounds.size.x, bounds.size.y), bounds.size.z);
1991 if (fabs(scale) < 0.00001)
1992 return;
1993
1994 so->transform.scale = VuoPoint3d_multiply(so->transform.scale, 1./scale);
1995 so->transform.translation = VuoPoint3d_multiply(so->transform.translation, 1./scale);
1996}
1997
2024{
2025 json_object *o = NULL;
2026
2027 int id = 0;
2028 if (json_object_object_get_ex(js, "id", &o))
2029 id = json_object_get_int64(o);
2030
2031 VuoSceneObjectSubType type = VuoSceneObjectSubType_Empty;
2032 if (json_object_object_get_ex(js, "type", &o))
2033 type = VuoSceneObject_typeFromCString(json_object_get_string(o));
2034
2035 VuoMesh mesh = NULL;
2036 if (json_object_object_get_ex(js, "mesh", &o))
2037 mesh = VuoMesh_makeFromJson(o);
2038
2039 VuoShader shader = NULL;
2040 if (json_object_object_get_ex(js, "shader", &o))
2041 shader = VuoShader_makeFromJson(o);
2042
2043 bool isRealSize = false;
2044 if (json_object_object_get_ex(js, "isRealSize", &o))
2045 isRealSize = VuoBoolean_makeFromJson(o);
2046
2047 bool preservePhysicalSize = false;
2048 if (json_object_object_get_ex(js, "preservePhysicalSize", &o))
2049 preservePhysicalSize = VuoBoolean_makeFromJson(o);
2050
2052 if (json_object_object_get_ex(js, "blendMode", &o))
2053 blendMode = VuoBlendMode_makeFromJson(o);
2054
2055 VuoList_VuoSceneObject childObjects = NULL;
2056 if (json_object_object_get_ex(js, "childObjects", &o))
2057 childObjects = VuoList_VuoSceneObject_makeFromJson(o);
2058
2059 float cameraFieldOfView;
2060 if (json_object_object_get_ex(js, "cameraFieldOfView", &o))
2061 cameraFieldOfView = json_object_get_double(o);
2062
2063 float cameraWidth;
2064 if (json_object_object_get_ex(js, "cameraWidth", &o))
2065 cameraWidth = json_object_get_double(o);
2066
2067 float cameraDistanceMin;
2068 if (json_object_object_get_ex(js, "cameraDistanceMin", &o))
2069 cameraDistanceMin = json_object_get_double(o);
2070
2071 float cameraDistanceMax;
2072 if (json_object_object_get_ex(js, "cameraDistanceMax", &o))
2073 cameraDistanceMax = json_object_get_double(o);
2074
2075 float cameraConfocalDistance;
2076 if (json_object_object_get_ex(js, "cameraConfocalDistance", &o))
2077 cameraConfocalDistance = json_object_get_double(o);
2078
2079 float cameraIntraocularDistance;
2080 if (json_object_object_get_ex(js, "cameraIntraocularDistance", &o))
2081 cameraIntraocularDistance = json_object_get_double(o);
2082
2083 float cameraVignetteWidth;
2084 if (json_object_object_get_ex(js, "cameraVignetteWidth", &o))
2085 cameraVignetteWidth = json_object_get_double(o);
2086
2087 float cameraVignetteSharpness;
2088 if (json_object_object_get_ex(js, "cameraVignetteSharpness", &o))
2089 cameraVignetteSharpness = json_object_get_double(o);
2090
2091 VuoColor lightColor;
2092 if (json_object_object_get_ex(js, "lightColor", &o))
2093 lightColor = VuoColor_makeFromJson(o);
2094
2095 float lightBrightness;
2096 if (json_object_object_get_ex(js, "lightBrightness", &o))
2097 lightBrightness = json_object_get_double(o);
2098
2099 float lightCone;
2100 if (json_object_object_get_ex(js, "lightCone", &o))
2101 lightCone = json_object_get_double(o);
2102
2103 float lightRange;
2104 if (json_object_object_get_ex(js, "lightRange", &o))
2105 lightRange = json_object_get_double(o);
2106
2107 float lightSharpness;
2108 if (json_object_object_get_ex(js, "lightSharpness", &o))
2109 lightSharpness = json_object_get_double(o);
2110
2111 VuoText name = NULL;
2112 if (json_object_object_get_ex(js, "name", &o))
2113 name = VuoText_makeFromJson(o);
2114
2115 json_object_object_get_ex(js, "transform", &o);
2117
2118 VuoText text = NULL;
2119 if (json_object_object_get_ex(js, "text", &o))
2120 text = VuoText_makeFromJson(o);
2121
2122 VuoFont font;
2123 if (json_object_object_get_ex(js, "textFont", &o))
2124 font = VuoFont_makeFromJson(o);
2125
2126 bool scaleWithScene = false;
2127 if (json_object_object_get_ex(js, "textScaleWithScene", &o))
2128 scaleWithScene = VuoBoolean_makeFromJson(o);
2129
2130 float wrapWidth = INFINITY;
2131 if (json_object_object_get_ex(js, "textWrapWidth", &o))
2132 wrapWidth = json_object_get_double(o);
2133
2134 VuoSceneObject obj;
2135 switch (type)
2136 {
2137 case VuoSceneObjectSubType_Empty:
2138 obj = nullptr;
2139 break;
2140 case VuoSceneObjectSubType_Group:
2141 obj = VuoSceneObject_makeGroup(childObjects, transform);
2142 break;
2143 case VuoSceneObjectSubType_Mesh:
2144 {
2145 obj = VuoSceneObject_makeMesh(mesh, shader, transform);
2146 VuoSceneObject_internal *so = (VuoSceneObject_internal *)obj;
2147 so->isRealSize = isRealSize;
2148 so->preservePhysicalSize = preservePhysicalSize;
2149 so->blendMode = blendMode;
2150 VuoSceneObject_setName(obj, name);
2151 break;
2152 }
2153 case VuoSceneObjectSubType_PerspectiveCamera:
2155 name,
2156 transform,
2157 cameraFieldOfView,
2158 cameraDistanceMin,
2159 cameraDistanceMax
2160 );
2161 break;
2162 case VuoSceneObjectSubType_StereoCamera:
2164 name,
2165 transform,
2166 cameraFieldOfView,
2167 cameraDistanceMin,
2168 cameraDistanceMax,
2169 cameraConfocalDistance,
2170 cameraIntraocularDistance
2171 );
2172 break;
2173 case VuoSceneObjectSubType_OrthographicCamera:
2175 name,
2176 transform,
2177 cameraWidth,
2178 cameraDistanceMin,
2179 cameraDistanceMax
2180 );
2181 break;
2182 case VuoSceneObjectSubType_FisheyeCamera:
2184 name,
2185 transform,
2186 cameraFieldOfView,
2187 cameraVignetteWidth,
2188 cameraVignetteSharpness
2189 );
2190 break;
2191 case VuoSceneObjectSubType_AmbientLight:
2192 obj = VuoSceneObject_makeAmbientLight(lightColor, lightBrightness);
2193 break;
2194 case VuoSceneObjectSubType_PointLight:
2195 obj = VuoSceneObject_makePointLight(lightColor, lightBrightness, transform.translation, lightRange, lightSharpness);
2196 break;
2197 case VuoSceneObjectSubType_Spotlight:
2198 obj = VuoSceneObject_makeSpotlight(lightColor, lightBrightness, transform, lightCone, lightRange, lightSharpness);
2199 break;
2200 case VuoSceneObjectSubType_Text:
2201 obj = VuoSceneObject_makeText(text, font, scaleWithScene, wrapWidth);
2202 VuoSceneObject_setTransform(obj, transform);
2203 VuoSceneObject_setMesh(obj, mesh);
2204 break;
2205 }
2206
2207 VuoSceneObject_setId(obj, id);
2208
2209 return obj;
2210}
2211
2216{
2217 if (!sceneObject)
2218 return nullptr;
2219
2220 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2221
2222 json_object *js = json_object_new_object();
2223
2224 json_object_object_add(js, "id", json_object_new_int64(so->id));
2225 json_object_object_add(js, "type", json_object_new_string(VuoSceneObject_cStringForType(so->type)));
2226
2227 switch (so->type)
2228 {
2229 case VuoSceneObjectSubType_Empty:
2230 break;
2231
2232 case VuoSceneObjectSubType_Mesh:
2233 if (so->mesh)
2234 json_object_object_add(js, "mesh", VuoMesh_getJson(so->mesh));
2235
2236 if (so->shader)
2237 json_object_object_add(js, "shader", VuoShader_getJson(so->shader));
2238
2239 json_object_object_add(js, "isRealSize", VuoBoolean_getJson(so->isRealSize));
2240
2241 json_object_object_add(js, "preservePhysicalSize", VuoBoolean_getJson(so->preservePhysicalSize));
2242
2243 if (so->blendMode != VuoBlendMode_Normal)
2244 json_object_object_add(js, "blendMode", VuoBlendMode_getJson(so->blendMode));
2245 break;
2246
2247 case VuoSceneObjectSubType_Group:
2248 if (so->childObjects)
2249 json_object_object_add(js, "childObjects", VuoList_VuoSceneObject_getJson(so->childObjects));
2250 break;
2251
2252 case VuoSceneObjectSubType_PerspectiveCamera:
2253 case VuoSceneObjectSubType_StereoCamera:
2254 case VuoSceneObjectSubType_OrthographicCamera:
2255 case VuoSceneObjectSubType_FisheyeCamera:
2256 {
2257 if (so->type != VuoSceneObjectSubType_FisheyeCamera)
2258 {
2259 json_object_object_add(js, "cameraDistanceMin", json_object_new_double(so->camera.distanceMin));
2260 json_object_object_add(js, "cameraDistanceMax", json_object_new_double(so->camera.distanceMax));
2261 }
2262
2263 if (so->type == VuoSceneObjectSubType_PerspectiveCamera
2264 || so->type == VuoSceneObjectSubType_StereoCamera
2265 || so->type == VuoSceneObjectSubType_FisheyeCamera)
2266 json_object_object_add(js, "cameraFieldOfView", json_object_new_double(so->camera.fieldOfView));
2267
2268 if (so->type == VuoSceneObjectSubType_StereoCamera)
2269 {
2270 json_object_object_add(js, "cameraConfocalDistance", json_object_new_double(so->camera.confocalDistance));
2271 json_object_object_add(js, "cameraIntraocularDistance", json_object_new_double(so->camera.intraocularDistance));
2272 }
2273
2274 if (so->type == VuoSceneObjectSubType_OrthographicCamera)
2275 json_object_object_add(js, "cameraWidth", json_object_new_double(so->camera.width));
2276
2277 if (so->type == VuoSceneObjectSubType_FisheyeCamera)
2278 {
2279 json_object_object_add(js, "cameraVignetteWidth", json_object_new_double(so->camera.vignetteWidth));
2280 json_object_object_add(js, "cameraVignetteSharpness", json_object_new_double(so->camera.vignetteSharpness));
2281 }
2282
2283 break;
2284 }
2285
2286 case VuoSceneObjectSubType_AmbientLight:
2287 case VuoSceneObjectSubType_PointLight:
2288 case VuoSceneObjectSubType_Spotlight:
2289 {
2290 json_object_object_add(js, "lightColor", VuoColor_getJson(so->light.color));
2291 json_object_object_add(js, "lightBrightness", json_object_new_double(so->light.brightness));
2292
2293 if (so->type == VuoSceneObjectSubType_PointLight
2294 || so->type == VuoSceneObjectSubType_Spotlight)
2295 {
2296 json_object_object_add(js, "lightRange", json_object_new_double(so->light.range));
2297 json_object_object_add(js, "lightSharpness", json_object_new_double(so->light.sharpness));
2298 }
2299 if (so->type == VuoSceneObjectSubType_Spotlight)
2300 json_object_object_add(js, "lightCone", json_object_new_double(so->light.cone));
2301
2302 break;
2303 }
2304
2305 case VuoSceneObjectSubType_Text:
2306 {
2307 if (so->text.text)
2308 json_object_object_add(js, "text", VuoText_getJson(so->text.text));
2309
2310 json_object_object_add(js, "textFont", VuoFont_getJson(so->text.font));
2311
2312 if (so->mesh)
2313 json_object_object_add(js, "mesh", VuoMesh_getJson(so->mesh));
2314
2315 json_object_object_add(js, "textScaleWithScene", VuoBoolean_getJson(so->text.scaleWithScene));
2316 json_object_object_add(js, "textWrapWidth", VuoReal_getJson(so->text.wrapWidth));
2317
2318 break;
2319 }
2320 }
2321
2322 if (so->name)
2323 json_object_object_add(js, "name", VuoText_getJson(so->name));
2324
2325 if (so->type != VuoSceneObjectSubType_AmbientLight)
2326 json_object_object_add(js, "transform", VuoTransform_getJson(so->transform));
2327
2328 return js;
2329}
2330
2334unsigned long VuoSceneObject_getVertexCount(const VuoSceneObject sceneObject)
2335{
2336 if (!sceneObject)
2337 return 0;
2338
2339 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2340 if (!so->mesh)
2341 return 0;
2342
2343 unsigned int vertexCount;
2344 VuoMesh_getCPUBuffers(so->mesh, &vertexCount, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
2345 return vertexCount;
2346}
2347
2351unsigned long VuoSceneObject_getElementCount(const VuoSceneObject sceneObject)
2352{
2353 if (!sceneObject)
2354 return 0;
2355
2356 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2357 if (!so->mesh)
2358 return 0;
2359
2360 unsigned int elementCount;
2361 VuoMesh_getCPUBuffers(so->mesh, nullptr, nullptr, nullptr, nullptr, nullptr, &elementCount, nullptr);
2362 return elementCount;
2363}
2364
2370void VuoSceneObject_getStatistics(const VuoSceneObject sceneObject, unsigned long *descendantCount, unsigned long *totalVertexCount, unsigned long *totalElementCount)
2371{
2372 if (!sceneObject)
2373 return;
2374
2375 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2376 unsigned long childObjectCount = 0;
2377 if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2378 childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2379 *descendantCount += childObjectCount;
2380 *totalVertexCount += VuoSceneObject_getVertexCount(sceneObject);
2381 *totalElementCount += VuoSceneObject_getElementCount(sceneObject);
2382
2383 if (so->type == VuoSceneObjectSubType_Group)
2384 for (unsigned long i = 1; i <= childObjectCount; ++i)
2385 VuoSceneObject_getStatistics(VuoListGetValue_VuoSceneObject(so->childObjects, i), descendantCount, totalVertexCount, totalElementCount);
2386}
2387
2392{
2393 if (!sceneObject)
2394 return nullptr;
2395
2396 // Exploit json_object's set-containing-only-unique-items data structure.
2397 __block json_object *names = json_object_new_object();
2398 VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2399 VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2400 if (co->shader)
2401 json_object_object_add(names, co->shader->name, NULL);
2402 return true;
2403 });
2404
2406 json_object_object_foreach(names, key, val)
2408 json_object_put(names);
2409 return nameList;
2410}
2411
2416{
2417 if (!VuoSceneObject_isPopulated(sceneObject))
2418 return strdup("No object");
2419
2420 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2421
2422 if (so->type == VuoSceneObjectSubType_Text)
2423 {
2424 char *fontSummary = VuoFont_getSummary(so->text.font);
2425 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);
2426 free(fontSummary);
2427 return textSummary;
2428 }
2429
2430 if (so->type == VuoSceneObjectSubType_PerspectiveCamera
2431 || so->type == VuoSceneObjectSubType_StereoCamera
2432 || so->type == VuoSceneObjectSubType_OrthographicCamera
2433 || so->type == VuoSceneObjectSubType_FisheyeCamera)
2434 {
2435 char *type = strdup(VuoSceneObject_cStringForType(so->type));
2436 type[0] = toupper(type[0]);
2437
2438 float cameraViewValue = 0;
2439 const char *cameraViewString = "";
2440 if (so->type == VuoSceneObjectSubType_PerspectiveCamera)
2441 {
2442 cameraViewValue = so->camera.fieldOfView;
2443 cameraViewString = "° field of view";
2444 }
2445 else if (so->type == VuoSceneObjectSubType_StereoCamera)
2446 {
2447 cameraViewValue = so->camera.fieldOfView;
2448 cameraViewString = "° field of view (stereoscopic)";
2449 }
2450 else if (so->type == VuoSceneObjectSubType_OrthographicCamera)
2451 {
2452 cameraViewValue = so->camera.width;
2453 cameraViewString = " unit width";
2454 }
2455 else if (so->type == VuoSceneObjectSubType_FisheyeCamera)
2456 {
2457 cameraViewValue = so->camera.fieldOfView;
2458 cameraViewString = "° field of view (fisheye)";
2459 }
2460
2461 char *translationString = VuoPoint3d_getSummary(so->transform.translation);
2462
2463 const char *rotationLabel;
2464 char *rotationString;
2465 if (so->transform.type == VuoTransformTypeEuler)
2466 {
2467 rotationLabel = "Rotated";
2468 rotationString = VuoPoint3d_getSummary(VuoPoint3d_multiply(so->transform.rotationSource.euler, -180.f/M_PI));
2469 }
2470 else
2471 {
2472 rotationLabel = "Target";
2473 rotationString = VuoPoint3d_getSummary(so->transform.rotationSource.target);
2474 }
2475
2476 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>",
2477 type, so->name ? so->name : "",
2478 translationString,
2479 rotationLabel, rotationString,
2480 cameraViewValue, cameraViewString,
2481 so->camera.distanceMin, so->camera.distanceMax);
2482 free(rotationString);
2483 free(translationString);
2484 free(type);
2485 return valueAsString;
2486 }
2487
2488 if (so->type == VuoSceneObjectSubType_AmbientLight
2489 || so->type == VuoSceneObjectSubType_PointLight
2490 || so->type == VuoSceneObjectSubType_Spotlight)
2491 {
2492 char *type = strdup(VuoSceneObject_cStringForType(so->type));
2493 type[0] = toupper(type[0]);
2494
2495 char *colorString = VuoColor_getShortSummary(so->light.color);
2496
2497 char *positionRangeString;
2498 if (so->type == VuoSceneObjectSubType_PointLight
2499 || so->type == VuoSceneObjectSubType_Spotlight)
2500 {
2501 char *positionString = VuoPoint3d_getSummary(so->transform.translation);
2502
2503 positionRangeString = VuoText_format("\n<div>Position (%s)</div>\n<div>Range %g units (%g sharpness)</div>",
2504 positionString, so->light.range, so->light.sharpness);
2505
2506 free(positionString);
2507 }
2508 else
2509 positionRangeString = strdup("");
2510
2511 char *directionConeString;
2512 if (so->type == VuoSceneObjectSubType_Spotlight)
2513 {
2514 VuoPoint3d direction = VuoTransform_getDirection(so->transform);
2515 char *directionString = VuoPoint3d_getSummary(direction);
2516
2517 directionConeString = VuoText_format("\n<div>Direction (%s)</div>\n<div>Cone %g°</div>",
2518 directionString, so->light.cone * 180./M_PI);
2519
2520 free(directionString);
2521 }
2522 else
2523 directionConeString = strdup("");
2524
2525 char *valueAsString = VuoText_format("<div>%s</div>\n<div>Color %s</div>\n<div>Brightness %g</div>%s%s",
2526 type, colorString, so->light.brightness, positionRangeString, directionConeString);
2527
2528 free(directionConeString);
2529 free(positionRangeString);
2530 free(colorString);
2531 free(type);
2532
2533 return valueAsString;
2534 }
2535
2536 unsigned long vertexCount = VuoSceneObject_getVertexCount(sceneObject);
2537 unsigned long elementCount = VuoSceneObject_getElementCount(sceneObject);
2538
2539 char *transform = VuoTransform_getSummary(so->transform);
2540
2541 unsigned long childObjectCount = 0;
2542 if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2543 childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2544 const char *childObjectPlural = childObjectCount == 1 ? "" : "s";
2545
2546 char *descendants;
2547 if (childObjectCount)
2548 {
2549 unsigned long descendantCount = 0;
2550 unsigned long totalVertexCount = 0;
2551 unsigned long totalElementCount = 0;
2552 VuoSceneObject_getStatistics(sceneObject, &descendantCount, &totalVertexCount, &totalElementCount);
2553 const char *descendantPlural = descendantCount == 1 ? "" : "s";
2554
2555 descendants = VuoText_format("\n<div>%ld descendant%s</div>\n<div>Total, including descendants:</div>\n<div>%ld vertices, %ld elements</div>",
2556 descendantCount, descendantPlural, totalVertexCount, totalElementCount);
2557 }
2558 else
2559 descendants = strdup("");
2560
2561 VuoList_VuoText shaderNames = VuoSceneObject_findShaderNames(sceneObject);
2562 VuoRetain(shaderNames);
2563 char *shaderNamesSummary;
2564 if (VuoListGetCount_VuoText(shaderNames))
2565 {
2566 VuoInteger shaderNameCount = VuoListGetCount_VuoText(shaderNames);
2567 const char *header = "\n<div>Shaders:<ul>";
2569 VuoInteger shaderNameLength = strlen(header);
2570 for (VuoInteger i = 1; i <= shaderNameCount; ++i)
2571 shaderNameLength += strlen("\n<li>") + strlen(VuoListGetValue_VuoText(shaderNames, i)) + strlen("</li>");
2572 shaderNameLength += strlen("</ul></div>");
2573
2574 shaderNamesSummary = (char *)malloc(shaderNameLength + 1);
2575 char *t = shaderNamesSummary;
2576 t = strcpy(t, header) + strlen(header);
2577 for (VuoInteger i = 1; i <= shaderNameCount; ++i)
2578 {
2579 t = strcpy(t, "\n<li>") + strlen("\n<li>");
2580 t = strcpy(t, VuoListGetValue_VuoText(shaderNames, i)) + strlen(VuoListGetValue_VuoText(shaderNames, i));
2581 t = strcpy(t, "</li>") + strlen("</li>");
2582 }
2583 t = strcpy(t, "</ul></div>");
2584 }
2585 else
2586 shaderNamesSummary = strdup("");
2587 VuoRelease(shaderNames);
2588
2589 char *name = NULL;
2590 if (so->name)
2591 name = VuoText_format("<div>Object named \"%s\"</div>\n", so->name);
2592
2593 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",
2594 name ? name : "",
2595 vertexCount, elementCount,
2596 transform,
2597 so->id,
2598 childObjectCount, childObjectPlural,
2599 descendants, shaderNamesSummary);
2600
2601 free(name);
2602 free(descendants);
2603 free(transform);
2604 free(shaderNamesSummary);
2605
2606 return valueAsString;
2607}
2608
2612static void VuoSceneObject_dump_internal(const VuoSceneObject sceneObject, unsigned int level)
2613{
2614 VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2615
2616 for (unsigned int i=0; i<level; ++i)
2617 fprintf(stderr, "\t");
2618
2619 if (!sceneObject)
2620 {
2621 fprintf(stderr, "no object\n");
2622 return;
2623 }
2624
2625 fprintf(stderr, "%s \"%s\" (%s) ", VuoSceneObject_cStringForType(so->type), so->name ? so->name : "(no name)", VuoTransform_getSummary(so->transform));
2626 if (so->type == VuoSceneObjectSubType_Mesh)
2627 fprintf(stderr, "%lu vertices, %lu elements, shader '%s' (%p)", VuoSceneObject_getVertexCount(sceneObject), VuoSceneObject_getElementCount(sceneObject), so->shader ? so->shader->name : "", so->shader);
2628 fprintf(stderr, "\n");
2629
2630 if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2631 {
2632 unsigned int childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2633 for (unsigned int i=1; i<=childObjectCount; ++i)
2634 VuoSceneObject_dump_internal(VuoListGetValue_VuoSceneObject(so->childObjects, i), level+1);
2635 }
2636}
2637
2642{
2644}
2645
2657{
2658 if (!so)
2659 return nullptr;
2660
2661 // Count the vertices.
2662
2663 __block unsigned long triangleVertexCount = 0;
2664 __block unsigned long trianglePrimitiveCount = 0;
2665 __block VuoMesh_FaceCulling triangleFaceCulling = VuoMesh_CullBackfaces;
2666
2667 __block unsigned long lineVertexCount = 0;
2668 __block unsigned long linePrimitiveCount = 0;
2669 __block double linePrimitiveSize = 0;
2670
2671 __block unsigned long pointVertexCount = 0;
2672 __block unsigned long pointPrimitiveCount = 0;
2673 __block double pointPrimitiveSize = 0;
2674
2675 VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2676 VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2677 if (!co->mesh)
2678 return true;
2679
2680 VuoMesh_ElementAssemblyMethod elementAssemblyMethod = VuoMesh_getElementAssemblyMethod(co->mesh);
2681
2682 unsigned int vertexCount;
2683 VuoMesh_getCPUBuffers(co->mesh, &vertexCount, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
2684
2685 if (elementAssemblyMethod == VuoMesh_IndividualTriangles
2686 || elementAssemblyMethod == VuoMesh_TriangleStrip
2687 || elementAssemblyMethod == VuoMesh_TriangleFan)
2688 {
2689 triangleVertexCount += vertexCount;
2690 trianglePrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2691 triangleFaceCulling = VuoMesh_getFaceCulling(co->mesh);
2692 }
2693 else if (elementAssemblyMethod == VuoMesh_IndividualLines
2694 || elementAssemblyMethod == VuoMesh_LineStrip)
2695 {
2696 lineVertexCount += vertexCount;
2697 linePrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2698 linePrimitiveSize = VuoMesh_getPrimitiveSize(co->mesh);
2699 }
2700 else if (elementAssemblyMethod == VuoMesh_Points)
2701 {
2702 pointVertexCount += vertexCount;
2703 pointPrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2704 pointPrimitiveSize = VuoMesh_getPrimitiveSize(co->mesh);
2705 }
2706
2707 return true;
2708 });
2709// VLog("triangles: %ldv %ldp lines: %ldv %ldp points: %ldv %ldp", triangleVertexCount, trianglePrimitiveCount, lineVertexCount, linePrimitiveCount, pointVertexCount, pointPrimitiveCount);
2710
2711 if (!trianglePrimitiveCount && !linePrimitiveCount && !pointPrimitiveCount)
2712 return nullptr;
2713
2714 // Allocate the buffers.
2715 unsigned int *triangleElements = nullptr;
2716 float *trianglePositions = nullptr, *triangleNormals = nullptr, *triangleTextureCoordinates = nullptr;
2717 unsigned int *lineElements = nullptr;
2718 float *linePositions = nullptr, *lineNormals = nullptr, *lineTextureCoordinates = nullptr;
2719 unsigned int *pointElements = nullptr;
2720 float *pointPositions = nullptr, *pointNormals = nullptr, *pointTextureCoordinates = nullptr;
2721 if (trianglePrimitiveCount)
2722 VuoMesh_allocateCPUBuffers(triangleVertexCount, &trianglePositions, &triangleNormals, &triangleTextureCoordinates, nullptr, trianglePrimitiveCount * 3, &triangleElements);
2723 if (linePrimitiveCount)
2724 VuoMesh_allocateCPUBuffers(lineVertexCount, &linePositions, &lineNormals, &lineTextureCoordinates, nullptr, linePrimitiveCount * 2, &lineElements);
2725 if (pointPrimitiveCount)
2726 VuoMesh_allocateCPUBuffers(pointVertexCount, &pointPositions, &pointNormals, &pointTextureCoordinates, nullptr, pointPrimitiveCount, &pointElements);
2727
2728 // Copy the vertex attributes.
2729 __block unsigned long triangleVertexIndex = 0;
2730 __block unsigned long triangleElementIndex = 0;
2731 __block unsigned long lineVertexIndex = 0;
2732 __block unsigned long lineElementIndex = 0;
2733 __block unsigned long pointVertexIndex = 0;
2734 __block unsigned long pointElementIndex = 0;
2735 __block bool anyTextureCoordinates = false;
2736 VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2737 VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2738 if (!co->mesh)
2739 return true;
2740
2741 VuoMesh_ElementAssemblyMethod elementAssemblyMethod = VuoMesh_getElementAssemblyMethod(co->mesh);
2742
2743 unsigned int vertexCount, elementCount, *elements;
2744 float *positions, *normals, *textureCoordinates;
2745 VuoMesh_getCPUBuffers(co->mesh, &vertexCount, &positions, &normals, &textureCoordinates, nullptr, &elementCount, &elements);
2746
2747 if (textureCoordinates)
2748 anyTextureCoordinates = true;
2749
2750 if (elementAssemblyMethod == VuoMesh_IndividualTriangles
2751 || elementAssemblyMethod == VuoMesh_TriangleStrip
2752 || elementAssemblyMethod == VuoMesh_TriangleFan)
2753 {
2754 unsigned long indexOffset = triangleVertexIndex;
2755 for (unsigned int n = 0; n < vertexCount; ++n)
2756 {
2757 VuoPoint3d p = VuoPoint3d_makeFromArray(&positions[n * 3]);
2758 VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2759 VuoPoint3d_setArray(&trianglePositions[triangleVertexIndex * 3], pt);
2760
2761 if (normals)
2762 {
2763 VuoPoint3d r = VuoPoint3d_makeFromArray(&normals[n * 3]);
2764 VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2765 VuoPoint3d_setArray(&triangleNormals[triangleVertexIndex * 3], rt);
2766 }
2767
2768 if (textureCoordinates)
2769 VuoPoint2d_setArray(&triangleTextureCoordinates[triangleVertexIndex * 2], VuoPoint2d_makeFromArray(&textureCoordinates[n * 2]));
2770
2771 ++triangleVertexIndex;
2772 }
2773
2774 if (elementAssemblyMethod == VuoMesh_IndividualTriangles)
2775 {
2776 if (elementCount)
2777 for (unsigned int n = 0; n < elementCount; ++n)
2778 triangleElements[triangleElementIndex++] = indexOffset + elements[n];
2779 else
2780 for (unsigned int n = 0; n < vertexCount; ++n)
2781 triangleElements[triangleElementIndex++] = indexOffset + n;
2782 }
2783 else if (elementAssemblyMethod == VuoMesh_TriangleStrip)
2784 {
2785 // Expand the triangle strip to individual triangles.
2786 if (elementCount)
2787 for (unsigned int n = 2; n < elementCount; ++n)
2788 if (n%2 == 0)
2789 {
2790 triangleElements[triangleElementIndex++] = indexOffset + elements[n-2];
2791 triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2792 triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2793 }
2794 else
2795 {
2796 triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2797 triangleElements[triangleElementIndex++] = indexOffset + elements[n-2];
2798 triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2799 }
2800 else
2801 for (unsigned int n = 0; n < vertexCount; ++n)
2802 if (n%2 == 0)
2803 {
2804 triangleElements[triangleElementIndex++] = indexOffset + n-2;
2805 triangleElements[triangleElementIndex++] = indexOffset + n-1;
2806 triangleElements[triangleElementIndex++] = indexOffset + n ;
2807 }
2808 else
2809 {
2810 triangleElements[triangleElementIndex++] = indexOffset + n-1;
2811 triangleElements[triangleElementIndex++] = indexOffset + n-2;
2812 triangleElements[triangleElementIndex++] = indexOffset + n ;
2813 }
2814 }
2815 else if (elementAssemblyMethod == VuoMesh_TriangleFan)
2816 {
2817 // Expand the triangle fan to individual triangles.
2818 if (elementCount)
2819 for (unsigned int n = 2; n < elementCount; ++n)
2820 {
2821 triangleElements[triangleElementIndex++] = indexOffset + elements[0 ];
2822 triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2823 triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2824 }
2825 else
2826 for (unsigned int n = 2; n < vertexCount; ++n)
2827 {
2828 triangleElements[triangleElementIndex++] = indexOffset + 0;
2829 triangleElements[triangleElementIndex++] = indexOffset + n-1;
2830 triangleElements[triangleElementIndex++] = indexOffset + n;
2831 }
2832 }
2833 }
2834 else if (elementAssemblyMethod == VuoMesh_IndividualLines
2835 || elementAssemblyMethod == VuoMesh_LineStrip)
2836 {
2837 unsigned long indexOffset = lineVertexIndex;
2838 for (unsigned int n = 0; n < vertexCount; ++n)
2839 {
2840 VuoPoint3d p = VuoPoint3d_makeFromArray(&positions[n * 3]);
2841 VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2842 VuoPoint3d_setArray(&linePositions[lineVertexIndex * 3], pt);
2843
2844 if (normals)
2845 {
2846 VuoPoint3d r = VuoPoint3d_makeFromArray(&normals[n * 3]);
2847 VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2848 VuoPoint3d_setArray(&lineNormals[lineVertexIndex * 3], rt);
2849 }
2850
2851 if (textureCoordinates)
2852 VuoPoint2d_setArray(&lineTextureCoordinates[lineVertexIndex], VuoPoint2d_makeFromArray(&textureCoordinates[n * 2]));
2853
2854 ++lineVertexIndex;
2855 }
2856
2857 if (elementAssemblyMethod == VuoMesh_IndividualLines)
2858 {
2859 if (elementCount)
2860 for (unsigned int n = 0; n < elementCount; ++n)
2861 lineElements[lineElementIndex++] = indexOffset + elements[n];
2862 else
2863 for (unsigned int n = 0; n < vertexCount; ++n)
2864 lineElements[lineElementIndex++] = indexOffset + n;
2865 }
2866 else if (elementAssemblyMethod == VuoMesh_LineStrip)
2867 {
2868 // Expand the line strip to individual lines.
2869 if (elementCount)
2870 for (unsigned int n = 1; n < elementCount; ++n)
2871 {
2872 lineElements[lineElementIndex++] = indexOffset + elements[n-1];
2873 lineElements[lineElementIndex++] = indexOffset + elements[n ];
2874 }
2875 else
2876 for (unsigned int n = 1; n < vertexCount; ++n)
2877 {
2878 lineElements[lineElementIndex++] = indexOffset + n-1;
2879 lineElements[lineElementIndex++] = indexOffset + n;
2880 }
2881 }
2882 }
2883 else if (elementAssemblyMethod == VuoMesh_Points)
2884 {
2885 unsigned long indexOffset = pointVertexIndex;
2886 for (unsigned int n = 0; n < vertexCount; ++n)
2887 {
2888 VuoPoint3d p = VuoPoint3d_makeFromArray(&positions[n * 3]);
2889 VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2890 VuoPoint3d_setArray(&pointPositions[pointVertexIndex * 3], pt);
2891
2892 if (normals)
2893 {
2894 VuoPoint3d r = VuoPoint3d_makeFromArray(&normals[n * 3]);
2895 VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2896 VuoPoint3d_setArray(&pointNormals[pointVertexIndex * 3], rt);
2897 }
2898
2899 if (textureCoordinates)
2900 VuoPoint2d_setArray(&pointTextureCoordinates[pointVertexIndex * 2], VuoPoint2d_makeFromArray(&textureCoordinates[n * 2]));
2901
2902 ++pointVertexIndex;
2903 }
2904
2905 if (elementCount)
2907 for (unsigned int n = 0; n < elementCount; ++n)
2908 pointElements[pointElementIndex++] = indexOffset + elements[n];
2909 else
2910 {
2911 for (unsigned int n = 0; n < vertexCount; ++n)
2912 pointElements[pointElementIndex++] = indexOffset + n;
2913 }
2914 }
2915
2916 return true;
2917 });
2918
2919
2920 VuoMesh triangleMesh = nullptr;
2921 VuoMesh lineMesh = nullptr;
2922 VuoMesh pointMesh = nullptr;
2923 if (trianglePrimitiveCount)
2924 {
2925 if (!anyTextureCoordinates)
2926 {
2927 free(triangleTextureCoordinates);
2928 triangleTextureCoordinates = nullptr;
2929 }
2930 triangleMesh = VuoMesh_makeFromCPUBuffers(triangleVertexCount, trianglePositions, triangleNormals, triangleTextureCoordinates, nullptr, trianglePrimitiveCount * 3, triangleElements, VuoMesh_IndividualTriangles);
2931 VuoMesh_setFaceCulling(triangleMesh, triangleFaceCulling);
2932 }
2933 if (linePrimitiveCount)
2934 {
2935 if (!anyTextureCoordinates)
2936 {
2937 free(lineTextureCoordinates);
2938 lineTextureCoordinates = nullptr;
2939 }
2940 lineMesh = VuoMesh_makeFromCPUBuffers(lineVertexCount, linePositions, lineNormals, lineTextureCoordinates, nullptr, linePrimitiveCount * 2, lineElements, VuoMesh_IndividualLines);
2941 VuoMesh_setPrimitiveSize(lineMesh, linePrimitiveSize);
2942 }
2943 if (pointPrimitiveCount)
2944 {
2945 if (!anyTextureCoordinates)
2946 {
2947 free(pointTextureCoordinates);
2948 pointTextureCoordinates = nullptr;
2949 }
2950 pointMesh = VuoMesh_makeFromCPUBuffers(pointVertexCount, pointPositions, pointNormals, pointTextureCoordinates, nullptr, pointPrimitiveCount, pointElements, VuoMesh_Points);
2951 VuoMesh_setPrimitiveSize(pointMesh, pointPrimitiveSize);
2952 }
2953
2954 if (triangleMesh && !lineMesh && !pointMesh)
2955 return VuoSceneObject_makeMesh(triangleMesh, NULL, VuoTransform_makeIdentity());
2956 else if (!triangleMesh && lineMesh && !pointMesh)
2957 return VuoSceneObject_makeMesh(lineMesh, NULL, VuoTransform_makeIdentity());
2958 else if (!triangleMesh && !lineMesh && pointMesh)
2959 return VuoSceneObject_makeMesh(pointMesh, NULL, VuoTransform_makeIdentity());
2960 else
2961 {
2963 if (triangleMesh)
2965 if (lineMesh)
2967 if (pointMesh)
2970 }
2971
2972 return NULL;
2973}
2974
2975#define CSGJS_HEADER_ONLY
2976#include "csgjs.cc"
2977
2982{
2983 if (!so)
2984 return csgjs_model();
2985
2987 VuoSceneObject_internal *f = (VuoSceneObject_internal *)flat;
2988 if (!f->mesh)
2989 return csgjs_model();
2990
2991 VuoLocal(flat);
2992
2994 return csgjs_model();
2995
2996 unsigned int vertexCount, elementCount, *elements;
2997 float *positions, *normals, *textureCoordinates;
2998 VuoMesh_getCPUBuffers(f->mesh, &vertexCount, &positions, &normals, &textureCoordinates, nullptr, &elementCount, &elements);
2999
3000 csgjs_model cm;
3001 for (unsigned int n = 0; n < vertexCount; ++n)
3002 {
3003 csgjs_vertex v;
3004 v.pos = csgjs_vector(positions[n * 3], positions[n * 3 + 1], positions[n * 3 + 2]);
3005 if (normals)
3006 v.normal = csgjs_vector(normals[n * 3], normals[n * 3 + 1], normals[n * 3 + 2]);
3007 if (textureCoordinates)
3008 v.uv = csgjs_vector(textureCoordinates[n * 2], textureCoordinates[n * 2 + 1], 0);
3009 cm.vertices.push_back(v);
3010 }
3011 for (unsigned int n = 0; n < elementCount; ++n)
3012 cm.indices.push_back(elements[n]);
3013
3014 return cm;
3015}
3016
3021{
3022 unsigned int vertexCount = cm.vertices.size();
3023 unsigned int elementCount = cm.indices.size();
3024 unsigned int *elements;
3025 float *positions, *normals, *textureCoordinates;
3026 VuoMesh_allocateCPUBuffers(vertexCount, &positions, &normals, &textureCoordinates, nullptr, elementCount, &elements);
3027
3028 const csgjs_vertex *vertex = &cm.vertices[0];
3029 for (unsigned int n = 0; n < vertexCount; ++n)
3030 {
3031 positions[n * 3 ] = vertex[n].pos.x;
3032 positions[n * 3 + 1] = vertex[n].pos.y;
3033 positions[n * 3 + 2] = vertex[n].pos.z;
3034 normals[n * 3 ] = vertex[n].normal.x;
3035 normals[n * 3 + 1] = vertex[n].normal.y;
3036 normals[n * 3 + 2] = vertex[n].normal.z;
3037 textureCoordinates[n * 2 ] = vertex[n].uv.x;
3038 textureCoordinates[n * 2 + 1] = vertex[n].uv.y;
3039 }
3040
3041 const int *index = &cm.indices[0];
3042 for (unsigned int n = 0; n < elementCount; ++n)
3043 elements[n] = index[n];
3044
3045 VuoMesh mesh = VuoMesh_makeFromCPUBuffers(vertexCount, positions, normals, textureCoordinates, nullptr, elementCount, elements, VuoMesh_IndividualTriangles);
3046
3048}
3049
3054static float convertQualityToEpsilon(float quality)
3055{
3056 return pow(10, -VuoReal_clamp(quality, 0, 1) * 5.);
3057}
3058
3063{
3064 float epsilon = convertQualityToEpsilon(quality);
3065
3066 unsigned long objectCount = VuoListGetCount_VuoSceneObject(objects);
3067 if (objectCount == 0)
3068 return nullptr;
3069 if (objectCount == 1)
3070 return VuoListGetValue_VuoSceneObject(objects, 1);
3071
3072 dispatch_queue_t queue = dispatch_queue_create("org.vuo.sceneobject.union", DISPATCH_QUEUE_CONCURRENT);
3073
3074 csgjs_model *models = new csgjs_model[objectCount];
3075 for (unsigned long i = 0; i < objectCount; ++i)
3076 dispatch_async(queue, ^{
3078 });
3079
3080 dispatch_barrier_sync(queue, ^{});
3081 dispatch_release(queue);
3082
3083 csgjs_model cu = models[0];
3084 for (unsigned long i = 1; i < objectCount; ++i)
3085 cu = csgjs_union(cu, models[i], epsilon);
3086 delete[] models;
3088}
3089
3094{
3095 float epsilon = convertQualityToEpsilon(quality);
3096
3097 dispatch_semaphore_t finished = dispatch_semaphore_create(0);
3098
3099 __block csgjs_model ca;
3100 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3102 dispatch_semaphore_signal(finished);
3103 });
3104
3105 csgjs_model cb = VuoSceneObject_getCsgjsModel(b);
3106
3107 dispatch_semaphore_wait(finished, DISPATCH_TIME_FOREVER);
3108 dispatch_release(finished);
3109
3110 csgjs_model d = csgjs_difference(ca, cb, epsilon);
3112}
3113
3118{
3119 float epsilon = convertQualityToEpsilon(quality);
3120
3121 unsigned long objectCount = VuoListGetCount_VuoSceneObject(objects);
3122 if (objectCount == 0)
3123 return nullptr;
3124 if (objectCount == 1)
3125 return VuoListGetValue_VuoSceneObject(objects, 1);
3126
3127 dispatch_queue_t queue = dispatch_queue_create("org.vuo.sceneobject.intersect", DISPATCH_QUEUE_CONCURRENT);
3128
3129 csgjs_model *models = new csgjs_model[objectCount];
3130 for (unsigned long i = 0; i < objectCount; ++i)
3131 dispatch_async(queue, ^{
3133 });
3134
3135 dispatch_barrier_sync(queue, ^{});
3136 dispatch_release(queue);
3137
3138 csgjs_model ci = models[0];
3139 for (unsigned long i = 1; i < objectCount; ++i)
3140 ci = csgjs_intersection(ci, models[i], epsilon);
3141 delete[] models;
3143}