Vuo  2.3.2
VuoSceneObject.cc
Go to the documentation of this file.
1 
10 #include <list>
11 
12 #include "VuoMacOSSDKWorkaround.h"
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
22 extern "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 
56 typedef 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 
122 void 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 
214 VuoSceneObject 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 
240 VuoSceneObject 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 
257 VuoSceneObject 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 
290 VuoSceneObject 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 
319 VuoSceneObject 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,
339  VuoTransform_makeEuler(VuoPoint3d_make(-.5,0,0), VuoPoint3d_make(0,-M_PI/2.,0), VuoPoint3d_make(1,1,1)));
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,
471  VuoTransform_makeEuler(VuoPoint3d_make(-.5,0,0), VuoPoint3d_make(0,-M_PI/2.,0), VuoPoint3d_make(1,1,1)));
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 
522 VuoSceneObject 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 
536 VuoSceneObject 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 
551 VuoSceneObject 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 
568 VuoSceneObject 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 
583 VuoSceneObject 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 
629 bool 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 
654  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
655  }
656 
657  return false;
658 }
659 
670 bool 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 
695  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
696  }
697 
698  return false;
699 }
700 
711 bool VuoSceneObject_findWithType(VuoSceneObject sceneObject, VuoSceneObjectSubType typeToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
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 
738  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
739  }
740 
741  return false;
742 }
743 
752 bool 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;
861  VuoSceneObject_setName((VuoSceneObject)so, VuoText_make("Ambient Light"));
862  so->light.color = color;
863  so->light.brightness = brightness;
864  return (VuoSceneObject)so;
865 }
866 
877 VuoSceneObject 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 
902 VuoSceneObject 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 
924 void 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 
964  VuoSceneObject keyLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .70, VuoPoint3d_make(-1,1,1), 5, .5);
965  VuoListAppendValue_VuoSceneObject(*pointLights, keyLight);
966 
967  VuoSceneObject fillLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .20, VuoPoint3d_make(.5,0,1), 5, 0);
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 
982 typedef struct
983 {
984  long objectCount;
985  const VuoSceneObject *objects;
986  float modelviewMatrix[16];
988 
1001 void 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 
1051 static 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 
1086 void 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 
1116 void 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 
1130 void 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 
1207 uint64_t VuoSceneObject_getId(const VuoSceneObject object)
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)
1529  return VuoTransform_makeIdentity();
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 
1569 void 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 
1648 void 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 
1662 void 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 
1731 void 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 
1746 void 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;
1850  VuoSceneObject_setName((VuoSceneObject)co, o->name);
1851  co->transform = o->transform;
1852  VuoSceneObject_setMesh((VuoSceneObject)co, o->mesh);
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 
1934 bool 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 
2051  VuoBlendMode blendMode = VuoBlendMode_Normal;
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);
2116  VuoTransform transform = VuoTransform_makeFromJson(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 
2334 unsigned 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 
2351 unsigned 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 
2370 void 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)
2407  VuoListAppendValue_VuoText(nameList, VuoText_make(key));
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 
2612 static 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)
2969  return VuoSceneObject_makeGroup(childObjects, VuoTransform_makeIdentity());
2970  }
2971 
2972  return NULL;
2973 }
2974 
2975 #define CSGJS_HEADER_ONLY
2976 #include "csgjs.cc"
2977 
2981 static csgjs_model VuoSceneObject_getCsgjsModel(const VuoSceneObject so)
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 * 3], textureCoordinates[n * 3 + 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 
3047  return VuoSceneObject_makeMesh(mesh, NULL, VuoTransform_makeIdentity());
3048 }
3049 
3054 static 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 }