Vuo  2.1.0
VuoSceneObject.cc
Go to the documentation of this file.
1 
10 #include <list>
11 
12 #include <OpenGL/CGLMacro.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include "type.h"
17 #include "VuoMeshUtility.h"
18 
20 #ifdef VUO_COMPILER
21 extern "C"
22 {
24  "title" : "Scene Object",
25  "description" : "A 3D Object: visible (mesh), or virtual (group, light, camera).",
26  "keywords" : [ ],
27  "version" : "1.0.0",
28  "dependencies" : [
29  "csgjs",
30  "VuoBlendMode",
31  "VuoBoolean",
32  "VuoCubemap",
33  "VuoFont",
34  "VuoMesh",
35  "VuoMeshUtility",
36  "VuoPoint3d",
37  "VuoShader",
38  "VuoText",
39  "VuoTransform",
40  "VuoList_VuoImage",
41  "VuoList_VuoSceneObject"
42  ]
43  });
44 }
45 #endif
46 
55 typedef struct
56 {
58 
59  // Data for all scene objects
60  uint64_t id;
61  VuoText name;
62  VuoTransform transform;
63 
64  // Mesh
65  VuoMesh mesh;
66  VuoShader shader;
67  bool isRealSize;
68  bool preservePhysicalSize;
69  VuoBlendMode blendMode;
70 
71  union
72  {
73  VuoList_VuoSceneObject childObjects;
74 
75  struct
76  {
77  float fieldOfView;
78  float width;
79  float distanceMin;
80  float distanceMax;
81  float confocalDistance;
82  float intraocularDistance;
83  float vignetteWidth;
84  float vignetteSharpness;
85  } camera;
86 
87  struct
88  {
89  VuoColor color;
90  float brightness;
91  float range;
92  float cone;
93  float sharpness;
94  } light;
95 
96  struct
97  {
98  VuoText text;
99  VuoFont font;
100  bool scaleWithScene;
101  float wrapWidth;
102  } text;
103  };
104 } VuoSceneObject_internal;
105 
111 {
112  static uint64_t id = 0;
113  return __sync_add_and_fetch(&id, 1);
114 }
115 
121 void VuoSceneObject_free(void *sceneObject)
122 {
123  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
124 
125  VuoRelease(so->name);
126  VuoRelease(so->mesh);
127  VuoRelease(so->shader);
128  if (so->type == VuoSceneObjectSubType_Group)
129  VuoRelease(so->childObjects);
130  else if (so->type == VuoSceneObjectSubType_Text)
131  {
132  VuoRelease(so->text.text);
133  VuoFont_release(so->text.font);
134  }
135 
136  free(so);
137 }
138 
143 {
144  VuoSceneObject_internal *so = (VuoSceneObject_internal *)calloc(1, sizeof(VuoSceneObject_internal));
146 
147  so->id = 0;
148  so->type = VuoSceneObjectSubType_Empty;
149 
150  so->mesh = NULL;
151  so->shader = NULL;
152  so->isRealSize = false;
153  so->preservePhysicalSize = false;
154  so->blendMode = VuoBlendMode_Normal;
155 
156  so->name = NULL;
157  so->transform = VuoTransform_makeIdentity();
158 
159  return (VuoSceneObject)so;
160 }
161 
166 {
167  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
168 
169  so->type = VuoSceneObjectSubType_Group;
170 
172 
173  so->transform = transform;
174 
175  return (VuoSceneObject)so;
176 }
177 
182 {
183  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
184 
185  so->type = VuoSceneObjectSubType_Mesh;
186 
188 
189  if (mesh && !shader)
191  else
193 
194  so->transform = transform;
195 
196  return (VuoSceneObject)so;
197 }
198 
213 VuoSceneObject VuoSceneObject_makeQuad(VuoShader shader, VuoPoint3d center, VuoPoint3d rotation, VuoReal width, VuoReal height)
214 {
217  shader,
219  center,
220  VuoPoint3d_multiply(rotation, M_PI/180.),
221  VuoPoint3d_make(width,height,1)
222  ));
223 }
224 
239 VuoSceneObject VuoSceneObject_makeQuadWithNormals(VuoShader shader, VuoPoint3d center, VuoPoint3d rotation, VuoReal width, VuoReal height)
240 {
243  shader,
245  center,
246  VuoPoint3d_multiply(rotation, M_PI/180.),
247  VuoPoint3d_make(width,height,1)
248  ));
249 }
250 
256 VuoSceneObject VuoSceneObject_makeImage(VuoImage image, VuoPoint3d center, VuoPoint3d rotation, VuoReal size, VuoOrientation fixed, VuoReal alpha)
257 {
258  if (!image)
259  return nullptr;
260 
261  float width, height;
262  if (fixed == VuoOrientation_Horizontal)
263  {
264  width = size;
265  height = image->pixelsHigh * size / image->pixelsWide;
266  }
267  else
268  {
269  width = image->pixelsWide * size / image->pixelsHigh;
270  height = size;
271  }
272 
274  VuoShader_makeUnlitImageShader(image, alpha),
275  center,
276  rotation,
277  width,
278  height
279  );
280 
281  return object;
282 }
283 
289 VuoSceneObject VuoSceneObject_makeLitImage(VuoImage image, VuoPoint3d center, VuoPoint3d rotation, VuoReal size, VuoOrientation fixed, VuoReal alpha, VuoColor highlightColor, VuoReal shininess)
290 {
291  if (!image)
292  return nullptr;
293 
294  float width, height;
295  if (fixed == VuoOrientation_Horizontal)
296  {
297  width = size;
298  height = image->pixelsHigh * size / image->pixelsWide;
299  }
300  else
301  {
302  width = image->pixelsWide * size / image->pixelsHigh;
303  height = size;
304  }
305 
307  VuoShader_makeLitImageShader(image, alpha, highlightColor, shininess),
308  center,
309  rotation,
310  width,
311  height
312  );
313 }
314 
318 VuoSceneObject VuoSceneObject_makeCube(VuoTransform transform, VuoShader frontShader, VuoShader leftShader, VuoShader rightShader, VuoShader backShader, VuoShader topShader, VuoShader bottomShader)
319 {
321 
322  VuoMesh quadMesh = VuoMesh_makeQuad();
323 
324  // Front Face
325  {
327  quadMesh,
328  frontShader,
330  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
331  }
332 
333  // Left Face
334  {
336  quadMesh,
337  leftShader,
338  VuoTransform_makeEuler(VuoPoint3d_make(-.5,0,0), VuoPoint3d_make(0,-M_PI/2.,0), VuoPoint3d_make(1,1,1)));
339  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
340  }
341 
342  // Right Face
343  {
345  quadMesh,
346  rightShader,
348  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
349  }
350 
351  // Back Face
352  {
354  quadMesh,
355  backShader,
357  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
358  }
359 
360  // Top Face
361  {
363  quadMesh,
364  topShader,
366  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
367  }
368 
369  // Bottom Face
370  {
372  quadMesh,
373  bottomShader,
375  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
376  }
377 
378  return VuoSceneObject_makeGroup(cubeChildObjects, transform);
379 }
380 
381 
388 {
389  return VuoSceneObject_makeMesh(VuoMesh_makeCube(), shader, transform);
390 }
391 
398 {
399  return VuoSceneObject_makeMesh(VuoMesh_makeCube(), shader, transform);
400 }
401 
408 {
410 }
411 
418 {
420 }
421 
428 {
429  return VuoSceneObject_makeCubeMulti(transform, 2, 2, 2,
436 }
437 
444  VuoShader front, VuoShader left, VuoShader right, VuoShader back, VuoShader top, VuoShader bottom)
445 {
447 
448  unsigned int _rows = MAX(2, MIN(512, rows));
449  unsigned int _columns = MAX(2, MIN(512, columns));
450  unsigned int _slices = MAX(2, MIN(512, slices));
451 
452  VuoMesh frontBackMesh = VuoMesh_makePlane(_columns, _rows);
453  VuoMesh leftRightMesh = VuoMesh_makePlane(_slices, _rows);
454  VuoMesh topBottomMesh = VuoMesh_makePlane(_columns, _slices);
455 
456  // Front Face
457  {
459  frontBackMesh,
460  front,
462  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
463  }
464 
465  // Left Face
466  {
468  leftRightMesh,
469  left,
470  VuoTransform_makeEuler(VuoPoint3d_make(-.5,0,0), VuoPoint3d_make(0,-M_PI/2.,0), VuoPoint3d_make(1,1,1)));
471  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
472  }
473 
474  // Right Face
475  {
477  leftRightMesh,
478  right,
480  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
481  }
482 
483  // Back Face
484  {
486  frontBackMesh,
487  back,
489  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
490  }
491 
492  // Top Face
493  {
495  topBottomMesh,
496  top,
498  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
499  }
500 
501  // Bottom Face
502  {
504  topBottomMesh,
505  bottom,
507  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
508  }
509 
510  return VuoSceneObject_makeGroup(cubeChildObjects, transform);
511 }
512 
521 VuoSceneObject VuoSceneObject_makeText(VuoText text, VuoFont font, VuoBoolean scaleWithScene, float wrapWidth)
522 {
523  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
524  so->type = VuoSceneObjectSubType_Text;
527  so->text.scaleWithScene = scaleWithScene;
528  so->text.wrapWidth = wrapWidth;
529  return (VuoSceneObject)so;
530 }
531 
535 VuoSceneObject VuoSceneObject_makePerspectiveCamera(VuoText name, VuoTransform transform, float fieldOfView, float distanceMin, float distanceMax)
536 {
537  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
538  so->type = VuoSceneObjectSubType_PerspectiveCamera;
540  so->transform = transform;
541  so->camera.fieldOfView = fieldOfView;
542  so->camera.distanceMin = distanceMin;
543  so->camera.distanceMax = distanceMax;
544  return (VuoSceneObject)so;
545 }
546 
550 VuoSceneObject VuoSceneObject_makeStereoCamera(VuoText name, VuoTransform transform, VuoReal fieldOfView, VuoReal distanceMin, VuoReal distanceMax, VuoReal confocalDistance, VuoReal intraocularDistance)
551 {
552  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
553  so->type = VuoSceneObjectSubType_StereoCamera;
555  so->transform = transform;
556  so->camera.fieldOfView = fieldOfView;
557  so->camera.distanceMin = distanceMin;
558  so->camera.distanceMax = distanceMax;
559  so->camera.confocalDistance = confocalDistance;
560  so->camera.intraocularDistance = intraocularDistance;
561  return (VuoSceneObject)so;
562 }
563 
567 VuoSceneObject VuoSceneObject_makeOrthographicCamera(VuoText name, VuoTransform transform, float width, float distanceMin, float distanceMax)
568 {
569  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
570  so->type = VuoSceneObjectSubType_OrthographicCamera;
572  so->transform = transform;
573  so->camera.width = width;
574  so->camera.distanceMin = distanceMin;
575  so->camera.distanceMax = distanceMax;
576  return (VuoSceneObject)so;
577 }
578 
582 VuoSceneObject VuoSceneObject_makeFisheyeCamera(VuoText name, VuoTransform transform, VuoReal fieldOfView, VuoReal vignetteWidth, VuoReal vignetteSharpness)
583 {
584  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
585  so->type = VuoSceneObjectSubType_FisheyeCamera;
587  so->transform = transform;
588  so->camera.fieldOfView = fieldOfView;
589 
590  // 0 and 1000 come from "Realtime Dome Imaging and Interaction" by Bailey/Clothier/Gebbie 2006.
591  so->camera.distanceMin = 0;
592  so->camera.distanceMax = 1000;
593 
594  so->camera.vignetteWidth = vignetteWidth;
595  so->camera.vignetteSharpness = vignetteSharpness;
596 
597  return (VuoSceneObject)so;
598 }
599 
604 {
606  VuoPoint3d_make(0,0,1),
607  VuoPoint3d_make(0,0,0),
608  VuoPoint3d_make(1,1,1)
609  );
611  VuoText_make("default camera"),
612  transform,
613  90,
614  0.1,
615  10.0
616  );
617 }
618 
628 bool VuoSceneObject_find(VuoSceneObject sceneObject, VuoText nameToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
629 {
630  if (!sceneObject)
631  return false;
632 
633  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
634 
635  if (VuoText_areEqual(so->name, nameToMatch))
636  {
637  *foundObject = (VuoSceneObject)so;
638  return true;
639  }
640 
641  if (so->type == VuoSceneObjectSubType_Group)
642  {
643  VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
644 
645  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
646  for (unsigned long i = 1; i <= childObjectCount; ++i)
647  {
648  VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
649  if (VuoSceneObject_find(childObject, nameToMatch, ancestorObjects, foundObject))
650  return true;
651  }
652 
653  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
654  }
655 
656  return false;
657 }
658 
669 bool VuoSceneObject_findById(VuoSceneObject sceneObject, uint64_t idToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
670 {
671  if (!sceneObject)
672  return false;
673 
674  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
675 
676  if (so->id == idToMatch)
677  {
678  *foundObject = (VuoSceneObject)so;
679  return true;
680  }
681 
682  if (so->type == VuoSceneObjectSubType_Group)
683  {
684  VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
685 
686  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
687  for (unsigned long i = 1; i <= childObjectCount; ++i)
688  {
689  VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
690  if (VuoSceneObject_findById(childObject, idToMatch, ancestorObjects, foundObject))
691  return true;
692  }
693 
694  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
695  }
696 
697  return false;
698 }
699 
710 bool VuoSceneObject_findWithType(VuoSceneObject sceneObject, VuoSceneObjectSubType typeToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
711 {
712  if (!sceneObject)
713  return false;
714 
715  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
716 
717  if (so->type == typeToMatch)
718  {
719  *foundObject = (VuoSceneObject)so;
720  return true;
721  }
722 
723  if (so->type == VuoSceneObjectSubType_Group)
724  {
725  VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
726 
727  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
728 
729  for (unsigned long i = 1; i <= childObjectCount; ++i)
730  {
731  VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
732 
733  if (VuoSceneObject_findWithType(childObject, typeToMatch, ancestorObjects, foundObject))
734  return true;
735  }
736 
737  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
738  }
739 
740  return false;
741 }
742 
751 bool VuoSceneObject_findCamera(VuoSceneObject sceneObject, VuoText nameToMatch, VuoSceneObject *foundCamera)
752 {
753  if (!sceneObject)
754  return false;
755 
756  __block bool didFindCamera = false;
757  VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
758  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
759  if ((co->type == VuoSceneObjectSubType_PerspectiveCamera
760  || co->type == VuoSceneObjectSubType_StereoCamera
761  || co->type == VuoSceneObjectSubType_OrthographicCamera
762  || co->type == VuoSceneObjectSubType_FisheyeCamera)
763  && (!nameToMatch || (co->name && nameToMatch && strstr(co->name, nameToMatch))))
764  {
765  *foundCamera = currentObject;
766  VuoSceneObject_setTransform(*foundCamera, VuoTransform_makeFromMatrix4x4(modelviewMatrix));
767  didFindCamera = true;
768  return false;
769  }
770  return true;
771  });
772 
773  return didFindCamera;
774 }
775 
780 {
781  if (!sceneObject)
782  return false;
783 
784  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
785  return so->type != VuoSceneObjectSubType_Empty;
786 }
787 
792 {
793  if (strcmp(typeString,"empty")==0)
794  return VuoSceneObjectSubType_Empty;
795  else if (strcmp(typeString,"group")==0)
796  return VuoSceneObjectSubType_Group;
797  else if (strcmp(typeString,"mesh")==0)
798  return VuoSceneObjectSubType_Mesh;
799  else if (strcmp(typeString,"camera-perspective")==0)
800  return VuoSceneObjectSubType_PerspectiveCamera;
801  else if (strcmp(typeString,"camera-stereo")==0)
802  return VuoSceneObjectSubType_StereoCamera;
803  else if (strcmp(typeString,"camera-orthographic")==0)
804  return VuoSceneObjectSubType_OrthographicCamera;
805  else if (strcmp(typeString,"camera-fisheye")==0)
806  return VuoSceneObjectSubType_FisheyeCamera;
807  else if (strcmp(typeString,"light-ambient")==0)
808  return VuoSceneObjectSubType_AmbientLight;
809  else if (strcmp(typeString,"light-point")==0)
810  return VuoSceneObjectSubType_PointLight;
811  else if (strcmp(typeString,"light-spot")==0)
812  return VuoSceneObjectSubType_Spotlight;
813  else if (strcmp(typeString,"text")==0)
814  return VuoSceneObjectSubType_Text;
815 
816  return VuoSceneObjectSubType_Empty;
817 }
818 
823 {
824  switch (type)
825  {
826  case VuoSceneObjectSubType_Group:
827  return "group";
828  case VuoSceneObjectSubType_Mesh:
829  return "mesh";
830  case VuoSceneObjectSubType_PerspectiveCamera:
831  return "camera-perspective";
832  case VuoSceneObjectSubType_StereoCamera:
833  return "camera-stereo";
834  case VuoSceneObjectSubType_OrthographicCamera:
835  return "camera-orthographic";
836  case VuoSceneObjectSubType_FisheyeCamera:
837  return "camera-fisheye";
838  case VuoSceneObjectSubType_AmbientLight:
839  return "light-ambient";
840  case VuoSceneObjectSubType_PointLight:
841  return "light-point";
842  case VuoSceneObjectSubType_Spotlight:
843  return "light-spot";
844  case VuoSceneObjectSubType_Text:
845  return "text";
846  // VuoSceneObjectSubType_Empty
847  default:
848  return "empty";
849  }
850 }
851 
857 {
858  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
859  so->type = VuoSceneObjectSubType_AmbientLight;
860  VuoSceneObject_setName((VuoSceneObject)so, VuoText_make("Ambient Light"));
861  so->light.color = color;
862  so->light.brightness = brightness;
863  return (VuoSceneObject)so;
864 }
865 
876 VuoSceneObject VuoSceneObject_makePointLight(VuoColor color, float brightness, VuoPoint3d position, float range, float sharpness)
877 {
878  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
879  so->type = VuoSceneObjectSubType_PointLight;
880  VuoText t = VuoText_make("Point Light");
882  so->light.color = color;
883  so->light.brightness = brightness;
884  so->light.range = range;
885  so->light.sharpness = MAX(MIN(sharpness,1),0);
886  so->transform = VuoTransform_makeEuler(position, VuoPoint3d_make(0,0,0), VuoPoint3d_make(1,1,1));
887  return (VuoSceneObject)so;
888 }
889 
901 VuoSceneObject VuoSceneObject_makeSpotlight(VuoColor color, float brightness, VuoTransform transform, float cone, float range, float sharpness)
902 {
903  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
904  so->type = VuoSceneObjectSubType_Spotlight;
906  so->light.color = color;
907  so->light.brightness = brightness;
908  so->light.cone = cone;
909  so->light.range = range;
910  so->light.sharpness = MAX(MIN(sharpness,1),0);
911  so->transform = transform;
912  return (VuoSceneObject)so;
913 }
914 
923 void VuoSceneObject_findLights(VuoSceneObject sceneObject, VuoColor *ambientColor, float *ambientBrightness, VuoList_VuoSceneObject *pointLights, VuoList_VuoSceneObject *spotLights)
924 {
925  __block VuoList_VuoColor ambientColors = VuoListCreate_VuoColor();
926  VuoRetain(ambientColors);
927 
928  *ambientBrightness = 0;
929  *pointLights = VuoListCreate_VuoSceneObject();
930  *spotLights = VuoListCreate_VuoSceneObject();
931 
932  VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
933  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
934  if (co->type == VuoSceneObjectSubType_AmbientLight)
935  {
936  VuoListAppendValue_VuoColor(ambientColors, co->light.color);
937  *ambientBrightness += co->light.brightness;
938  }
939  else if (co->type == VuoSceneObjectSubType_PointLight)
940  {
943  VuoListAppendValue_VuoSceneObject(*pointLights, l);
944  }
945  else if (co->type == VuoSceneObjectSubType_Spotlight)
946  {
949  VuoListAppendValue_VuoSceneObject(*spotLights, l);
950  }
951  return true;
952  });
953 
954  if (!VuoListGetCount_VuoColor(ambientColors)
955  && !VuoListGetCount_VuoSceneObject(*pointLights)
956  && !VuoListGetCount_VuoSceneObject(*spotLights))
957  {
958  *ambientColor = VuoColor_makeWithRGBA(1,1,1,1);
959  *ambientBrightness = 0.05;
960 
961  // https://en.wikipedia.org/wiki/Three-point_lighting
962 
963  VuoSceneObject keyLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .70, VuoPoint3d_make(-1,1,1), 5, .5);
964  VuoListAppendValue_VuoSceneObject(*pointLights, keyLight);
965 
966  VuoSceneObject fillLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .20, VuoPoint3d_make(.5,0,1), 5, 0);
967  VuoListAppendValue_VuoSceneObject(*pointLights, fillLight);
968 
969  VuoSceneObject backLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .15, VuoPoint3d_make(1,.75,-.5), 5, 0);
970  VuoListAppendValue_VuoSceneObject(*pointLights, backLight);
971  }
972  else
973  *ambientColor = VuoColor_average(ambientColors);
974 
975  VuoRelease(ambientColors);
976 }
977 
981 typedef struct
982 {
983  long objectCount;
984  const VuoSceneObject *objects;
985  float modelviewMatrix[16];
987 
1000 void VuoSceneObject_visit(const VuoSceneObject object, bool (^function)(const VuoSceneObject currentObject, float modelviewMatrix[16]))
1001 {
1002  if (!object)
1003  return;
1004 
1005  VuoSceneObject_treeState rootState;
1006  rootState.objectCount = 1;
1007  rootState.objects = &object;
1008  VuoTransform_getMatrix(VuoTransform_makeIdentity(), rootState.modelviewMatrix);
1009 
1010  std::list<VuoSceneObject_treeState> objectsToVisit(1, rootState);
1011  while (!objectsToVisit.empty())
1012  {
1013  VuoSceneObject_treeState currentState = objectsToVisit.front();
1014  objectsToVisit.pop_front();
1015 
1016  for (long i = 0; i < currentState.objectCount; ++i)
1017  {
1018  VuoSceneObject_internal *currentObject = (VuoSceneObject_internal *)currentState.objects[i];
1019  if (!currentObject)
1020  continue;
1021 
1022  float localModelviewMatrix[16];
1023  VuoTransform_getMatrix(currentObject->transform, localModelviewMatrix);
1024  float compositeModelviewMatrix[16];
1025  VuoTransform_multiplyMatrices4x4(localModelviewMatrix, currentState.modelviewMatrix, compositeModelviewMatrix);
1026 
1027  if (!function((VuoSceneObject)currentObject, compositeModelviewMatrix))
1028  return;
1029 
1030  if (currentObject->type == VuoSceneObjectSubType_Group)
1031  {
1032  // Prepend this object's childObjects to the objectsToVisit queue.
1033  long childObjectCount = VuoListGetCount_VuoSceneObject(currentObject->childObjects);
1034  if (childObjectCount)
1035  {
1036  VuoSceneObject_treeState childState;
1037  childState.objectCount = childObjectCount;
1038  childState.objects = VuoListGetData_VuoSceneObject(currentObject->childObjects);
1039  memcpy(childState.modelviewMatrix, compositeModelviewMatrix, sizeof(float[16]));
1040  objectsToVisit.push_front(childState);
1041  }
1042  }
1043  }
1044  }
1045 }
1046 
1050 static void VuoSceneObject_applyInternal(VuoSceneObject sceneObject, void (^function)(VuoSceneObject currentObject, float modelviewMatrix[16]), float modelviewMatrix[16])
1051 {
1052  if (!sceneObject)
1053  return;
1054 
1055  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1056 
1057  float localModelviewMatrix[16];
1058  VuoTransform_getMatrix(so->transform, localModelviewMatrix);
1059  float compositeModelviewMatrix[16];
1060  VuoTransform_multiplyMatrices4x4(localModelviewMatrix, modelviewMatrix, compositeModelviewMatrix);
1061 
1062  function(sceneObject, compositeModelviewMatrix);
1063 
1064  if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
1065  {
1066  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
1067  for (unsigned long i = 1; i <= childObjectCount; ++i)
1068  {
1069  VuoSceneObject o = VuoListGetValue_VuoSceneObject(so->childObjects, i);
1070  VuoSceneObject_applyInternal(o, function, compositeModelviewMatrix);
1071  VuoListSetValue_VuoSceneObject(so->childObjects, o, i, false);
1072  }
1073  }
1074 }
1075 
1085 void VuoSceneObject_apply(VuoSceneObject object, void (^function)(VuoSceneObject currentObject, float modelviewMatrix[16]))
1086 {
1087  if (!object)
1088  return;
1089 
1090  float localModelviewMatrix[16];
1091  VuoTransform_getMatrix(VuoTransform_makeIdentity(), localModelviewMatrix);
1092 
1093  VuoSceneObject_applyInternal(object, function, localModelviewMatrix);
1094 }
1095 
1102 {
1103  if (!object)
1104  return;
1105 
1106  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1107  so->transform = VuoTransform_composite(so->transform, transform);
1108 }
1109 
1115 void VuoSceneObject_translate(VuoSceneObject object, VuoPoint3d translation)
1116 {
1117  if (!object)
1118  return;
1119 
1120  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1121  so->transform.translation += translation;
1122 }
1123 
1129 void VuoSceneObject_scale(VuoSceneObject object, VuoPoint3d scale)
1130 {
1131  if (!object)
1132  return;
1133 
1134  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1135  so->transform.scale *= scale;
1136 }
1137 
1144 {
1145  if (!object)
1146  return nullptr;
1147 
1148  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1149  return so->name;
1150 }
1151 
1161 {
1162  if (!object)
1163  return nullptr;
1164 
1165  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1166  if (so->type != VuoSceneObjectSubType_Group)
1167  return nullptr;
1168 
1169  return so->childObjects;
1170 }
1171 
1178 {
1179  if (!object)
1180  return VuoBlendMode_Normal;
1181 
1182  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1183  return so->blendMode;
1184 }
1185 
1192 {
1193  if (!object)
1194  return VuoSceneObjectSubType_Empty;
1195 
1196  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1197  return so->type;
1198 }
1199 
1206 uint64_t VuoSceneObject_getId(const VuoSceneObject object)
1207 {
1208  if (!object)
1209  return 0;
1210 
1211  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1212  return so->id;
1213 }
1214 
1224 {
1225  if (!object)
1226  return nullptr;
1227 
1228  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1229  return so->shader;
1230 }
1231 
1239 {
1240  if (!object)
1241  return false;
1242 
1243  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1244  return so->isRealSize;
1245 }
1246 
1255 {
1256  if (!object)
1257  return false;
1258 
1259  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1260  return so->preservePhysicalSize;
1261 }
1262 
1272 {
1273  if (!object)
1274  return nullptr;
1275 
1276  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1277  return so->mesh;
1278 }
1279 
1286 {
1287  if (!object)
1288  return nullptr;
1289 
1290  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1291  return so->text.text;
1292 }
1293 
1300 {
1301  if (!object)
1302  return VuoFont_makeDefault();
1303 
1304  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1305  return so->text.font;
1306 }
1307 
1316 {
1317  if (!object)
1318  return false;
1319 
1320  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1321  return so->text.scaleWithScene;
1322 }
1323 
1330 {
1331  if (!object)
1332  return 0;
1333 
1334  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1335  return so->text.wrapWidth;
1336 }
1337 
1344 {
1345  if (!object)
1346  return 0;
1347 
1348  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1349  return so->camera.fieldOfView;
1350 }
1351 
1358 {
1359  if (!object)
1360  return 0;
1361 
1362  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1363  return so->camera.width;
1364 }
1365 
1372 {
1373  if (!object)
1374  return 0;
1375 
1376  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1377  return so->camera.distanceMin;
1378 }
1379 
1386 {
1387  if (!object)
1388  return 0;
1389 
1390  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1391  return so->camera.distanceMax;
1392 }
1393 
1400 {
1401  if (!object)
1402  return 0;
1403 
1404  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1405  return so->camera.vignetteWidth;
1406 }
1407 
1414 {
1415  if (!object)
1416  return 0;
1417 
1418  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1419  return so->camera.vignetteSharpness;
1420 }
1421 
1428 {
1429  if (!object)
1430  return 0;
1431 
1432  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1433  return so->camera.intraocularDistance;
1434 }
1435 
1442 {
1443  if (!object)
1444  return 0;
1445 
1446  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1447  return so->camera.confocalDistance;
1448 }
1449 
1456 {
1457  if (!object)
1458  return (VuoColor){0,0,0,0};
1459 
1460  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1461  return so->light.color;
1462 }
1463 
1470 {
1471  if (!object)
1472  return 0;
1473 
1474  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1475  return so->light.brightness;
1476 }
1477 
1484 {
1485  if (!object)
1486  return 0;
1487 
1488  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1489  return so->light.range;
1490 }
1491 
1498 {
1499  if (!object)
1500  return 0;
1501 
1502  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1503  return so->light.sharpness;
1504 }
1505 
1512 {
1513  if (!object)
1514  return 0;
1515 
1516  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1517  return so->light.cone;
1518 }
1519 
1526 {
1527  if (!object)
1528  return VuoTransform_makeIdentity();
1529 
1530  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1531  return so->transform;
1532 }
1533 
1540 {
1541  if (!object)
1542  return (VuoPoint3d){0,0,0};
1543 
1544  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1545  return so->transform.translation;
1546 }
1547 
1554 {
1555  if (!object)
1556  return;
1557 
1558  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1559  so->type = type;
1560 }
1561 
1568 void VuoSceneObject_setId(VuoSceneObject object, uint64_t id)
1569 {
1570  if (!object)
1571  return;
1572 
1573  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1574  so->id = id;
1575 }
1576 
1583 {
1584  if (!object)
1585  return;
1586 
1587  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1588  VuoRetain(name);
1589  VuoRelease(so->name);
1590  so->name = name;
1591 }
1592 
1599 {
1600  if (!object)
1601  return;
1602 
1603  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1604  if (so->type != VuoSceneObjectSubType_Group)
1605  return;
1606 
1607  VuoRetain(childObjects);
1608  VuoRelease(so->childObjects);
1609  so->childObjects = childObjects;
1610 }
1611 
1618 {
1619  if (!object)
1620  return;
1621 
1622  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1623  VuoRetain(mesh);
1624  VuoRelease(so->mesh);
1625  so->mesh = mesh;
1626 }
1627 
1634 {
1635  if (!object)
1636  return;
1637 
1638  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1639  so->transform = transform;
1640 }
1641 
1647 void VuoSceneObject_setTranslation(VuoSceneObject object, VuoPoint3d translation)
1648 {
1649  if (!object)
1650  return;
1651 
1652  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1653  so->transform.translation = translation;
1654 }
1655 
1661 void VuoSceneObject_setScale(VuoSceneObject object, VuoPoint3d scale)
1662 {
1663  if (!object)
1664  return;
1665 
1666  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1667  so->transform.scale = scale;
1668 }
1669 
1676 {
1677  if (!object)
1678  return;
1679 
1680  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1681  VuoRetain(shader);
1682  VuoRelease(so->shader);
1683  so->shader = shader;
1684 }
1685 
1690 {
1691  if (!object)
1692  return;
1693 
1694  VuoSceneObject_apply(object, ^(VuoSceneObject currentObject, float modelviewMatrix[16]){
1696  VuoMesh_setFaceCulling(m, faceCulling);
1697  VuoSceneObject_setMesh(currentObject, m);
1698  });
1699 }
1700 
1714 {
1715  if (!object)
1716  return;
1717 
1718  VuoSceneObject_apply(object, ^(VuoSceneObject currentObject, float modelviewMatrix[16]){
1719  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
1720  co->blendMode = blendMode;
1721  });
1722 }
1723 
1730 void VuoSceneObject_setRealSize(VuoSceneObject object, bool isRealSize)
1731 {
1732  if (!object)
1733  return;
1734 
1735  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1736  so->isRealSize = isRealSize;
1737 }
1738 
1745 void VuoSceneObject_setPreservePhysicalSize(VuoSceneObject object, bool shouldPreservePhysicalSize)
1746 {
1747  if (!object)
1748  return;
1749 
1750  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1751  so->preservePhysicalSize = shouldPreservePhysicalSize;
1752 }
1753 
1760 {
1761  if (!object)
1762  return;
1763 
1764  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1765  VuoRetain(text);
1766  VuoRelease(so->text.text);
1767  so->text.text = text;
1768 }
1769 
1776 {
1777  if (!object)
1778  return;
1779 
1780  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1781  VuoFont_retain(font);
1782  VuoFont_release(so->text.font);
1783  so->text.font = font;
1784 }
1785 
1792 {
1793  if (!object)
1794  return;
1795 
1796  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1797  so->camera.fieldOfView = fieldOfView;
1798 }
1799 
1806 {
1807  if (!object)
1808  return;
1809 
1810  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1811  so->camera.distanceMin = distanceMin;
1812 }
1813 
1820 {
1821  if (!object)
1822  return;
1823 
1824  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1825  so->camera.distanceMax = distanceMax;
1826 }
1827 
1839 {
1840  if (!object)
1841  return nullptr;
1842 
1843  VuoSceneObject_internal *o = (VuoSceneObject_internal *)object;
1844 
1845  VuoSceneObject_internal *co = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
1846 
1847  co->type = o->type;
1848  co->id = o->id;
1849  VuoSceneObject_setName((VuoSceneObject)co, o->name);
1850  co->transform = o->transform;
1851  VuoSceneObject_setMesh((VuoSceneObject)co, o->mesh);
1852  VuoSceneObject_setShader((VuoSceneObject)co, o->shader); // @todo
1853  co->isRealSize = o->isRealSize;
1854  co->preservePhysicalSize = o->preservePhysicalSize;
1855  co->blendMode = o->blendMode;
1856 
1857  if (o->type == VuoSceneObjectSubType_Group && o->childObjects)
1858  {
1859  co->childObjects = VuoListCreate_VuoSceneObject();
1860  VuoRetain(co->childObjects);
1861  VuoListForeach_VuoSceneObject(o->childObjects, ^(const VuoSceneObject object){
1862  VuoListAppendValue_VuoSceneObject(co->childObjects, VuoSceneObject_copy(object));
1863  return true;
1864  });
1865  }
1866 
1867  if (o->type == VuoSceneObjectSubType_PerspectiveCamera
1868  || o->type == VuoSceneObjectSubType_StereoCamera
1869  || o->type == VuoSceneObjectSubType_OrthographicCamera
1870  || o->type == VuoSceneObjectSubType_FisheyeCamera)
1871  {
1872  co->camera.fieldOfView = o->camera.fieldOfView;
1873  co->camera.width = o->camera.width;
1874  co->camera.distanceMin = o->camera.distanceMin;
1875  co->camera.distanceMax = o->camera.distanceMax;
1876  co->camera.confocalDistance = o->camera.confocalDistance;
1877  co->camera.intraocularDistance = o->camera.intraocularDistance;
1878  co->camera.vignetteWidth = o->camera.vignetteWidth;
1879  co->camera.vignetteSharpness = o->camera.vignetteSharpness;
1880  }
1881  else if (o->type == VuoSceneObjectSubType_AmbientLight
1882  || o->type == VuoSceneObjectSubType_PointLight
1883  || o->type == VuoSceneObjectSubType_Spotlight)
1884  {
1885  co->light.color = o->light.color;
1886  co->light.brightness = o->light.brightness;
1887  co->light.range = o->light.range;
1888  co->light.cone = o->light.cone;
1889  co->light.sharpness = o->light.sharpness;
1890  }
1891  else if (o->type == VuoSceneObjectSubType_Text)
1892  {
1893  VuoSceneObject_setText((VuoSceneObject)co, o->text.text);
1894  VuoSceneObject_setTextFont((VuoSceneObject)co, o->text.font);
1895  co->text.scaleWithScene = o->text.scaleWithScene;
1896  co->text.wrapWidth = o->text.wrapWidth;
1897  }
1898 
1899  return (VuoSceneObject)co;
1900 }
1901 
1906 {
1907  if (!so)
1908  return VuoBox_make((VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0});
1909 
1910  __block bool haveGlobalBounds = false;
1911  __block VuoBox globalBounds;
1912 
1913  VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
1914  VuoBox bounds;
1915  bool foundBounds = VuoSceneObject_meshBounds(currentObject, &bounds, modelviewMatrix);
1916  if (foundBounds)
1917  {
1918  globalBounds = haveGlobalBounds ? VuoBox_encapsulate(globalBounds, bounds) : bounds;
1919  haveGlobalBounds = true;
1920  }
1921  return true;
1922  });
1923 
1924  if (haveGlobalBounds)
1925  return globalBounds;
1926  else
1927  return VuoBox_make( (VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0} );
1928 }
1929 
1933 bool VuoSceneObject_meshBounds(const VuoSceneObject sceneObject, VuoBox *bounds, float matrix[16])
1934 {
1935  if (!sceneObject)
1936  return false;
1937 
1938  if (VuoSceneObject_getVertexCount(sceneObject) < 1)
1939  return false;
1940 
1941  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1942  if (so->isRealSize
1943  || so->type == VuoSceneObjectSubType_Text)
1944  {
1945  // We don't know what the actual rendered size of the realSize layer will be,
1946  // but we can at least include its center point.
1947  *bounds = VuoBox_make(VuoPoint3d_make(matrix[12], matrix[13], matrix[14]), VuoPoint3d_make(0,0,0));
1948  }
1949  else
1950  {
1951  *bounds = VuoMesh_bounds(so->mesh, matrix);
1952 
1953  if (so->shader)
1954  {
1955  bounds->size.x *= so->shader->objectScale;
1956  bounds->size.y *= so->shader->objectScale;
1957  bounds->size.z *= so->shader->objectScale;
1958  }
1959  }
1960 
1961  return true;
1962 }
1963 
1968 {
1969  if (!sceneObject)
1970  return;
1971 
1972  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1973  VuoBox bounds = VuoSceneObject_bounds(sceneObject);
1974  so->transform.translation = VuoPoint3d_subtract(so->transform.translation, bounds.center);
1975 }
1976 
1983 {
1984  if (!sceneObject)
1985  return;
1986  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1987  VuoBox bounds = VuoSceneObject_bounds(sceneObject);
1988 
1989  float scale = fmax(fmax(bounds.size.x, bounds.size.y), bounds.size.z);
1990  if (fabs(scale) < 0.00001)
1991  return;
1992 
1993  so->transform.scale = VuoPoint3d_multiply(so->transform.scale, 1./scale);
1994  so->transform.translation = VuoPoint3d_multiply(so->transform.translation, 1./scale);
1995 }
1996 
2023 {
2024  json_object *o = NULL;
2025 
2026  int id = 0;
2027  if (json_object_object_get_ex(js, "id", &o))
2028  id = json_object_get_int64(o);
2029 
2030  VuoSceneObjectSubType type = VuoSceneObjectSubType_Empty;
2031  if (json_object_object_get_ex(js, "type", &o))
2032  type = VuoSceneObject_typeFromCString(json_object_get_string(o));
2033 
2034  VuoMesh mesh = NULL;
2035  if (json_object_object_get_ex(js, "mesh", &o))
2036  mesh = VuoMesh_makeFromJson(o);
2037 
2038  VuoShader shader = NULL;
2039  if (json_object_object_get_ex(js, "shader", &o))
2040  shader = VuoShader_makeFromJson(o);
2041 
2042  bool isRealSize = false;
2043  if (json_object_object_get_ex(js, "isRealSize", &o))
2044  isRealSize = VuoBoolean_makeFromJson(o);
2045 
2046  bool preservePhysicalSize = false;
2047  if (json_object_object_get_ex(js, "preservePhysicalSize", &o))
2048  preservePhysicalSize = VuoBoolean_makeFromJson(o);
2049 
2050  VuoBlendMode blendMode = VuoBlendMode_Normal;
2051  if (json_object_object_get_ex(js, "blendMode", &o))
2052  blendMode = VuoBlendMode_makeFromJson(o);
2053 
2054  VuoList_VuoSceneObject childObjects = NULL;
2055  if (json_object_object_get_ex(js, "childObjects", &o))
2056  childObjects = VuoList_VuoSceneObject_makeFromJson(o);
2057 
2058  float cameraFieldOfView;
2059  if (json_object_object_get_ex(js, "cameraFieldOfView", &o))
2060  cameraFieldOfView = json_object_get_double(o);
2061 
2062  float cameraWidth;
2063  if (json_object_object_get_ex(js, "cameraWidth", &o))
2064  cameraWidth = json_object_get_double(o);
2065 
2066  float cameraDistanceMin;
2067  if (json_object_object_get_ex(js, "cameraDistanceMin", &o))
2068  cameraDistanceMin = json_object_get_double(o);
2069 
2070  float cameraDistanceMax;
2071  if (json_object_object_get_ex(js, "cameraDistanceMax", &o))
2072  cameraDistanceMax = json_object_get_double(o);
2073 
2074  float cameraConfocalDistance;
2075  if (json_object_object_get_ex(js, "cameraConfocalDistance", &o))
2076  cameraConfocalDistance = json_object_get_double(o);
2077 
2078  float cameraIntraocularDistance;
2079  if (json_object_object_get_ex(js, "cameraIntraocularDistance", &o))
2080  cameraIntraocularDistance = json_object_get_double(o);
2081 
2082  float cameraVignetteWidth;
2083  if (json_object_object_get_ex(js, "cameraVignetteWidth", &o))
2084  cameraVignetteWidth = json_object_get_double(o);
2085 
2086  float cameraVignetteSharpness;
2087  if (json_object_object_get_ex(js, "cameraVignetteSharpness", &o))
2088  cameraVignetteSharpness = json_object_get_double(o);
2089 
2090  VuoColor lightColor;
2091  if (json_object_object_get_ex(js, "lightColor", &o))
2092  lightColor = VuoColor_makeFromJson(o);
2093 
2094  float lightBrightness;
2095  if (json_object_object_get_ex(js, "lightBrightness", &o))
2096  lightBrightness = json_object_get_double(o);
2097 
2098  float lightCone;
2099  if (json_object_object_get_ex(js, "lightCone", &o))
2100  lightCone = json_object_get_double(o);
2101 
2102  float lightRange;
2103  if (json_object_object_get_ex(js, "lightRange", &o))
2104  lightRange = json_object_get_double(o);
2105 
2106  float lightSharpness;
2107  if (json_object_object_get_ex(js, "lightSharpness", &o))
2108  lightSharpness = json_object_get_double(o);
2109 
2110  VuoText name = NULL;
2111  if (json_object_object_get_ex(js, "name", &o))
2112  name = VuoText_makeFromJson(o);
2113 
2114  json_object_object_get_ex(js, "transform", &o);
2115  VuoTransform transform = VuoTransform_makeFromJson(o);
2116 
2117  VuoText text = NULL;
2118  if (json_object_object_get_ex(js, "text", &o))
2119  text = VuoText_makeFromJson(o);
2120 
2121  VuoFont font;
2122  if (json_object_object_get_ex(js, "textFont", &o))
2123  font = VuoFont_makeFromJson(o);
2124 
2125  bool scaleWithScene = false;
2126  if (json_object_object_get_ex(js, "textScaleWithScene", &o))
2127  scaleWithScene = VuoBoolean_makeFromJson(o);
2128 
2129  float wrapWidth = INFINITY;
2130  if (json_object_object_get_ex(js, "textWrapWidth", &o))
2131  wrapWidth = json_object_get_double(o);
2132 
2133  VuoSceneObject obj;
2134  switch (type)
2135  {
2136  case VuoSceneObjectSubType_Empty:
2137  obj = nullptr;
2138  break;
2139  case VuoSceneObjectSubType_Group:
2140  obj = VuoSceneObject_makeGroup(childObjects, transform);
2141  break;
2142  case VuoSceneObjectSubType_Mesh:
2143  {
2144  obj = VuoSceneObject_makeMesh(mesh, shader, transform);
2145  VuoSceneObject_internal *so = (VuoSceneObject_internal *)obj;
2146  so->isRealSize = isRealSize;
2147  so->preservePhysicalSize = preservePhysicalSize;
2148  so->blendMode = blendMode;
2149  VuoSceneObject_setName(obj, name);
2150  break;
2151  }
2152  case VuoSceneObjectSubType_PerspectiveCamera:
2154  name,
2155  transform,
2156  cameraFieldOfView,
2157  cameraDistanceMin,
2158  cameraDistanceMax
2159  );
2160  break;
2161  case VuoSceneObjectSubType_StereoCamera:
2163  name,
2164  transform,
2165  cameraFieldOfView,
2166  cameraDistanceMin,
2167  cameraDistanceMax,
2168  cameraConfocalDistance,
2169  cameraIntraocularDistance
2170  );
2171  break;
2172  case VuoSceneObjectSubType_OrthographicCamera:
2174  name,
2175  transform,
2176  cameraWidth,
2177  cameraDistanceMin,
2178  cameraDistanceMax
2179  );
2180  break;
2181  case VuoSceneObjectSubType_FisheyeCamera:
2183  name,
2184  transform,
2185  cameraFieldOfView,
2186  cameraVignetteWidth,
2187  cameraVignetteSharpness
2188  );
2189  break;
2190  case VuoSceneObjectSubType_AmbientLight:
2191  obj = VuoSceneObject_makeAmbientLight(lightColor, lightBrightness);
2192  break;
2193  case VuoSceneObjectSubType_PointLight:
2194  obj = VuoSceneObject_makePointLight(lightColor, lightBrightness, transform.translation, lightRange, lightSharpness);
2195  break;
2196  case VuoSceneObjectSubType_Spotlight:
2197  obj = VuoSceneObject_makeSpotlight(lightColor, lightBrightness, transform, lightCone, lightRange, lightSharpness);
2198  break;
2199  case VuoSceneObjectSubType_Text:
2200  obj = VuoSceneObject_makeText(text, font, scaleWithScene, wrapWidth);
2201  VuoSceneObject_setTransform(obj, transform);
2202  VuoSceneObject_setMesh(obj, mesh);
2203  break;
2204  }
2205 
2206  VuoSceneObject_setId(obj, id);
2207 
2208  return obj;
2209 }
2210 
2215 {
2216  if (!sceneObject)
2217  return nullptr;
2218 
2219  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2220 
2221  json_object *js = json_object_new_object();
2222 
2223  json_object_object_add(js, "id", json_object_new_int64(so->id));
2224  json_object_object_add(js, "type", json_object_new_string(VuoSceneObject_cStringForType(so->type)));
2225 
2226  switch (so->type)
2227  {
2228  case VuoSceneObjectSubType_Empty:
2229  break;
2230 
2231  case VuoSceneObjectSubType_Mesh:
2232  if (so->mesh)
2233  json_object_object_add(js, "mesh", VuoMesh_getJson(so->mesh));
2234 
2235  if (so->shader)
2236  json_object_object_add(js, "shader", VuoShader_getJson(so->shader));
2237 
2238  json_object_object_add(js, "isRealSize", VuoBoolean_getJson(so->isRealSize));
2239 
2240  json_object_object_add(js, "preservePhysicalSize", VuoBoolean_getJson(so->preservePhysicalSize));
2241 
2242  if (so->blendMode != VuoBlendMode_Normal)
2243  json_object_object_add(js, "blendMode", VuoBlendMode_getJson(so->blendMode));
2244  break;
2245 
2246  case VuoSceneObjectSubType_Group:
2247  if (so->childObjects)
2248  json_object_object_add(js, "childObjects", VuoList_VuoSceneObject_getJson(so->childObjects));
2249  break;
2250 
2251  case VuoSceneObjectSubType_PerspectiveCamera:
2252  case VuoSceneObjectSubType_StereoCamera:
2253  case VuoSceneObjectSubType_OrthographicCamera:
2254  case VuoSceneObjectSubType_FisheyeCamera:
2255  {
2256  if (so->type != VuoSceneObjectSubType_FisheyeCamera)
2257  {
2258  json_object_object_add(js, "cameraDistanceMin", json_object_new_double(so->camera.distanceMin));
2259  json_object_object_add(js, "cameraDistanceMax", json_object_new_double(so->camera.distanceMax));
2260  }
2261 
2262  if (so->type == VuoSceneObjectSubType_PerspectiveCamera
2263  || so->type == VuoSceneObjectSubType_StereoCamera
2264  || so->type == VuoSceneObjectSubType_FisheyeCamera)
2265  json_object_object_add(js, "cameraFieldOfView", json_object_new_double(so->camera.fieldOfView));
2266 
2267  if (so->type == VuoSceneObjectSubType_StereoCamera)
2268  {
2269  json_object_object_add(js, "cameraConfocalDistance", json_object_new_double(so->camera.confocalDistance));
2270  json_object_object_add(js, "cameraIntraocularDistance", json_object_new_double(so->camera.intraocularDistance));
2271  }
2272 
2273  if (so->type == VuoSceneObjectSubType_OrthographicCamera)
2274  json_object_object_add(js, "cameraWidth", json_object_new_double(so->camera.width));
2275 
2276  if (so->type == VuoSceneObjectSubType_FisheyeCamera)
2277  {
2278  json_object_object_add(js, "cameraVignetteWidth", json_object_new_double(so->camera.vignetteWidth));
2279  json_object_object_add(js, "cameraVignetteSharpness", json_object_new_double(so->camera.vignetteSharpness));
2280  }
2281 
2282  break;
2283  }
2284 
2285  case VuoSceneObjectSubType_AmbientLight:
2286  case VuoSceneObjectSubType_PointLight:
2287  case VuoSceneObjectSubType_Spotlight:
2288  {
2289  json_object_object_add(js, "lightColor", VuoColor_getJson(so->light.color));
2290  json_object_object_add(js, "lightBrightness", json_object_new_double(so->light.brightness));
2291 
2292  if (so->type == VuoSceneObjectSubType_PointLight
2293  || so->type == VuoSceneObjectSubType_Spotlight)
2294  {
2295  json_object_object_add(js, "lightRange", json_object_new_double(so->light.range));
2296  json_object_object_add(js, "lightSharpness", json_object_new_double(so->light.sharpness));
2297  }
2298  if (so->type == VuoSceneObjectSubType_Spotlight)
2299  json_object_object_add(js, "lightCone", json_object_new_double(so->light.cone));
2300 
2301  break;
2302  }
2303 
2304  case VuoSceneObjectSubType_Text:
2305  {
2306  if (so->text.text)
2307  json_object_object_add(js, "text", VuoText_getJson(so->text.text));
2308 
2309  json_object_object_add(js, "textFont", VuoFont_getJson(so->text.font));
2310 
2311  if (so->mesh)
2312  json_object_object_add(js, "mesh", VuoMesh_getJson(so->mesh));
2313 
2314  json_object_object_add(js, "textScaleWithScene", VuoBoolean_getJson(so->text.scaleWithScene));
2315  json_object_object_add(js, "textWrapWidth", VuoReal_getJson(so->text.wrapWidth));
2316 
2317  break;
2318  }
2319  }
2320 
2321  if (so->name)
2322  json_object_object_add(js, "name", VuoText_getJson(so->name));
2323 
2324  if (so->type != VuoSceneObjectSubType_AmbientLight)
2325  json_object_object_add(js, "transform", VuoTransform_getJson(so->transform));
2326 
2327  return js;
2328 }
2329 
2333 unsigned long VuoSceneObject_getVertexCount(const VuoSceneObject sceneObject)
2334 {
2335  if (!sceneObject)
2336  return 0;
2337 
2338  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2339  if (!so->mesh)
2340  return 0;
2341 
2342  unsigned int vertexCount;
2343  VuoMesh_getCPUBuffers(so->mesh, &vertexCount, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
2344  return vertexCount;
2345 }
2346 
2350 unsigned long VuoSceneObject_getElementCount(const VuoSceneObject sceneObject)
2351 {
2352  if (!sceneObject)
2353  return 0;
2354 
2355  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2356  if (!so->mesh)
2357  return 0;
2358 
2359  unsigned int elementCount;
2360  VuoMesh_getCPUBuffers(so->mesh, nullptr, nullptr, nullptr, nullptr, nullptr, &elementCount, nullptr);
2361  return elementCount;
2362 }
2363 
2369 void VuoSceneObject_getStatistics(const VuoSceneObject sceneObject, unsigned long *descendantCount, unsigned long *totalVertexCount, unsigned long *totalElementCount)
2370 {
2371  if (!sceneObject)
2372  return;
2373 
2374  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2375  unsigned long childObjectCount = 0;
2376  if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2377  childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2378  *descendantCount += childObjectCount;
2379  *totalVertexCount += VuoSceneObject_getVertexCount(sceneObject);
2380  *totalElementCount += VuoSceneObject_getElementCount(sceneObject);
2381 
2382  if (so->type == VuoSceneObjectSubType_Group)
2383  for (unsigned long i = 1; i <= childObjectCount; ++i)
2384  VuoSceneObject_getStatistics(VuoListGetValue_VuoSceneObject(so->childObjects, i), descendantCount, totalVertexCount, totalElementCount);
2385 }
2386 
2391 {
2392  if (!sceneObject)
2393  return nullptr;
2394 
2395  // Exploit json_object's set-containing-only-unique-items data structure.
2396  __block json_object *names = json_object_new_object();
2397  VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2398  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2399  if (co->shader)
2400  json_object_object_add(names, co->shader->name, NULL);
2401  return true;
2402  });
2403 
2405  json_object_object_foreach(names, key, val)
2406  VuoListAppendValue_VuoText(nameList, VuoText_make(key));
2407  json_object_put(names);
2408  return nameList;
2409 }
2410 
2415 {
2416  if (!VuoSceneObject_isPopulated(sceneObject))
2417  return strdup("No object");
2418 
2419  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2420 
2421  if (so->type == VuoSceneObjectSubType_Text)
2422  {
2423  char *fontSummary = VuoFont_getSummary(so->text.font);
2424  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);
2425  free(fontSummary);
2426  return textSummary;
2427  }
2428 
2429  if (so->type == VuoSceneObjectSubType_PerspectiveCamera
2430  || so->type == VuoSceneObjectSubType_StereoCamera
2431  || so->type == VuoSceneObjectSubType_OrthographicCamera
2432  || so->type == VuoSceneObjectSubType_FisheyeCamera)
2433  {
2434  char *type = strdup(VuoSceneObject_cStringForType(so->type));
2435  type[0] = toupper(type[0]);
2436 
2437  float cameraViewValue = 0;
2438  const char *cameraViewString = "";
2439  if (so->type == VuoSceneObjectSubType_PerspectiveCamera)
2440  {
2441  cameraViewValue = so->camera.fieldOfView;
2442  cameraViewString = "° field of view";
2443  }
2444  else if (so->type == VuoSceneObjectSubType_StereoCamera)
2445  {
2446  cameraViewValue = so->camera.fieldOfView;
2447  cameraViewString = "° field of view (stereoscopic)";
2448  }
2449  else if (so->type == VuoSceneObjectSubType_OrthographicCamera)
2450  {
2451  cameraViewValue = so->camera.width;
2452  cameraViewString = " unit width";
2453  }
2454  else if (so->type == VuoSceneObjectSubType_FisheyeCamera)
2455  {
2456  cameraViewValue = so->camera.fieldOfView;
2457  cameraViewString = "° field of view (fisheye)";
2458  }
2459 
2460  char *translationString = VuoPoint3d_getSummary(so->transform.translation);
2461 
2462  const char *rotationLabel;
2463  char *rotationString;
2464  if (so->transform.type == VuoTransformTypeEuler)
2465  {
2466  rotationLabel = "Rotated";
2467  rotationString = VuoPoint3d_getSummary(VuoPoint3d_multiply(so->transform.rotationSource.euler, -180.f/M_PI));
2468  }
2469  else
2470  {
2471  rotationLabel = "Target";
2472  rotationString = VuoPoint3d_getSummary(so->transform.rotationSource.target);
2473  }
2474 
2475  char *valueAsString = VuoText_format("<div>%s named \"%s\"</div><div>At (%s)</div><div>%s (%s)</div><div>%g%s</div><div>Shows objects between depth %g and %g</div>",
2476  type, so->name ? so->name : "",
2477  translationString,
2478  rotationLabel, rotationString,
2479  cameraViewValue, cameraViewString,
2480  so->camera.distanceMin, so->camera.distanceMax);
2481  free(rotationString);
2482  free(translationString);
2483  free(type);
2484  return valueAsString;
2485  }
2486 
2487  if (so->type == VuoSceneObjectSubType_AmbientLight
2488  || so->type == VuoSceneObjectSubType_PointLight
2489  || so->type == VuoSceneObjectSubType_Spotlight)
2490  {
2491  char *type = strdup(VuoSceneObject_cStringForType(so->type));
2492  type[0] = toupper(type[0]);
2493 
2494  char *colorString = VuoColor_getShortSummary(so->light.color);
2495 
2496  char *positionRangeString;
2497  if (so->type == VuoSceneObjectSubType_PointLight
2498  || so->type == VuoSceneObjectSubType_Spotlight)
2499  {
2500  char *positionString = VuoPoint3d_getSummary(so->transform.translation);
2501 
2502  positionRangeString = VuoText_format("<div>Position (%s)</div><div>Range %g units (%g sharpness)</div>",
2503  positionString, so->light.range, so->light.sharpness);
2504 
2505  free(positionString);
2506  }
2507  else
2508  positionRangeString = strdup("");
2509 
2510  char *directionConeString;
2511  if (so->type == VuoSceneObjectSubType_Spotlight)
2512  {
2513  VuoPoint3d direction = VuoTransform_getDirection(so->transform);
2514  char *directionString = VuoPoint3d_getSummary(direction);
2515 
2516  directionConeString = VuoText_format("<div>Direction (%s)</div><div>Cone %g°</div>",
2517  directionString, so->light.cone * 180./M_PI);
2518 
2519  free(directionString);
2520  }
2521  else
2522  directionConeString = strdup("");
2523 
2524  char *valueAsString = VuoText_format("<div>%s</div><div>Color %s</div><div>Brightness %g</div>%s%s",
2525  type, colorString, so->light.brightness, positionRangeString, directionConeString);
2526 
2527  free(directionConeString);
2528  free(positionRangeString);
2529  free(colorString);
2530  free(type);
2531 
2532  return valueAsString;
2533  }
2534 
2535  unsigned long vertexCount = VuoSceneObject_getVertexCount(sceneObject);
2536  unsigned long elementCount = VuoSceneObject_getElementCount(sceneObject);
2537 
2538  char *transform = VuoTransform_getSummary(so->transform);
2539 
2540  unsigned long childObjectCount = 0;
2541  if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2542  childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2543  const char *childObjectPlural = childObjectCount == 1 ? "" : "s";
2544 
2545  char *descendants;
2546  if (childObjectCount)
2547  {
2548  unsigned long descendantCount = 0;
2549  unsigned long totalVertexCount = 0;
2550  unsigned long totalElementCount = 0;
2551  VuoSceneObject_getStatistics(sceneObject, &descendantCount, &totalVertexCount, &totalElementCount);
2552  const char *descendantPlural = descendantCount == 1 ? "" : "s";
2553 
2554  descendants = VuoText_format("<div>%ld descendant%s</div><div>Total, including descendants:</div><div>%ld vertices, %ld elements</div>",
2555  descendantCount, descendantPlural, totalVertexCount, totalElementCount);
2556  }
2557  else
2558  descendants = strdup("");
2559 
2560  VuoList_VuoText shaderNames = VuoSceneObject_findShaderNames(sceneObject);
2561  VuoRetain(shaderNames);
2562  char *shaderNamesSummary;
2563  if (VuoListGetCount_VuoText(shaderNames))
2564  {
2565  VuoInteger shaderNameCount = VuoListGetCount_VuoText(shaderNames);
2566  const char *header = "<div>Shaders:<ul>";
2567  VuoInteger shaderNameLength = strlen(header);
2568  for (VuoInteger i = 1; i <= shaderNameCount; ++i)
2569  shaderNameLength += strlen("<li>") + strlen(VuoListGetValue_VuoText(shaderNames, i)) + strlen("</li>");
2570  shaderNameLength += strlen("</ul></div>");
2571 
2572  shaderNamesSummary = (char *)malloc(shaderNameLength + 1);
2573  char *t = shaderNamesSummary;
2574  t = strcpy(t, header) + strlen(header);
2575  for (VuoInteger i = 1; i <= shaderNameCount; ++i)
2576  {
2577  t = strcpy(t, "<li>") + strlen("<li>");
2578  t = strcpy(t, VuoListGetValue_VuoText(shaderNames, i)) + strlen(VuoListGetValue_VuoText(shaderNames, i));
2579  t = strcpy(t, "</li>") + strlen("</li>");
2580  }
2581  t = strcpy(t, "</ul></div>");
2582  }
2583  else
2584  shaderNamesSummary = strdup("");
2585  VuoRelease(shaderNames);
2586 
2587  char *name = NULL;
2588  if (so->name)
2589  name = VuoText_format("<div>Object named \"%s\"</div>", so->name);
2590 
2591  char *valueAsString = VuoText_format("%s<div>%ld vertices, %ld elements</div><div>%s</div><div>ID %lld</div><div>%ld child object%s</div>%s%s",
2592  name ? name : "",
2593  vertexCount, elementCount,
2594  transform,
2595  so->id,
2596  childObjectCount, childObjectPlural,
2597  descendants, shaderNamesSummary);
2598 
2599  free(descendants);
2600  free(transform);
2601  free(shaderNamesSummary);
2602 
2603  return valueAsString;
2604 }
2605 
2609 static void VuoSceneObject_dump_internal(const VuoSceneObject sceneObject, unsigned int level)
2610 {
2611  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2612 
2613  for (unsigned int i=0; i<level; ++i)
2614  fprintf(stderr, "\t");
2615 
2616  if (!sceneObject)
2617  {
2618  fprintf(stderr, "no object\n");
2619  return;
2620  }
2621 
2622  fprintf(stderr, "%s (%s) ", VuoSceneObject_cStringForType(so->type), VuoTransform_getSummary(so->transform));
2623  if (so->type == VuoSceneObjectSubType_Mesh)
2624  fprintf(stderr, "%lu vertices, %lu elements, shader '%s' (%p)", VuoSceneObject_getVertexCount(sceneObject), VuoSceneObject_getElementCount(sceneObject), so->shader ? so->shader->name : "", so->shader);
2625  fprintf(stderr, "\n");
2626 
2627  if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2628  {
2629  unsigned int childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2630  for (unsigned int i=1; i<=childObjectCount; ++i)
2631  VuoSceneObject_dump_internal(VuoListGetValue_VuoSceneObject(so->childObjects, i), level+1);
2632  }
2633 }
2634 
2639 {
2641 }
2642 
2654 {
2655  if (!so)
2656  return nullptr;
2657 
2658  // Count the vertices.
2659 
2660  __block unsigned long triangleVertexCount = 0;
2661  __block unsigned long trianglePrimitiveCount = 0;
2662  __block VuoMesh_FaceCulling triangleFaceCulling = VuoMesh_CullBackfaces;
2663 
2664  __block unsigned long lineVertexCount = 0;
2665  __block unsigned long linePrimitiveCount = 0;
2666  __block double linePrimitiveSize = 0;
2667 
2668  __block unsigned long pointVertexCount = 0;
2669  __block unsigned long pointPrimitiveCount = 0;
2670  __block double pointPrimitiveSize = 0;
2671 
2672  VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2673  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2674  if (!co->mesh)
2675  return true;
2676 
2677  VuoMesh_ElementAssemblyMethod elementAssemblyMethod = VuoMesh_getElementAssemblyMethod(co->mesh);
2678 
2679  unsigned int vertexCount;
2680  VuoMesh_getCPUBuffers(co->mesh, &vertexCount, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
2681 
2682  if (elementAssemblyMethod == VuoMesh_IndividualTriangles
2683  || elementAssemblyMethod == VuoMesh_TriangleStrip
2684  || elementAssemblyMethod == VuoMesh_TriangleFan)
2685  {
2686  triangleVertexCount += vertexCount;
2687  trianglePrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2688  triangleFaceCulling = VuoMesh_getFaceCulling(co->mesh);
2689  }
2690  else if (elementAssemblyMethod == VuoMesh_IndividualLines
2691  || elementAssemblyMethod == VuoMesh_LineStrip)
2692  {
2693  lineVertexCount += vertexCount;
2694  linePrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2695  linePrimitiveSize = VuoMesh_getPrimitiveSize(co->mesh);
2696  }
2697  else if (elementAssemblyMethod == VuoMesh_Points)
2698  {
2699  pointVertexCount += vertexCount;
2700  pointPrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2701  pointPrimitiveSize = VuoMesh_getPrimitiveSize(co->mesh);
2702  }
2703 
2704  return true;
2705  });
2706 // VLog("triangles: %ldv %ldp lines: %ldv %ldp points: %ldv %ldp", triangleVertexCount, trianglePrimitiveCount, lineVertexCount, linePrimitiveCount, pointVertexCount, pointPrimitiveCount);
2707 
2708  if (!trianglePrimitiveCount && !linePrimitiveCount && !pointPrimitiveCount)
2709  return nullptr;
2710 
2711  // Allocate the buffers.
2712  unsigned int *triangleElements = nullptr;
2713  float *trianglePositions = nullptr, *triangleNormals = nullptr, *triangleTextureCoordinates = nullptr;
2714  unsigned int *lineElements = nullptr;
2715  float *linePositions = nullptr, *lineNormals = nullptr, *lineTextureCoordinates = nullptr;
2716  unsigned int *pointElements = nullptr;
2717  float *pointPositions = nullptr, *pointNormals = nullptr, *pointTextureCoordinates = nullptr;
2718  if (trianglePrimitiveCount)
2719  VuoMesh_allocateCPUBuffers(triangleVertexCount, &trianglePositions, &triangleNormals, &triangleTextureCoordinates, nullptr, trianglePrimitiveCount * 3, &triangleElements);
2720  if (linePrimitiveCount)
2721  VuoMesh_allocateCPUBuffers(lineVertexCount, &linePositions, &lineNormals, &lineTextureCoordinates, nullptr, linePrimitiveCount * 2, &lineElements);
2722  if (pointPrimitiveCount)
2723  VuoMesh_allocateCPUBuffers(pointVertexCount, &pointPositions, &pointNormals, &pointTextureCoordinates, nullptr, pointPrimitiveCount, &pointElements);
2724 
2725  // Copy the vertex attributes.
2726  __block unsigned long triangleVertexIndex = 0;
2727  __block unsigned long triangleElementIndex = 0;
2728  __block unsigned long lineVertexIndex = 0;
2729  __block unsigned long lineElementIndex = 0;
2730  __block unsigned long pointVertexIndex = 0;
2731  __block unsigned long pointElementIndex = 0;
2732  __block bool anyTextureCoordinates = false;
2733  VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2734  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2735  if (!co->mesh)
2736  return true;
2737 
2738  VuoMesh_ElementAssemblyMethod elementAssemblyMethod = VuoMesh_getElementAssemblyMethod(co->mesh);
2739 
2740  unsigned int vertexCount, elementCount, *elements;
2741  float *positions, *normals, *textureCoordinates;
2742  VuoMesh_getCPUBuffers(co->mesh, &vertexCount, &positions, &normals, &textureCoordinates, nullptr, &elementCount, &elements);
2743 
2744  if (textureCoordinates)
2745  anyTextureCoordinates = true;
2746 
2747  if (elementAssemblyMethod == VuoMesh_IndividualTriangles
2748  || elementAssemblyMethod == VuoMesh_TriangleStrip
2749  || elementAssemblyMethod == VuoMesh_TriangleFan)
2750  {
2751  unsigned long indexOffset = triangleVertexIndex;
2752  for (unsigned int n = 0; n < vertexCount; ++n)
2753  {
2754  VuoPoint3d p = VuoPoint3d_makeFromArray(&positions[n * 3]);
2755  VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2756  VuoPoint3d_setArray(&trianglePositions[triangleVertexIndex * 3], pt);
2757 
2758  if (normals)
2759  {
2760  VuoPoint3d r = VuoPoint3d_makeFromArray(&normals[n * 3]);
2761  VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2762  VuoPoint3d_setArray(&triangleNormals[triangleVertexIndex * 3], rt);
2763  }
2764 
2765  if (textureCoordinates)
2766  VuoPoint2d_setArray(&triangleTextureCoordinates[triangleVertexIndex * 2], VuoPoint2d_makeFromArray(&textureCoordinates[n * 2]));
2767 
2768  ++triangleVertexIndex;
2769  }
2770 
2771  if (elementAssemblyMethod == VuoMesh_IndividualTriangles)
2772  {
2773  if (elementCount)
2774  for (unsigned int n = 0; n < elementCount; ++n)
2775  triangleElements[triangleElementIndex++] = indexOffset + elements[n];
2776  else
2777  for (unsigned int n = 0; n < vertexCount; ++n)
2778  triangleElements[triangleElementIndex++] = indexOffset + n;
2779  }
2780  else if (elementAssemblyMethod == VuoMesh_TriangleStrip)
2781  {
2782  // Expand the triangle strip to individual triangles.
2783  if (elementCount)
2784  for (unsigned int n = 2; n < elementCount; ++n)
2785  if (n%2 == 0)
2786  {
2787  triangleElements[triangleElementIndex++] = indexOffset + elements[n-2];
2788  triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2789  triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2790  }
2791  else
2792  {
2793  triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2794  triangleElements[triangleElementIndex++] = indexOffset + elements[n-2];
2795  triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2796  }
2797  else
2798  for (unsigned int n = 0; n < vertexCount; ++n)
2799  if (n%2 == 0)
2800  {
2801  triangleElements[triangleElementIndex++] = indexOffset + n-2;
2802  triangleElements[triangleElementIndex++] = indexOffset + n-1;
2803  triangleElements[triangleElementIndex++] = indexOffset + n ;
2804  }
2805  else
2806  {
2807  triangleElements[triangleElementIndex++] = indexOffset + n-1;
2808  triangleElements[triangleElementIndex++] = indexOffset + n-2;
2809  triangleElements[triangleElementIndex++] = indexOffset + n ;
2810  }
2811  }
2812  else if (elementAssemblyMethod == VuoMesh_TriangleFan)
2813  {
2814  // Expand the triangle fan to individual triangles.
2815  if (elementCount)
2816  for (unsigned int n = 2; n < elementCount; ++n)
2817  {
2818  triangleElements[triangleElementIndex++] = indexOffset + elements[0 ];
2819  triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2820  triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2821  }
2822  else
2823  for (unsigned int n = 2; n < vertexCount; ++n)
2824  {
2825  triangleElements[triangleElementIndex++] = indexOffset + 0;
2826  triangleElements[triangleElementIndex++] = indexOffset + n-1;
2827  triangleElements[triangleElementIndex++] = indexOffset + n;
2828  }
2829  }
2830  }
2831  else if (elementAssemblyMethod == VuoMesh_IndividualLines
2832  || elementAssemblyMethod == VuoMesh_LineStrip)
2833  {
2834  unsigned long indexOffset = lineVertexIndex;
2835  for (unsigned int n = 0; n < vertexCount; ++n)
2836  {
2837  VuoPoint3d p = VuoPoint3d_makeFromArray(&positions[n * 3]);
2838  VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2839  VuoPoint3d_setArray(&linePositions[lineVertexIndex * 3], pt);
2840 
2841  if (normals)
2842  {
2843  VuoPoint3d r = VuoPoint3d_makeFromArray(&normals[n * 3]);
2844  VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2845  VuoPoint3d_setArray(&lineNormals[lineVertexIndex * 3], rt);
2846  }
2847 
2848  if (textureCoordinates)
2849  VuoPoint2d_setArray(&lineTextureCoordinates[lineVertexIndex], VuoPoint2d_makeFromArray(&textureCoordinates[n * 2]));
2850 
2851  ++lineVertexIndex;
2852  }
2853 
2854  if (elementAssemblyMethod == VuoMesh_IndividualLines)
2855  {
2856  if (elementCount)
2857  for (unsigned int n = 0; n < elementCount; ++n)
2858  lineElements[lineElementIndex++] = indexOffset + elements[n];
2859  else
2860  for (unsigned int n = 0; n < vertexCount; ++n)
2861  lineElements[lineElementIndex++] = indexOffset + n;
2862  }
2863  else if (elementAssemblyMethod == VuoMesh_LineStrip)
2864  {
2865  // Expand the line strip to individual lines.
2866  if (elementCount)
2867  for (unsigned int n = 1; n < elementCount; ++n)
2868  {
2869  lineElements[lineElementIndex++] = indexOffset + elements[n-1];
2870  lineElements[lineElementIndex++] = indexOffset + elements[n ];
2871  }
2872  else
2873  for (unsigned int n = 1; n < vertexCount; ++n)
2874  {
2875  lineElements[lineElementIndex++] = indexOffset + n-1;
2876  lineElements[lineElementIndex++] = indexOffset + n;
2877  }
2878  }
2879  }
2880  else if (elementAssemblyMethod == VuoMesh_Points)
2881  {
2882  unsigned long indexOffset = pointVertexIndex;
2883  for (unsigned int n = 0; n < vertexCount; ++n)
2884  {
2885  VuoPoint3d p = VuoPoint3d_makeFromArray(&positions[n * 3]);
2886  VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2887  VuoPoint3d_setArray(&pointPositions[pointVertexIndex * 3], pt);
2888 
2889  if (normals)
2890  {
2891  VuoPoint3d r = VuoPoint3d_makeFromArray(&normals[n * 3]);
2892  VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2893  VuoPoint3d_setArray(&pointNormals[pointVertexIndex * 3], rt);
2894  }
2895 
2896  if (textureCoordinates)
2897  VuoPoint2d_setArray(&pointTextureCoordinates[pointVertexIndex * 2], VuoPoint2d_makeFromArray(&textureCoordinates[n * 2]));
2898 
2899  ++pointVertexIndex;
2900  }
2901 
2902  if (elementCount)
2904  for (unsigned int n = 0; n < elementCount; ++n)
2905  pointElements[pointElementIndex++] = indexOffset + elements[n];
2906  else
2907  {
2908  for (unsigned int n = 0; n < vertexCount; ++n)
2909  pointElements[pointElementIndex++] = indexOffset + n;
2910  }
2911  }
2912 
2913  return true;
2914  });
2915 
2916 
2917  VuoMesh triangleMesh = nullptr;
2918  VuoMesh lineMesh = nullptr;
2919  VuoMesh pointMesh = nullptr;
2920  if (trianglePrimitiveCount)
2921  {
2922  if (!anyTextureCoordinates)
2923  {
2924  free(triangleTextureCoordinates);
2925  triangleTextureCoordinates = nullptr;
2926  }
2927  triangleMesh = VuoMesh_makeFromCPUBuffers(triangleVertexCount, trianglePositions, triangleNormals, triangleTextureCoordinates, nullptr, trianglePrimitiveCount * 3, triangleElements, VuoMesh_IndividualTriangles);
2928  VuoMesh_setFaceCulling(triangleMesh, triangleFaceCulling);
2929  }
2930  if (linePrimitiveCount)
2931  {
2932  if (!anyTextureCoordinates)
2933  {
2934  free(lineTextureCoordinates);
2935  lineTextureCoordinates = nullptr;
2936  }
2937  lineMesh = VuoMesh_makeFromCPUBuffers(lineVertexCount, linePositions, lineNormals, lineTextureCoordinates, nullptr, linePrimitiveCount * 2, lineElements, VuoMesh_IndividualLines);
2938  VuoMesh_setPrimitiveSize(lineMesh, linePrimitiveSize);
2939  }
2940  if (pointPrimitiveCount)
2941  {
2942  if (!anyTextureCoordinates)
2943  {
2944  free(pointTextureCoordinates);
2945  pointTextureCoordinates = nullptr;
2946  }
2947  pointMesh = VuoMesh_makeFromCPUBuffers(pointVertexCount, pointPositions, pointNormals, pointTextureCoordinates, nullptr, pointPrimitiveCount, pointElements, VuoMesh_Points);
2948  VuoMesh_setPrimitiveSize(pointMesh, pointPrimitiveSize);
2949  }
2950 
2951  if (triangleMesh && !lineMesh && !pointMesh)
2952  return VuoSceneObject_makeMesh(triangleMesh, NULL, VuoTransform_makeIdentity());
2953  else if (!triangleMesh && lineMesh && !pointMesh)
2954  return VuoSceneObject_makeMesh(lineMesh, NULL, VuoTransform_makeIdentity());
2955  else if (!triangleMesh && !lineMesh && pointMesh)
2956  return VuoSceneObject_makeMesh(pointMesh, NULL, VuoTransform_makeIdentity());
2957  else
2958  {
2960  if (triangleMesh)
2962  if (lineMesh)
2964  if (pointMesh)
2966  return VuoSceneObject_makeGroup(childObjects, VuoTransform_makeIdentity());
2967  }
2968 
2969  return NULL;
2970 }
2971 
2972 #define CSGJS_HEADER_ONLY
2973 #include "csgjs.cc"
2974 
2978 static csgjs_model VuoSceneObject_getCsgjsModel(const VuoSceneObject so)
2979 {
2980  if (!so)
2981  return csgjs_model();
2982 
2984  VuoSceneObject_internal *f = (VuoSceneObject_internal *)flat;
2985  if (!f->mesh)
2986  return csgjs_model();
2987 
2988  VuoLocal(flat);
2989 
2991  return csgjs_model();
2992 
2993  unsigned int vertexCount, elementCount, *elements;
2994  float *positions, *normals, *textureCoordinates;
2995  VuoMesh_getCPUBuffers(f->mesh, &vertexCount, &positions, &normals, &textureCoordinates, nullptr, &elementCount, &elements);
2996 
2997  csgjs_model cm;
2998  for (unsigned int n = 0; n < vertexCount; ++n)
2999  {
3000  csgjs_vertex v;
3001  v.pos = csgjs_vector(positions[n * 3], positions[n * 3 + 1], positions[n * 3 + 2]);
3002  if (normals)
3003  v.normal = csgjs_vector(normals[n * 3], normals[n * 3 + 1], normals[n * 3 + 2]);
3004  if (textureCoordinates)
3005  v.uv = csgjs_vector(textureCoordinates[n * 3], textureCoordinates[n * 3 + 1], 0);
3006  cm.vertices.push_back(v);
3007  }
3008  for (unsigned int n = 0; n < elementCount; ++n)
3009  cm.indices.push_back(elements[n]);
3010 
3011  return cm;
3012 }
3013 
3018 {
3019  unsigned int vertexCount = cm.vertices.size();
3020  unsigned int elementCount = cm.indices.size();
3021  unsigned int *elements;
3022  float *positions, *normals, *textureCoordinates;
3023  VuoMesh_allocateCPUBuffers(vertexCount, &positions, &normals, &textureCoordinates, nullptr, elementCount, &elements);
3024 
3025  const csgjs_vertex *vertex = &cm.vertices[0];
3026  for (unsigned int n = 0; n < vertexCount; ++n)
3027  {
3028  positions[n * 3 ] = vertex[n].pos.x;
3029  positions[n * 3 + 1] = vertex[n].pos.y;
3030  positions[n * 3 + 2] = vertex[n].pos.z;
3031  normals[n * 3 ] = vertex[n].normal.x;
3032  normals[n * 3 + 1] = vertex[n].normal.y;
3033  normals[n * 3 + 2] = vertex[n].normal.z;
3034  textureCoordinates[n * 2 ] = vertex[n].uv.x;
3035  textureCoordinates[n * 2 + 1] = vertex[n].uv.y;
3036  }
3037 
3038  const int *index = &cm.indices[0];
3039  for (unsigned int n = 0; n < elementCount; ++n)
3040  elements[n] = index[n];
3041 
3042  VuoMesh mesh = VuoMesh_makeFromCPUBuffers(vertexCount, positions, normals, textureCoordinates, nullptr, elementCount, elements, VuoMesh_IndividualTriangles);
3043 
3044  return VuoSceneObject_makeMesh(mesh, NULL, VuoTransform_makeIdentity());
3045 }
3046 
3051 static float convertQualityToEpsilon(float quality)
3052 {
3053  return pow(10, -VuoReal_clamp(quality, 0, 1) * 5.);
3054 }
3055 
3060 {
3061  float epsilon = convertQualityToEpsilon(quality);
3062 
3063  unsigned long objectCount = VuoListGetCount_VuoSceneObject(objects);
3064  if (objectCount == 0)
3065  return nullptr;
3066  if (objectCount == 1)
3067  return VuoListGetValue_VuoSceneObject(objects, 1);
3068 
3069  dispatch_queue_t queue = dispatch_queue_create("org.vuo.sceneobject.union", DISPATCH_QUEUE_CONCURRENT);
3070 
3071  csgjs_model *models = new csgjs_model[objectCount];
3072  for (unsigned long i = 0; i < objectCount; ++i)
3073  dispatch_async(queue, ^{
3075  });
3076 
3077  dispatch_barrier_sync(queue, ^{});
3078  dispatch_release(queue);
3079 
3080  csgjs_model cu = models[0];
3081  for (unsigned long i = 1; i < objectCount; ++i)
3082  cu = csgjs_union(cu, models[i], epsilon);
3083  delete[] models;
3085 }
3086 
3091 {
3092  float epsilon = convertQualityToEpsilon(quality);
3093 
3094  dispatch_semaphore_t finished = dispatch_semaphore_create(0);
3095 
3096  __block csgjs_model ca;
3097  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3099  dispatch_semaphore_signal(finished);
3100  });
3101 
3102  csgjs_model cb = VuoSceneObject_getCsgjsModel(b);
3103 
3104  dispatch_semaphore_wait(finished, DISPATCH_TIME_FOREVER);
3105  dispatch_release(finished);
3106 
3107  csgjs_model d = csgjs_difference(ca, cb, epsilon);
3109 }
3110 
3115 {
3116  float epsilon = convertQualityToEpsilon(quality);
3117 
3118  unsigned long objectCount = VuoListGetCount_VuoSceneObject(objects);
3119  if (objectCount == 0)
3120  return nullptr;
3121  if (objectCount == 1)
3122  return VuoListGetValue_VuoSceneObject(objects, 1);
3123 
3124  dispatch_queue_t queue = dispatch_queue_create("org.vuo.sceneobject.intersect", DISPATCH_QUEUE_CONCURRENT);
3125 
3126  csgjs_model *models = new csgjs_model[objectCount];
3127  for (unsigned long i = 0; i < objectCount; ++i)
3128  dispatch_async(queue, ^{
3130  });
3131 
3132  dispatch_barrier_sync(queue, ^{});
3133  dispatch_release(queue);
3134 
3135  csgjs_model ci = models[0];
3136  for (unsigned long i = 1; i < objectCount; ++i)
3137  ci = csgjs_intersection(ci, models[i], epsilon);
3138  delete[] models;
3140 }