Vuo  2.0.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 width, VuoReal alpha)
257 {
258  if (!image)
259  return nullptr;
260 
262  VuoShader_makeUnlitImageShader(image, alpha),
263  center,
264  rotation,
265  width,
266  image->pixelsHigh * width/image->pixelsWide
267  );
268 
269  return object;
270 }
271 
277 VuoSceneObject VuoSceneObject_makeLitImage(VuoImage image, VuoPoint3d center, VuoPoint3d rotation, VuoReal width, VuoReal alpha, VuoColor highlightColor, VuoReal shininess)
278 {
279  if (!image)
280  return nullptr;
281 
283  VuoShader_makeLitImageShader(image, alpha, highlightColor, shininess),
284  center,
285  rotation,
286  width,
287  image->pixelsHigh * width/image->pixelsWide
288  );
289 }
290 
294 VuoSceneObject VuoSceneObject_makeCube(VuoTransform transform, VuoShader frontShader, VuoShader leftShader, VuoShader rightShader, VuoShader backShader, VuoShader topShader, VuoShader bottomShader)
295 {
297 
298  VuoMesh quadMesh = VuoMesh_makeQuad();
299 
300  // Front Face
301  {
303  quadMesh,
304  frontShader,
306  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
307  }
308 
309  // Left Face
310  {
312  quadMesh,
313  leftShader,
314  VuoTransform_makeEuler(VuoPoint3d_make(-.5,0,0), VuoPoint3d_make(0,-M_PI/2.,0), VuoPoint3d_make(1,1,1)));
315  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
316  }
317 
318  // Right Face
319  {
321  quadMesh,
322  rightShader,
324  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
325  }
326 
327  // Back Face
328  {
330  quadMesh,
331  backShader,
333  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
334  }
335 
336  // Top Face
337  {
339  quadMesh,
340  topShader,
342  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
343  }
344 
345  // Bottom Face
346  {
348  quadMesh,
349  bottomShader,
351  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
352  }
353 
354  return VuoSceneObject_makeGroup(cubeChildObjects, transform);
355 }
356 
357 
364 {
365  return VuoSceneObject_makeMesh(VuoMesh_makeCube(), shader, transform);
366 }
367 
374 {
375  return VuoSceneObject_makeMesh(VuoMesh_makeCube(), shader, transform);
376 }
377 
384 {
386 }
387 
394 {
396 }
397 
404 {
405  return VuoSceneObject_makeCubeMulti(transform, 2, 2, 2,
412 }
413 
420  VuoShader front, VuoShader left, VuoShader right, VuoShader back, VuoShader top, VuoShader bottom)
421 {
423 
424  unsigned int _rows = MAX(2, MIN(512, rows));
425  unsigned int _columns = MAX(2, MIN(512, columns));
426  unsigned int _slices = MAX(2, MIN(512, slices));
427 
428  VuoMesh frontBackMesh = VuoMesh_makePlane(_columns, _rows);
429  VuoMesh leftRightMesh = VuoMesh_makePlane(_slices, _rows);
430  VuoMesh topBottomMesh = VuoMesh_makePlane(_columns, _slices);
431 
432  // Front Face
433  {
435  frontBackMesh,
436  front,
438  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
439  }
440 
441  // Left Face
442  {
444  leftRightMesh,
445  left,
446  VuoTransform_makeEuler(VuoPoint3d_make(-.5,0,0), VuoPoint3d_make(0,-M_PI/2.,0), VuoPoint3d_make(1,1,1)));
447  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
448  }
449 
450  // Right Face
451  {
453  leftRightMesh,
454  right,
456  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
457  }
458 
459  // Back Face
460  {
462  frontBackMesh,
463  back,
465  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
466  }
467 
468  // Top Face
469  {
471  topBottomMesh,
472  top,
474  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
475  }
476 
477  // Bottom Face
478  {
480  topBottomMesh,
481  bottom,
483  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
484  }
485 
486  return VuoSceneObject_makeGroup(cubeChildObjects, transform);
487 }
488 
497 VuoSceneObject VuoSceneObject_makeText(VuoText text, VuoFont font, VuoBoolean scaleWithScene, float wrapWidth)
498 {
499  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
500  so->type = VuoSceneObjectSubType_Text;
503  so->text.scaleWithScene = scaleWithScene;
504  so->text.wrapWidth = wrapWidth;
505  return (VuoSceneObject)so;
506 }
507 
511 VuoSceneObject VuoSceneObject_makePerspectiveCamera(VuoText name, VuoTransform transform, float fieldOfView, float distanceMin, float distanceMax)
512 {
513  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
514  so->type = VuoSceneObjectSubType_PerspectiveCamera;
516  so->transform = transform;
517  so->camera.fieldOfView = fieldOfView;
518  so->camera.distanceMin = distanceMin;
519  so->camera.distanceMax = distanceMax;
520  return (VuoSceneObject)so;
521 }
522 
526 VuoSceneObject VuoSceneObject_makeStereoCamera(VuoText name, VuoTransform transform, VuoReal fieldOfView, VuoReal distanceMin, VuoReal distanceMax, VuoReal confocalDistance, VuoReal intraocularDistance)
527 {
528  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
529  so->type = VuoSceneObjectSubType_StereoCamera;
531  so->transform = transform;
532  so->camera.fieldOfView = fieldOfView;
533  so->camera.distanceMin = distanceMin;
534  so->camera.distanceMax = distanceMax;
535  so->camera.confocalDistance = confocalDistance;
536  so->camera.intraocularDistance = intraocularDistance;
537  return (VuoSceneObject)so;
538 }
539 
543 VuoSceneObject VuoSceneObject_makeOrthographicCamera(VuoText name, VuoTransform transform, float width, float distanceMin, float distanceMax)
544 {
545  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
546  so->type = VuoSceneObjectSubType_OrthographicCamera;
548  so->transform = transform;
549  so->camera.width = width;
550  so->camera.distanceMin = distanceMin;
551  so->camera.distanceMax = distanceMax;
552  return (VuoSceneObject)so;
553 }
554 
558 VuoSceneObject VuoSceneObject_makeFisheyeCamera(VuoText name, VuoTransform transform, VuoReal fieldOfView, VuoReal vignetteWidth, VuoReal vignetteSharpness)
559 {
560  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
561  so->type = VuoSceneObjectSubType_FisheyeCamera;
563  so->transform = transform;
564  so->camera.fieldOfView = fieldOfView;
565 
566  // 0 and 1000 come from "Realtime Dome Imaging and Interaction" by Bailey/Clothier/Gebbie 2006.
567  so->camera.distanceMin = 0;
568  so->camera.distanceMax = 1000;
569 
570  so->camera.vignetteWidth = vignetteWidth;
571  so->camera.vignetteSharpness = vignetteSharpness;
572 
573  return (VuoSceneObject)so;
574 }
575 
580 {
582  VuoPoint3d_make(0,0,1),
583  VuoPoint3d_make(0,0,0),
584  VuoPoint3d_make(1,1,1)
585  );
587  VuoText_make("default camera"),
588  transform,
589  90,
590  0.1,
591  10.0
592  );
593 }
594 
604 bool VuoSceneObject_find(VuoSceneObject sceneObject, VuoText nameToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
605 {
606  if (!sceneObject)
607  return false;
608 
609  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
610 
611  if (VuoText_areEqual(so->name, nameToMatch))
612  {
613  *foundObject = (VuoSceneObject)so;
614  return true;
615  }
616 
617  if (so->type == VuoSceneObjectSubType_Group)
618  {
619  VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
620 
621  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
622  for (unsigned long i = 1; i <= childObjectCount; ++i)
623  {
624  VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
625  if (VuoSceneObject_find(childObject, nameToMatch, ancestorObjects, foundObject))
626  return true;
627  }
628 
629  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
630  }
631 
632  return false;
633 }
634 
645 bool VuoSceneObject_findById(VuoSceneObject sceneObject, uint64_t idToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
646 {
647  if (!sceneObject)
648  return false;
649 
650  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
651 
652  if (so->id == idToMatch)
653  {
654  *foundObject = (VuoSceneObject)so;
655  return true;
656  }
657 
658  if (so->type == VuoSceneObjectSubType_Group)
659  {
660  VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
661 
662  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
663  for (unsigned long i = 1; i <= childObjectCount; ++i)
664  {
665  VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
666  if (VuoSceneObject_findById(childObject, idToMatch, ancestorObjects, foundObject))
667  return true;
668  }
669 
670  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
671  }
672 
673  return false;
674 }
675 
686 bool VuoSceneObject_findWithType(VuoSceneObject sceneObject, VuoSceneObjectSubType typeToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
687 {
688  if (!sceneObject)
689  return false;
690 
691  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
692 
693  if (so->type == typeToMatch)
694  {
695  *foundObject = (VuoSceneObject)so;
696  return true;
697  }
698 
699  if (so->type == VuoSceneObjectSubType_Group)
700  {
701  VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
702 
703  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
704 
705  for (unsigned long i = 1; i <= childObjectCount; ++i)
706  {
707  VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
708 
709  if (VuoSceneObject_findWithType(childObject, typeToMatch, ancestorObjects, foundObject))
710  return true;
711  }
712 
713  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
714  }
715 
716  return false;
717 }
718 
727 bool VuoSceneObject_findCamera(VuoSceneObject sceneObject, VuoText nameToMatch, VuoSceneObject *foundCamera)
728 {
729  if (!sceneObject)
730  return false;
731 
732  __block bool didFindCamera = false;
733  VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
734  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
735  if ((co->type == VuoSceneObjectSubType_PerspectiveCamera
736  || co->type == VuoSceneObjectSubType_StereoCamera
737  || co->type == VuoSceneObjectSubType_OrthographicCamera
738  || co->type == VuoSceneObjectSubType_FisheyeCamera)
739  && (!nameToMatch || (co->name && nameToMatch && strstr(co->name, nameToMatch))))
740  {
741  *foundCamera = currentObject;
742  VuoSceneObject_setTransform(*foundCamera, VuoTransform_makeFromMatrix4x4(modelviewMatrix));
743  didFindCamera = true;
744  return false;
745  }
746  return true;
747  });
748 
749  return didFindCamera;
750 }
751 
756 {
757  if (!sceneObject)
758  return false;
759 
760  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
761  return so->type != VuoSceneObjectSubType_Empty;
762 }
763 
768 {
769  if (strcmp(typeString,"empty")==0)
770  return VuoSceneObjectSubType_Empty;
771  else if (strcmp(typeString,"group")==0)
772  return VuoSceneObjectSubType_Group;
773  else if (strcmp(typeString,"mesh")==0)
774  return VuoSceneObjectSubType_Mesh;
775  else if (strcmp(typeString,"camera-perspective")==0)
776  return VuoSceneObjectSubType_PerspectiveCamera;
777  else if (strcmp(typeString,"camera-stereo")==0)
778  return VuoSceneObjectSubType_StereoCamera;
779  else if (strcmp(typeString,"camera-orthographic")==0)
780  return VuoSceneObjectSubType_OrthographicCamera;
781  else if (strcmp(typeString,"camera-fisheye")==0)
782  return VuoSceneObjectSubType_FisheyeCamera;
783  else if (strcmp(typeString,"light-ambient")==0)
784  return VuoSceneObjectSubType_AmbientLight;
785  else if (strcmp(typeString,"light-point")==0)
786  return VuoSceneObjectSubType_PointLight;
787  else if (strcmp(typeString,"light-spot")==0)
788  return VuoSceneObjectSubType_Spotlight;
789  else if (strcmp(typeString,"text")==0)
790  return VuoSceneObjectSubType_Text;
791 
792  return VuoSceneObjectSubType_Empty;
793 }
794 
799 {
800  switch (type)
801  {
802  case VuoSceneObjectSubType_Group:
803  return "group";
804  case VuoSceneObjectSubType_Mesh:
805  return "mesh";
806  case VuoSceneObjectSubType_PerspectiveCamera:
807  return "camera-perspective";
808  case VuoSceneObjectSubType_StereoCamera:
809  return "camera-stereo";
810  case VuoSceneObjectSubType_OrthographicCamera:
811  return "camera-orthographic";
812  case VuoSceneObjectSubType_FisheyeCamera:
813  return "camera-fisheye";
814  case VuoSceneObjectSubType_AmbientLight:
815  return "light-ambient";
816  case VuoSceneObjectSubType_PointLight:
817  return "light-point";
818  case VuoSceneObjectSubType_Spotlight:
819  return "light-spot";
820  case VuoSceneObjectSubType_Text:
821  return "text";
822  // VuoSceneObjectSubType_Empty
823  default:
824  return "empty";
825  }
826 }
827 
833 {
834  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
835  so->type = VuoSceneObjectSubType_AmbientLight;
836  VuoSceneObject_setName((VuoSceneObject)so, VuoText_make("Ambient Light"));
837  so->light.color = color;
838  so->light.brightness = brightness;
839  return (VuoSceneObject)so;
840 }
841 
852 VuoSceneObject VuoSceneObject_makePointLight(VuoColor color, float brightness, VuoPoint3d position, float range, float sharpness)
853 {
854  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
855  so->type = VuoSceneObjectSubType_PointLight;
856  VuoText t = VuoText_make("Point Light");
858  so->light.color = color;
859  so->light.brightness = brightness;
860  so->light.range = range;
861  so->light.sharpness = MAX(MIN(sharpness,1),0);
862  so->transform = VuoTransform_makeEuler(position, VuoPoint3d_make(0,0,0), VuoPoint3d_make(1,1,1));
863  return (VuoSceneObject)so;
864 }
865 
877 VuoSceneObject VuoSceneObject_makeSpotlight(VuoColor color, float brightness, VuoTransform transform, float cone, float range, float sharpness)
878 {
879  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
880  so->type = VuoSceneObjectSubType_Spotlight;
882  so->light.color = color;
883  so->light.brightness = brightness;
884  so->light.cone = cone;
885  so->light.range = range;
886  so->light.sharpness = MAX(MIN(sharpness,1),0);
887  so->transform = transform;
888  return (VuoSceneObject)so;
889 }
890 
899 void VuoSceneObject_findLights(VuoSceneObject sceneObject, VuoColor *ambientColor, float *ambientBrightness, VuoList_VuoSceneObject *pointLights, VuoList_VuoSceneObject *spotLights)
900 {
901  __block VuoList_VuoColor ambientColors = VuoListCreate_VuoColor();
902  VuoRetain(ambientColors);
903 
904  *ambientBrightness = 0;
905  *pointLights = VuoListCreate_VuoSceneObject();
906  *spotLights = VuoListCreate_VuoSceneObject();
907 
908  VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
909  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
910  if (co->type == VuoSceneObjectSubType_AmbientLight)
911  {
912  VuoListAppendValue_VuoColor(ambientColors, co->light.color);
913  *ambientBrightness += co->light.brightness;
914  }
915  else if (co->type == VuoSceneObjectSubType_PointLight)
916  {
919  VuoListAppendValue_VuoSceneObject(*pointLights, l);
920  }
921  else if (co->type == VuoSceneObjectSubType_Spotlight)
922  {
925  VuoListAppendValue_VuoSceneObject(*spotLights, l);
926  }
927  return true;
928  });
929 
930  if (!VuoListGetCount_VuoColor(ambientColors)
931  && !VuoListGetCount_VuoSceneObject(*pointLights)
932  && !VuoListGetCount_VuoSceneObject(*spotLights))
933  {
934  *ambientColor = VuoColor_makeWithRGBA(1,1,1,1);
935  *ambientBrightness = 0.05;
936 
937  // https://en.wikipedia.org/wiki/Three-point_lighting
938 
939  VuoSceneObject keyLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .70, VuoPoint3d_make(-1,1,1), 5, .5);
940  VuoListAppendValue_VuoSceneObject(*pointLights, keyLight);
941 
942  VuoSceneObject fillLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .20, VuoPoint3d_make(.5,0,1), 5, 0);
943  VuoListAppendValue_VuoSceneObject(*pointLights, fillLight);
944 
945  VuoSceneObject backLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .15, VuoPoint3d_make(1,.75,-.5), 5, 0);
946  VuoListAppendValue_VuoSceneObject(*pointLights, backLight);
947  }
948  else
949  *ambientColor = VuoColor_average(ambientColors);
950 
951  VuoRelease(ambientColors);
952 }
953 
957 typedef struct
958 {
959  long objectCount;
960  const VuoSceneObject *objects;
961  float modelviewMatrix[16];
963 
976 void VuoSceneObject_visit(const VuoSceneObject object, bool (^function)(const VuoSceneObject currentObject, float modelviewMatrix[16]))
977 {
978  if (!object)
979  return;
980 
981  VuoSceneObject_treeState rootState;
982  rootState.objectCount = 1;
983  rootState.objects = &object;
984  VuoTransform_getMatrix(VuoTransform_makeIdentity(), rootState.modelviewMatrix);
985 
986  std::list<VuoSceneObject_treeState> objectsToVisit(1, rootState);
987  while (!objectsToVisit.empty())
988  {
989  VuoSceneObject_treeState currentState = objectsToVisit.front();
990  objectsToVisit.pop_front();
991 
992  for (long i = 0; i < currentState.objectCount; ++i)
993  {
994  VuoSceneObject_internal *currentObject = (VuoSceneObject_internal *)currentState.objects[i];
995  if (!currentObject)
996  continue;
997 
998  float localModelviewMatrix[16];
999  VuoTransform_getMatrix(currentObject->transform, localModelviewMatrix);
1000  float compositeModelviewMatrix[16];
1001  VuoTransform_multiplyMatrices4x4(localModelviewMatrix, currentState.modelviewMatrix, compositeModelviewMatrix);
1002 
1003  if (!function((VuoSceneObject)currentObject, compositeModelviewMatrix))
1004  return;
1005 
1006  if (currentObject->type == VuoSceneObjectSubType_Group)
1007  {
1008  // Prepend this object's childObjects to the objectsToVisit queue.
1009  long childObjectCount = VuoListGetCount_VuoSceneObject(currentObject->childObjects);
1010  if (childObjectCount)
1011  {
1012  VuoSceneObject_treeState childState;
1013  childState.objectCount = childObjectCount;
1014  childState.objects = VuoListGetData_VuoSceneObject(currentObject->childObjects);
1015  memcpy(childState.modelviewMatrix, compositeModelviewMatrix, sizeof(float[16]));
1016  objectsToVisit.push_front(childState);
1017  }
1018  }
1019  }
1020  }
1021 }
1022 
1026 static void VuoSceneObject_applyInternal(VuoSceneObject sceneObject, void (^function)(VuoSceneObject currentObject, float modelviewMatrix[16]), float modelviewMatrix[16])
1027 {
1028  if (!sceneObject)
1029  return;
1030 
1031  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1032 
1033  float localModelviewMatrix[16];
1034  VuoTransform_getMatrix(so->transform, localModelviewMatrix);
1035  float compositeModelviewMatrix[16];
1036  VuoTransform_multiplyMatrices4x4(localModelviewMatrix, modelviewMatrix, compositeModelviewMatrix);
1037 
1038  function(sceneObject, compositeModelviewMatrix);
1039 
1040  if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
1041  {
1042  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
1043  for (unsigned long i = 1; i <= childObjectCount; ++i)
1044  {
1045  VuoSceneObject o = VuoListGetValue_VuoSceneObject(so->childObjects, i);
1046  VuoSceneObject_applyInternal(o, function, compositeModelviewMatrix);
1047  VuoListSetValue_VuoSceneObject(so->childObjects, o, i, false);
1048  }
1049  }
1050 }
1051 
1061 void VuoSceneObject_apply(VuoSceneObject object, void (^function)(VuoSceneObject currentObject, float modelviewMatrix[16]))
1062 {
1063  if (!object)
1064  return;
1065 
1066  float localModelviewMatrix[16];
1067  VuoTransform_getMatrix(VuoTransform_makeIdentity(), localModelviewMatrix);
1068 
1069  VuoSceneObject_applyInternal(object, function, localModelviewMatrix);
1070 }
1071 
1078 {
1079  if (!object)
1080  return;
1081 
1082  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1083  so->transform = VuoTransform_composite(so->transform, transform);
1084 }
1085 
1091 void VuoSceneObject_translate(VuoSceneObject object, VuoPoint3d translation)
1092 {
1093  if (!object)
1094  return;
1095 
1096  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1097  so->transform.translation += translation;
1098 }
1099 
1105 void VuoSceneObject_scale(VuoSceneObject object, VuoPoint3d scale)
1106 {
1107  if (!object)
1108  return;
1109 
1110  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1111  so->transform.scale *= scale;
1112 }
1113 
1120 {
1121  if (!object)
1122  return nullptr;
1123 
1124  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1125  return so->name;
1126 }
1127 
1137 {
1138  if (!object)
1139  return nullptr;
1140 
1141  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1142  if (so->type != VuoSceneObjectSubType_Group)
1143  return nullptr;
1144 
1145  return so->childObjects;
1146 }
1147 
1154 {
1155  if (!object)
1156  return VuoBlendMode_Normal;
1157 
1158  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1159  return so->blendMode;
1160 }
1161 
1168 {
1169  if (!object)
1170  return VuoSceneObjectSubType_Empty;
1171 
1172  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1173  return so->type;
1174 }
1175 
1182 uint64_t VuoSceneObject_getId(const VuoSceneObject object)
1183 {
1184  if (!object)
1185  return 0;
1186 
1187  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1188  return so->id;
1189 }
1190 
1200 {
1201  if (!object)
1202  return nullptr;
1203 
1204  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1205  return so->shader;
1206 }
1207 
1215 {
1216  if (!object)
1217  return false;
1218 
1219  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1220  return so->isRealSize;
1221 }
1222 
1231 {
1232  if (!object)
1233  return false;
1234 
1235  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1236  return so->preservePhysicalSize;
1237 }
1238 
1248 {
1249  if (!object)
1250  return nullptr;
1251 
1252  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1253  return so->mesh;
1254 }
1255 
1262 {
1263  if (!object)
1264  return nullptr;
1265 
1266  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1267  return so->text.text;
1268 }
1269 
1276 {
1277  if (!object)
1278  return VuoFont_makeDefault();
1279 
1280  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1281  return so->text.font;
1282 }
1283 
1292 {
1293  if (!object)
1294  return false;
1295 
1296  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1297  return so->text.scaleWithScene;
1298 }
1299 
1306 {
1307  if (!object)
1308  return 0;
1309 
1310  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1311  return so->text.wrapWidth;
1312 }
1313 
1320 {
1321  if (!object)
1322  return 0;
1323 
1324  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1325  return so->camera.fieldOfView;
1326 }
1327 
1334 {
1335  if (!object)
1336  return 0;
1337 
1338  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1339  return so->camera.width;
1340 }
1341 
1348 {
1349  if (!object)
1350  return 0;
1351 
1352  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1353  return so->camera.distanceMin;
1354 }
1355 
1362 {
1363  if (!object)
1364  return 0;
1365 
1366  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1367  return so->camera.distanceMax;
1368 }
1369 
1376 {
1377  if (!object)
1378  return 0;
1379 
1380  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1381  return so->camera.vignetteWidth;
1382 }
1383 
1390 {
1391  if (!object)
1392  return 0;
1393 
1394  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1395  return so->camera.vignetteSharpness;
1396 }
1397 
1404 {
1405  if (!object)
1406  return 0;
1407 
1408  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1409  return so->camera.intraocularDistance;
1410 }
1411 
1418 {
1419  if (!object)
1420  return 0;
1421 
1422  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1423  return so->camera.confocalDistance;
1424 }
1425 
1432 {
1433  if (!object)
1434  return (VuoColor){0,0,0,0};
1435 
1436  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1437  return so->light.color;
1438 }
1439 
1446 {
1447  if (!object)
1448  return 0;
1449 
1450  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1451  return so->light.brightness;
1452 }
1453 
1460 {
1461  if (!object)
1462  return 0;
1463 
1464  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1465  return so->light.range;
1466 }
1467 
1474 {
1475  if (!object)
1476  return 0;
1477 
1478  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1479  return so->light.sharpness;
1480 }
1481 
1488 {
1489  if (!object)
1490  return 0;
1491 
1492  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1493  return so->light.cone;
1494 }
1495 
1502 {
1503  if (!object)
1504  return VuoTransform_makeIdentity();
1505 
1506  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1507  return so->transform;
1508 }
1509 
1516 {
1517  if (!object)
1518  return (VuoPoint3d){0,0,0};
1519 
1520  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1521  return so->transform.translation;
1522 }
1523 
1530 {
1531  if (!object)
1532  return;
1533 
1534  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1535  so->type = type;
1536 }
1537 
1544 void VuoSceneObject_setId(VuoSceneObject object, uint64_t id)
1545 {
1546  if (!object)
1547  return;
1548 
1549  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1550  so->id = id;
1551 }
1552 
1559 {
1560  if (!object)
1561  return;
1562 
1563  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1564  VuoRetain(name);
1565  VuoRelease(so->name);
1566  so->name = name;
1567 }
1568 
1575 {
1576  if (!object)
1577  return;
1578 
1579  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1580  if (so->type != VuoSceneObjectSubType_Group)
1581  return;
1582 
1583  VuoRetain(childObjects);
1584  VuoRelease(so->childObjects);
1585  so->childObjects = childObjects;
1586 }
1587 
1594 {
1595  if (!object)
1596  return;
1597 
1598  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1599  VuoRetain(mesh);
1600  VuoRelease(so->mesh);
1601  so->mesh = mesh;
1602 }
1603 
1610 {
1611  if (!object)
1612  return;
1613 
1614  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1615  so->transform = transform;
1616 }
1617 
1623 void VuoSceneObject_setTranslation(VuoSceneObject object, VuoPoint3d translation)
1624 {
1625  if (!object)
1626  return;
1627 
1628  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1629  so->transform.translation = translation;
1630 }
1631 
1637 void VuoSceneObject_setScale(VuoSceneObject object, VuoPoint3d scale)
1638 {
1639  if (!object)
1640  return;
1641 
1642  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1643  so->transform.scale = scale;
1644 }
1645 
1652 {
1653  if (!object)
1654  return;
1655 
1656  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1657  VuoRetain(shader);
1658  VuoRelease(so->shader);
1659  so->shader = shader;
1660 }
1661 
1666 {
1667  if (!object)
1668  return;
1669 
1670  VuoSceneObject_apply(object, ^(VuoSceneObject currentObject, float modelviewMatrix[16]){
1672  VuoMesh_setFaceCulling(m, faceCulling);
1673  VuoSceneObject_setMesh(currentObject, m);
1674  });
1675 }
1676 
1690 {
1691  if (!object)
1692  return;
1693 
1694  VuoSceneObject_apply(object, ^(VuoSceneObject currentObject, float modelviewMatrix[16]){
1695  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
1696  co->blendMode = blendMode;
1697  });
1698 }
1699 
1706 void VuoSceneObject_setRealSize(VuoSceneObject object, bool isRealSize)
1707 {
1708  if (!object)
1709  return;
1710 
1711  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1712  so->isRealSize = isRealSize;
1713 }
1714 
1721 void VuoSceneObject_setPreservePhysicalSize(VuoSceneObject object, bool shouldPreservePhysicalSize)
1722 {
1723  if (!object)
1724  return;
1725 
1726  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1727  so->preservePhysicalSize = shouldPreservePhysicalSize;
1728 }
1729 
1736 {
1737  if (!object)
1738  return;
1739 
1740  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1741  VuoRetain(text);
1742  VuoRelease(so->text.text);
1743  so->text.text = text;
1744 }
1745 
1752 {
1753  if (!object)
1754  return;
1755 
1756  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1757  VuoFont_retain(font);
1758  VuoFont_release(so->text.font);
1759  so->text.font = font;
1760 }
1761 
1768 {
1769  if (!object)
1770  return;
1771 
1772  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1773  so->camera.fieldOfView = fieldOfView;
1774 }
1775 
1782 {
1783  if (!object)
1784  return;
1785 
1786  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1787  so->camera.distanceMin = distanceMin;
1788 }
1789 
1796 {
1797  if (!object)
1798  return;
1799 
1800  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1801  so->camera.distanceMax = distanceMax;
1802 }
1803 
1815 {
1816  if (!object)
1817  return nullptr;
1818 
1819  VuoSceneObject_internal *o = (VuoSceneObject_internal *)object;
1820 
1821  VuoSceneObject_internal *co = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
1822 
1823  co->type = o->type;
1824  co->id = o->id;
1825  VuoSceneObject_setName((VuoSceneObject)co, o->name);
1826  co->transform = o->transform;
1827  VuoSceneObject_setMesh((VuoSceneObject)co, o->mesh);
1828  VuoSceneObject_setShader((VuoSceneObject)co, o->shader); // @todo
1829  co->isRealSize = o->isRealSize;
1830  co->preservePhysicalSize = o->preservePhysicalSize;
1831  co->blendMode = o->blendMode;
1832 
1833  if (o->type == VuoSceneObjectSubType_Group && o->childObjects)
1834  {
1835  co->childObjects = VuoListCreate_VuoSceneObject();
1836  VuoRetain(co->childObjects);
1837  VuoListForeach_VuoSceneObject(o->childObjects, ^(const VuoSceneObject object){
1838  VuoListAppendValue_VuoSceneObject(co->childObjects, VuoSceneObject_copy(object));
1839  return true;
1840  });
1841  }
1842 
1843  if (o->type == VuoSceneObjectSubType_PerspectiveCamera
1844  || o->type == VuoSceneObjectSubType_StereoCamera
1845  || o->type == VuoSceneObjectSubType_OrthographicCamera
1846  || o->type == VuoSceneObjectSubType_FisheyeCamera)
1847  {
1848  co->camera.fieldOfView = o->camera.fieldOfView;
1849  co->camera.width = o->camera.width;
1850  co->camera.distanceMin = o->camera.distanceMin;
1851  co->camera.distanceMax = o->camera.distanceMax;
1852  co->camera.confocalDistance = o->camera.confocalDistance;
1853  co->camera.intraocularDistance = o->camera.intraocularDistance;
1854  co->camera.vignetteWidth = o->camera.vignetteWidth;
1855  co->camera.vignetteSharpness = o->camera.vignetteSharpness;
1856  }
1857  else if (o->type == VuoSceneObjectSubType_AmbientLight
1858  || o->type == VuoSceneObjectSubType_PointLight
1859  || o->type == VuoSceneObjectSubType_Spotlight)
1860  {
1861  co->light.color = o->light.color;
1862  co->light.brightness = o->light.brightness;
1863  co->light.range = o->light.range;
1864  co->light.cone = o->light.cone;
1865  co->light.sharpness = o->light.sharpness;
1866  }
1867  else if (o->type == VuoSceneObjectSubType_Text)
1868  {
1869  VuoSceneObject_setText((VuoSceneObject)co, o->text.text);
1870  VuoSceneObject_setTextFont((VuoSceneObject)co, o->text.font);
1871  co->text.scaleWithScene = o->text.scaleWithScene;
1872  co->text.wrapWidth = o->text.wrapWidth;
1873  }
1874 
1875  return (VuoSceneObject)co;
1876 }
1877 
1882 {
1883  if (!so)
1884  return VuoBox_make((VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0});
1885 
1886  __block bool haveGlobalBounds = false;
1887  __block VuoBox globalBounds;
1888 
1889  VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
1890  VuoBox bounds;
1891  bool foundBounds = VuoSceneObject_meshBounds(currentObject, &bounds, modelviewMatrix);
1892  if (foundBounds)
1893  {
1894  globalBounds = haveGlobalBounds ? VuoBox_encapsulate(globalBounds, bounds) : bounds;
1895  haveGlobalBounds = true;
1896  }
1897  return true;
1898  });
1899 
1900  if (haveGlobalBounds)
1901  return globalBounds;
1902  else
1903  return VuoBox_make( (VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0} );
1904 }
1905 
1909 bool VuoSceneObject_meshBounds(const VuoSceneObject sceneObject, VuoBox *bounds, float matrix[16])
1910 {
1911  if (!sceneObject)
1912  return false;
1913 
1914  if (VuoSceneObject_getVertexCount(sceneObject) < 1)
1915  return false;
1916 
1917  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1918  if (so->isRealSize
1919  || so->type == VuoSceneObjectSubType_Text)
1920  {
1921  // We don't know what the actual rendered size of the realSize layer will be,
1922  // but we can at least include its center point.
1923  *bounds = VuoBox_make(VuoPoint3d_make(matrix[12], matrix[13], matrix[14]), VuoPoint3d_make(0,0,0));
1924  }
1925  else
1926  {
1927  *bounds = VuoMesh_bounds(so->mesh, matrix);
1928 
1929  if (so->shader)
1930  {
1931  bounds->size.x *= so->shader->objectScale;
1932  bounds->size.y *= so->shader->objectScale;
1933  bounds->size.z *= so->shader->objectScale;
1934  }
1935  }
1936 
1937  return true;
1938 }
1939 
1944 {
1945  if (!sceneObject)
1946  return;
1947 
1948  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1949  VuoBox bounds = VuoSceneObject_bounds(sceneObject);
1950  so->transform.translation = VuoPoint3d_subtract(so->transform.translation, bounds.center);
1951 }
1952 
1959 {
1960  if (!sceneObject)
1961  return;
1962  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1963  VuoBox bounds = VuoSceneObject_bounds(sceneObject);
1964 
1965  float scale = fmax(fmax(bounds.size.x, bounds.size.y), bounds.size.z);
1966  if (fabs(scale) < 0.00001)
1967  return;
1968 
1969  so->transform.scale = VuoPoint3d_multiply(so->transform.scale, 1./scale);
1970  so->transform.translation = VuoPoint3d_multiply(so->transform.translation, 1./scale);
1971 }
1972 
1999 {
2000  json_object *o = NULL;
2001 
2002  int id = 0;
2003  if (json_object_object_get_ex(js, "id", &o))
2004  id = json_object_get_int64(o);
2005 
2006  VuoSceneObjectSubType type = VuoSceneObjectSubType_Empty;
2007  if (json_object_object_get_ex(js, "type", &o))
2008  type = VuoSceneObject_typeFromCString(json_object_get_string(o));
2009 
2010  VuoMesh mesh = NULL;
2011  if (json_object_object_get_ex(js, "mesh", &o))
2012  mesh = VuoMesh_makeFromJson(o);
2013 
2014  VuoShader shader = NULL;
2015  if (json_object_object_get_ex(js, "shader", &o))
2016  shader = VuoShader_makeFromJson(o);
2017 
2018  bool isRealSize = false;
2019  if (json_object_object_get_ex(js, "isRealSize", &o))
2020  isRealSize = VuoBoolean_makeFromJson(o);
2021 
2022  bool preservePhysicalSize = false;
2023  if (json_object_object_get_ex(js, "preservePhysicalSize", &o))
2024  preservePhysicalSize = VuoBoolean_makeFromJson(o);
2025 
2026  VuoBlendMode blendMode = VuoBlendMode_Normal;
2027  if (json_object_object_get_ex(js, "blendMode", &o))
2028  blendMode = VuoBlendMode_makeFromJson(o);
2029 
2030  VuoList_VuoSceneObject childObjects = NULL;
2031  if (json_object_object_get_ex(js, "childObjects", &o))
2032  childObjects = VuoList_VuoSceneObject_makeFromJson(o);
2033 
2034  float cameraFieldOfView;
2035  if (json_object_object_get_ex(js, "cameraFieldOfView", &o))
2036  cameraFieldOfView = json_object_get_double(o);
2037 
2038  float cameraWidth;
2039  if (json_object_object_get_ex(js, "cameraWidth", &o))
2040  cameraWidth = json_object_get_double(o);
2041 
2042  float cameraDistanceMin;
2043  if (json_object_object_get_ex(js, "cameraDistanceMin", &o))
2044  cameraDistanceMin = json_object_get_double(o);
2045 
2046  float cameraDistanceMax;
2047  if (json_object_object_get_ex(js, "cameraDistanceMax", &o))
2048  cameraDistanceMax = json_object_get_double(o);
2049 
2050  float cameraConfocalDistance;
2051  if (json_object_object_get_ex(js, "cameraConfocalDistance", &o))
2052  cameraConfocalDistance = json_object_get_double(o);
2053 
2054  float cameraIntraocularDistance;
2055  if (json_object_object_get_ex(js, "cameraIntraocularDistance", &o))
2056  cameraIntraocularDistance = json_object_get_double(o);
2057 
2058  float cameraVignetteWidth;
2059  if (json_object_object_get_ex(js, "cameraVignetteWidth", &o))
2060  cameraVignetteWidth = json_object_get_double(o);
2061 
2062  float cameraVignetteSharpness;
2063  if (json_object_object_get_ex(js, "cameraVignetteSharpness", &o))
2064  cameraVignetteSharpness = json_object_get_double(o);
2065 
2066  VuoColor lightColor;
2067  if (json_object_object_get_ex(js, "lightColor", &o))
2068  lightColor = VuoColor_makeFromJson(o);
2069 
2070  float lightBrightness;
2071  if (json_object_object_get_ex(js, "lightBrightness", &o))
2072  lightBrightness = json_object_get_double(o);
2073 
2074  float lightCone;
2075  if (json_object_object_get_ex(js, "lightCone", &o))
2076  lightCone = json_object_get_double(o);
2077 
2078  float lightRange;
2079  if (json_object_object_get_ex(js, "lightRange", &o))
2080  lightRange = json_object_get_double(o);
2081 
2082  float lightSharpness;
2083  if (json_object_object_get_ex(js, "lightSharpness", &o))
2084  lightSharpness = json_object_get_double(o);
2085 
2086  VuoText name = NULL;
2087  if (json_object_object_get_ex(js, "name", &o))
2088  name = VuoText_makeFromJson(o);
2089 
2090  json_object_object_get_ex(js, "transform", &o);
2091  VuoTransform transform = VuoTransform_makeFromJson(o);
2092 
2093  VuoText text = NULL;
2094  if (json_object_object_get_ex(js, "text", &o))
2095  text = VuoText_makeFromJson(o);
2096 
2097  VuoFont font;
2098  if (json_object_object_get_ex(js, "textFont", &o))
2099  font = VuoFont_makeFromJson(o);
2100 
2101  bool scaleWithScene = false;
2102  if (json_object_object_get_ex(js, "textScaleWithScene", &o))
2103  scaleWithScene = VuoBoolean_makeFromJson(o);
2104 
2105  float wrapWidth = INFINITY;
2106  if (json_object_object_get_ex(js, "textWrapWidth", &o))
2107  wrapWidth = json_object_get_double(o);
2108 
2109  VuoSceneObject obj;
2110  switch (type)
2111  {
2112  case VuoSceneObjectSubType_Empty:
2113  obj = nullptr;
2114  break;
2115  case VuoSceneObjectSubType_Group:
2116  obj = VuoSceneObject_makeGroup(childObjects, transform);
2117  break;
2118  case VuoSceneObjectSubType_Mesh:
2119  {
2120  obj = VuoSceneObject_makeMesh(mesh, shader, transform);
2121  VuoSceneObject_internal *so = (VuoSceneObject_internal *)obj;
2122  so->isRealSize = isRealSize;
2123  so->preservePhysicalSize = preservePhysicalSize;
2124  so->blendMode = blendMode;
2125  VuoSceneObject_setName(obj, name);
2126  break;
2127  }
2128  case VuoSceneObjectSubType_PerspectiveCamera:
2130  name,
2131  transform,
2132  cameraFieldOfView,
2133  cameraDistanceMin,
2134  cameraDistanceMax
2135  );
2136  break;
2137  case VuoSceneObjectSubType_StereoCamera:
2139  name,
2140  transform,
2141  cameraFieldOfView,
2142  cameraDistanceMin,
2143  cameraDistanceMax,
2144  cameraConfocalDistance,
2145  cameraIntraocularDistance
2146  );
2147  break;
2148  case VuoSceneObjectSubType_OrthographicCamera:
2150  name,
2151  transform,
2152  cameraWidth,
2153  cameraDistanceMin,
2154  cameraDistanceMax
2155  );
2156  break;
2157  case VuoSceneObjectSubType_FisheyeCamera:
2159  name,
2160  transform,
2161  cameraFieldOfView,
2162  cameraVignetteWidth,
2163  cameraVignetteSharpness
2164  );
2165  break;
2166  case VuoSceneObjectSubType_AmbientLight:
2167  obj = VuoSceneObject_makeAmbientLight(lightColor, lightBrightness);
2168  break;
2169  case VuoSceneObjectSubType_PointLight:
2170  obj = VuoSceneObject_makePointLight(lightColor, lightBrightness, transform.translation, lightRange, lightSharpness);
2171  break;
2172  case VuoSceneObjectSubType_Spotlight:
2173  obj = VuoSceneObject_makeSpotlight(lightColor, lightBrightness, transform, lightCone, lightRange, lightSharpness);
2174  break;
2175  case VuoSceneObjectSubType_Text:
2176  obj = VuoSceneObject_makeText(text, font, scaleWithScene, wrapWidth);
2177  VuoSceneObject_setTransform(obj, transform);
2178  VuoSceneObject_setMesh(obj, mesh);
2179  break;
2180  }
2181 
2182  VuoSceneObject_setId(obj, id);
2183 
2184  return obj;
2185 }
2186 
2191 {
2192  if (!sceneObject)
2193  return nullptr;
2194 
2195  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2196 
2197  json_object *js = json_object_new_object();
2198 
2199  json_object_object_add(js, "id", json_object_new_int64(so->id));
2200  json_object_object_add(js, "type", json_object_new_string(VuoSceneObject_cStringForType(so->type)));
2201 
2202  switch (so->type)
2203  {
2204  case VuoSceneObjectSubType_Empty:
2205  break;
2206 
2207  case VuoSceneObjectSubType_Mesh:
2208  if (so->mesh)
2209  json_object_object_add(js, "mesh", VuoMesh_getJson(so->mesh));
2210 
2211  if (so->shader)
2212  json_object_object_add(js, "shader", VuoShader_getJson(so->shader));
2213 
2214  json_object_object_add(js, "isRealSize", VuoBoolean_getJson(so->isRealSize));
2215 
2216  json_object_object_add(js, "preservePhysicalSize", VuoBoolean_getJson(so->preservePhysicalSize));
2217 
2218  if (so->blendMode != VuoBlendMode_Normal)
2219  json_object_object_add(js, "blendMode", VuoBlendMode_getJson(so->blendMode));
2220  break;
2221 
2222  case VuoSceneObjectSubType_Group:
2223  if (so->childObjects)
2224  json_object_object_add(js, "childObjects", VuoList_VuoSceneObject_getJson(so->childObjects));
2225  break;
2226 
2227  case VuoSceneObjectSubType_PerspectiveCamera:
2228  case VuoSceneObjectSubType_StereoCamera:
2229  case VuoSceneObjectSubType_OrthographicCamera:
2230  case VuoSceneObjectSubType_FisheyeCamera:
2231  {
2232  if (so->type != VuoSceneObjectSubType_FisheyeCamera)
2233  {
2234  json_object_object_add(js, "cameraDistanceMin", json_object_new_double(so->camera.distanceMin));
2235  json_object_object_add(js, "cameraDistanceMax", json_object_new_double(so->camera.distanceMax));
2236  }
2237 
2238  if (so->type == VuoSceneObjectSubType_PerspectiveCamera
2239  || so->type == VuoSceneObjectSubType_StereoCamera
2240  || so->type == VuoSceneObjectSubType_FisheyeCamera)
2241  json_object_object_add(js, "cameraFieldOfView", json_object_new_double(so->camera.fieldOfView));
2242 
2243  if (so->type == VuoSceneObjectSubType_StereoCamera)
2244  {
2245  json_object_object_add(js, "cameraConfocalDistance", json_object_new_double(so->camera.confocalDistance));
2246  json_object_object_add(js, "cameraIntraocularDistance", json_object_new_double(so->camera.intraocularDistance));
2247  }
2248 
2249  if (so->type == VuoSceneObjectSubType_OrthographicCamera)
2250  json_object_object_add(js, "cameraWidth", json_object_new_double(so->camera.width));
2251 
2252  if (so->type == VuoSceneObjectSubType_FisheyeCamera)
2253  {
2254  json_object_object_add(js, "cameraVignetteWidth", json_object_new_double(so->camera.vignetteWidth));
2255  json_object_object_add(js, "cameraVignetteSharpness", json_object_new_double(so->camera.vignetteSharpness));
2256  }
2257 
2258  break;
2259  }
2260 
2261  case VuoSceneObjectSubType_AmbientLight:
2262  case VuoSceneObjectSubType_PointLight:
2263  case VuoSceneObjectSubType_Spotlight:
2264  {
2265  json_object_object_add(js, "lightColor", VuoColor_getJson(so->light.color));
2266  json_object_object_add(js, "lightBrightness", json_object_new_double(so->light.brightness));
2267 
2268  if (so->type == VuoSceneObjectSubType_PointLight
2269  || so->type == VuoSceneObjectSubType_Spotlight)
2270  {
2271  json_object_object_add(js, "lightRange", json_object_new_double(so->light.range));
2272  json_object_object_add(js, "lightSharpness", json_object_new_double(so->light.sharpness));
2273  }
2274  if (so->type == VuoSceneObjectSubType_Spotlight)
2275  json_object_object_add(js, "lightCone", json_object_new_double(so->light.cone));
2276 
2277  break;
2278  }
2279 
2280  case VuoSceneObjectSubType_Text:
2281  {
2282  if (so->text.text)
2283  json_object_object_add(js, "text", VuoText_getJson(so->text.text));
2284 
2285  json_object_object_add(js, "textFont", VuoFont_getJson(so->text.font));
2286 
2287  if (so->mesh)
2288  json_object_object_add(js, "mesh", VuoMesh_getJson(so->mesh));
2289 
2290  json_object_object_add(js, "textScaleWithScene", VuoBoolean_getJson(so->text.scaleWithScene));
2291  json_object_object_add(js, "textWrapWidth", VuoReal_getJson(so->text.wrapWidth));
2292 
2293  break;
2294  }
2295  }
2296 
2297  if (so->name)
2298  json_object_object_add(js, "name", VuoText_getJson(so->name));
2299 
2300  if (so->type != VuoSceneObjectSubType_AmbientLight)
2301  json_object_object_add(js, "transform", VuoTransform_getJson(so->transform));
2302 
2303  return js;
2304 }
2305 
2309 unsigned long VuoSceneObject_getVertexCount(const VuoSceneObject sceneObject)
2310 {
2311  if (!sceneObject)
2312  return 0;
2313 
2314  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2315  if (!so->mesh)
2316  return 0;
2317 
2318  unsigned int vertexCount;
2319  VuoMesh_getCPUBuffers(so->mesh, &vertexCount, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
2320  return vertexCount;
2321 }
2322 
2326 unsigned long VuoSceneObject_getElementCount(const VuoSceneObject sceneObject)
2327 {
2328  if (!sceneObject)
2329  return 0;
2330 
2331  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2332  if (!so->mesh)
2333  return 0;
2334 
2335  unsigned int elementCount;
2336  VuoMesh_getCPUBuffers(so->mesh, nullptr, nullptr, nullptr, nullptr, nullptr, &elementCount, nullptr);
2337  return elementCount;
2338 }
2339 
2345 void VuoSceneObject_getStatistics(const VuoSceneObject sceneObject, unsigned long *descendantCount, unsigned long *totalVertexCount, unsigned long *totalElementCount)
2346 {
2347  if (!sceneObject)
2348  return;
2349 
2350  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2351  unsigned long childObjectCount = 0;
2352  if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2353  childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2354  *descendantCount += childObjectCount;
2355  *totalVertexCount += VuoSceneObject_getVertexCount(sceneObject);
2356  *totalElementCount += VuoSceneObject_getElementCount(sceneObject);
2357 
2358  if (so->type == VuoSceneObjectSubType_Group)
2359  for (unsigned long i = 1; i <= childObjectCount; ++i)
2360  VuoSceneObject_getStatistics(VuoListGetValue_VuoSceneObject(so->childObjects, i), descendantCount, totalVertexCount, totalElementCount);
2361 }
2362 
2367 {
2368  if (!sceneObject)
2369  return nullptr;
2370 
2371  // Exploit json_object's set-containing-only-unique-items data structure.
2372  __block json_object *names = json_object_new_object();
2373  VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2374  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2375  if (co->shader)
2376  json_object_object_add(names, co->shader->name, NULL);
2377  return true;
2378  });
2379 
2381  json_object_object_foreach(names, key, val)
2382  VuoListAppendValue_VuoText(nameList, VuoText_make(key));
2383  json_object_put(names);
2384  return nameList;
2385 }
2386 
2391 {
2392  if (!sceneObject)
2393  return strdup("no object");
2394 
2395  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2396 
2397  if (so->type == VuoSceneObjectSubType_Text)
2398  {
2399  char *fontSummary = VuoFont_getSummary(so->text.font);
2400  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);
2401  free(fontSummary);
2402  return textSummary;
2403  }
2404 
2405  if (so->type == VuoSceneObjectSubType_PerspectiveCamera
2406  || so->type == VuoSceneObjectSubType_StereoCamera
2407  || so->type == VuoSceneObjectSubType_OrthographicCamera
2408  || so->type == VuoSceneObjectSubType_FisheyeCamera)
2409  {
2410  const char *type = VuoSceneObject_cStringForType(so->type);
2411 
2412  float cameraViewValue = 0;
2413  const char *cameraViewString = "";
2414  if (so->type == VuoSceneObjectSubType_PerspectiveCamera)
2415  {
2416  cameraViewValue = so->camera.fieldOfView;
2417  cameraViewString = "° field of view";
2418  }
2419  else if (so->type == VuoSceneObjectSubType_StereoCamera)
2420  {
2421  cameraViewValue = so->camera.fieldOfView;
2422  cameraViewString = "° field of view (stereoscopic)";
2423  }
2424  else if (so->type == VuoSceneObjectSubType_OrthographicCamera)
2425  {
2426  cameraViewValue = so->camera.width;
2427  cameraViewString = " unit width";
2428  }
2429  else if (so->type == VuoSceneObjectSubType_FisheyeCamera)
2430  {
2431  cameraViewValue = so->camera.fieldOfView;
2432  cameraViewString = "° field of view (fisheye)";
2433  }
2434 
2435  char *translationString = VuoPoint3d_getSummary(so->transform.translation);
2436 
2437  const char *rotationLabel;
2438  char *rotationString;
2439  if (so->transform.type == VuoTransformTypeEuler)
2440  {
2441  rotationLabel = "rotated";
2442  rotationString = VuoPoint3d_getSummary(VuoPoint3d_multiply(so->transform.rotationSource.euler, -180.f/M_PI));
2443  }
2444  else
2445  {
2446  rotationLabel = "target";
2447  rotationString = VuoPoint3d_getSummary(so->transform.rotationSource.target);
2448  }
2449 
2450  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>",
2451  type, so->name ? so->name : "",
2452  translationString,
2453  rotationLabel, rotationString,
2454  cameraViewValue, cameraViewString,
2455  so->camera.distanceMin, so->camera.distanceMax);
2456  free(rotationString);
2457  free(translationString);
2458  return valueAsString;
2459  }
2460 
2461  if (so->type == VuoSceneObjectSubType_AmbientLight
2462  || so->type == VuoSceneObjectSubType_PointLight
2463  || so->type == VuoSceneObjectSubType_Spotlight)
2464  {
2465  const char *type = VuoSceneObject_cStringForType(so->type);
2466  char *colorString = VuoColor_getShortSummary(so->light.color);
2467 
2468  char *positionRangeString;
2469  if (so->type == VuoSceneObjectSubType_PointLight
2470  || so->type == VuoSceneObjectSubType_Spotlight)
2471  {
2472  char *positionString = VuoPoint3d_getSummary(so->transform.translation);
2473 
2474  positionRangeString = VuoText_format("<div>position (%s)</div><div>range %g units (%g sharpness)</div>",
2475  positionString, so->light.range, so->light.sharpness);
2476 
2477  free(positionString);
2478  }
2479  else
2480  positionRangeString = strdup("");
2481 
2482  char *directionConeString;
2483  if (so->type == VuoSceneObjectSubType_Spotlight)
2484  {
2485  VuoPoint3d direction = VuoTransform_getDirection(so->transform);
2486  char *directionString = VuoPoint3d_getSummary(direction);
2487 
2488  directionConeString = VuoText_format("<div>direction (%s)</div><div>cone %g°</div>",
2489  directionString, so->light.cone * 180./M_PI);
2490 
2491  free(directionString);
2492  }
2493  else
2494  directionConeString = strdup("");
2495 
2496  char *valueAsString = VuoText_format("<div>%s</div><div>color %s</div><div>brightness %g</div>%s%s",
2497  type, colorString, so->light.brightness, positionRangeString, directionConeString);
2498 
2499  free(directionConeString);
2500  free(positionRangeString);
2501  free(colorString);
2502 
2503  return valueAsString;
2504  }
2505 
2506  unsigned long vertexCount = VuoSceneObject_getVertexCount(sceneObject);
2507  unsigned long elementCount = VuoSceneObject_getElementCount(sceneObject);
2508 
2509  char *transform = VuoTransform_getSummary(so->transform);
2510 
2511  unsigned long childObjectCount = 0;
2512  if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2513  childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2514  const char *childObjectPlural = childObjectCount == 1 ? "" : "s";
2515 
2516  char *descendants;
2517  if (childObjectCount)
2518  {
2519  unsigned long descendantCount = 0;
2520  unsigned long totalVertexCount = 0;
2521  unsigned long totalElementCount = 0;
2522  VuoSceneObject_getStatistics(sceneObject, &descendantCount, &totalVertexCount, &totalElementCount);
2523  const char *descendantPlural = descendantCount == 1 ? "" : "s";
2524 
2525  descendants = VuoText_format("<div>%ld descendant%s</div><div>total, including descendants:</div><div>%ld vertices, %ld elements</div>",
2526  descendantCount, descendantPlural, totalVertexCount, totalElementCount);
2527  }
2528  else
2529  descendants = strdup("");
2530 
2531  VuoList_VuoText shaderNames = VuoSceneObject_findShaderNames(sceneObject);
2532  VuoRetain(shaderNames);
2533  char *shaderNamesSummary;
2534  if (VuoListGetCount_VuoText(shaderNames))
2535  {
2536  VuoInteger shaderNameCount = VuoListGetCount_VuoText(shaderNames);
2537  const char *header = "<div>shaders:<ul>";
2538  VuoInteger shaderNameLength = strlen(header);
2539  for (VuoInteger i = 1; i <= shaderNameCount; ++i)
2540  shaderNameLength += strlen("<li>") + strlen(VuoListGetValue_VuoText(shaderNames, i)) + strlen("</li>");
2541  shaderNameLength += strlen("</ul></div>");
2542 
2543  shaderNamesSummary = (char *)malloc(shaderNameLength + 1);
2544  char *t = shaderNamesSummary;
2545  t = strcpy(t, header) + strlen(header);
2546  for (VuoInteger i = 1; i <= shaderNameCount; ++i)
2547  {
2548  t = strcpy(t, "<li>") + strlen("<li>");
2549  t = strcpy(t, VuoListGetValue_VuoText(shaderNames, i)) + strlen(VuoListGetValue_VuoText(shaderNames, i));
2550  t = strcpy(t, "</li>") + strlen("</li>");
2551  }
2552  t = strcpy(t, "</ul></div>");
2553  }
2554  else
2555  shaderNamesSummary = strdup("");
2556  VuoRelease(shaderNames);
2557 
2558  char *valueAsString = VuoText_format("<div>object named \"%s\"</div><div>%ld vertices, %ld elements</div><div>%s</div><div>id %lld</div><div>%ld child object%s</div>%s%s",
2559  so->name ? so->name : "",
2560  vertexCount, elementCount,
2561  transform,
2562  so->id,
2563  childObjectCount, childObjectPlural,
2564  descendants, shaderNamesSummary);
2565 
2566  free(descendants);
2567  free(transform);
2568  free(shaderNamesSummary);
2569 
2570  return valueAsString;
2571 }
2572 
2576 static void VuoSceneObject_dump_internal(const VuoSceneObject sceneObject, unsigned int level)
2577 {
2578  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2579 
2580  for (unsigned int i=0; i<level; ++i)
2581  fprintf(stderr, "\t");
2582 
2583  if (!sceneObject)
2584  {
2585  fprintf(stderr, "no object\n");
2586  return;
2587  }
2588 
2589  fprintf(stderr, "%s (%s) ", VuoSceneObject_cStringForType(so->type), VuoTransform_getSummary(so->transform));
2590  if (so->type == VuoSceneObjectSubType_Mesh)
2591  fprintf(stderr, "%lu vertices, %lu elements, shader '%s' (%p)", VuoSceneObject_getVertexCount(sceneObject), VuoSceneObject_getElementCount(sceneObject), so->shader ? so->shader->name : "", so->shader);
2592  fprintf(stderr, "\n");
2593 
2594  if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2595  {
2596  unsigned int childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2597  for (unsigned int i=1; i<=childObjectCount; ++i)
2598  VuoSceneObject_dump_internal(VuoListGetValue_VuoSceneObject(so->childObjects, i), level+1);
2599  }
2600 }
2601 
2606 {
2608 }
2609 
2621 {
2622  if (!so)
2623  return nullptr;
2624 
2625  // Count the vertices.
2626 
2627  __block unsigned long triangleVertexCount = 0;
2628  __block unsigned long trianglePrimitiveCount = 0;
2629  __block VuoMesh_FaceCulling triangleFaceCulling = VuoMesh_CullBackfaces;
2630 
2631  __block unsigned long lineVertexCount = 0;
2632  __block unsigned long linePrimitiveCount = 0;
2633  __block double linePrimitiveSize = 0;
2634 
2635  __block unsigned long pointVertexCount = 0;
2636  __block unsigned long pointPrimitiveCount = 0;
2637  __block double pointPrimitiveSize = 0;
2638 
2639  VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2640  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2641  if (!co->mesh)
2642  return true;
2643 
2644  VuoMesh_ElementAssemblyMethod elementAssemblyMethod = VuoMesh_getElementAssemblyMethod(co->mesh);
2645 
2646  unsigned int vertexCount;
2647  VuoMesh_getCPUBuffers(co->mesh, &vertexCount, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
2648 
2649  if (elementAssemblyMethod == VuoMesh_IndividualTriangles
2650  || elementAssemblyMethod == VuoMesh_TriangleStrip
2651  || elementAssemblyMethod == VuoMesh_TriangleFan)
2652  {
2653  triangleVertexCount += vertexCount;
2654  trianglePrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2655  triangleFaceCulling = VuoMesh_getFaceCulling(co->mesh);
2656  }
2657  else if (elementAssemblyMethod == VuoMesh_IndividualLines
2658  || elementAssemblyMethod == VuoMesh_LineStrip)
2659  {
2660  lineVertexCount += vertexCount;
2661  linePrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2662  linePrimitiveSize = VuoMesh_getPrimitiveSize(co->mesh);
2663  }
2664  else if (elementAssemblyMethod == VuoMesh_Points)
2665  {
2666  pointVertexCount += vertexCount;
2667  pointPrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2668  pointPrimitiveSize = VuoMesh_getPrimitiveSize(co->mesh);
2669  }
2670 
2671  return true;
2672  });
2673 // VLog("triangles: %ldv %ldp lines: %ldv %ldp points: %ldv %ldp", triangleVertexCount, trianglePrimitiveCount, lineVertexCount, linePrimitiveCount, pointVertexCount, pointPrimitiveCount);
2674 
2675  if (!trianglePrimitiveCount && !linePrimitiveCount && !pointPrimitiveCount)
2676  return nullptr;
2677 
2678  // Allocate the buffers.
2679  unsigned int *triangleElements = nullptr;
2680  float *trianglePositions = nullptr, *triangleNormals = nullptr, *triangleTextureCoordinates = nullptr;
2681  unsigned int *lineElements = nullptr;
2682  float *linePositions = nullptr, *lineNormals = nullptr, *lineTextureCoordinates = nullptr;
2683  unsigned int *pointElements = nullptr;
2684  float *pointPositions = nullptr, *pointNormals = nullptr, *pointTextureCoordinates = nullptr;
2685  if (trianglePrimitiveCount)
2686  VuoMesh_allocateCPUBuffers(triangleVertexCount, &trianglePositions, &triangleNormals, &triangleTextureCoordinates, nullptr, trianglePrimitiveCount * 3, &triangleElements);
2687  if (linePrimitiveCount)
2688  VuoMesh_allocateCPUBuffers(lineVertexCount, &linePositions, &lineNormals, &lineTextureCoordinates, nullptr, linePrimitiveCount * 2, &lineElements);
2689  if (pointPrimitiveCount)
2690  VuoMesh_allocateCPUBuffers(pointVertexCount, &pointPositions, &pointNormals, &pointTextureCoordinates, nullptr, pointPrimitiveCount, &pointElements);
2691 
2692  // Copy the vertex attributes.
2693  __block unsigned long triangleVertexIndex = 0;
2694  __block unsigned long triangleElementIndex = 0;
2695  __block unsigned long lineVertexIndex = 0;
2696  __block unsigned long lineElementIndex = 0;
2697  __block unsigned long pointVertexIndex = 0;
2698  __block unsigned long pointElementIndex = 0;
2699  __block bool anyTextureCoordinates = false;
2700  VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2701  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2702  if (!co->mesh)
2703  return true;
2704 
2705  VuoMesh_ElementAssemblyMethod elementAssemblyMethod = VuoMesh_getElementAssemblyMethod(co->mesh);
2706 
2707  unsigned int vertexCount, elementCount, *elements;
2708  float *positions, *normals, *textureCoordinates;
2709  VuoMesh_getCPUBuffers(co->mesh, &vertexCount, &positions, &normals, &textureCoordinates, nullptr, &elementCount, &elements);
2710 
2711  if (textureCoordinates)
2712  anyTextureCoordinates = true;
2713 
2714  if (elementAssemblyMethod == VuoMesh_IndividualTriangles
2715  || elementAssemblyMethod == VuoMesh_TriangleStrip
2716  || elementAssemblyMethod == VuoMesh_TriangleFan)
2717  {
2718  unsigned long indexOffset = triangleVertexIndex;
2719  for (unsigned int n = 0; n < vertexCount; ++n)
2720  {
2721  VuoPoint3d p = VuoPoint3d_makeFromArray(&positions[n * 3]);
2722  VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2723  VuoPoint3d_setArray(&trianglePositions[triangleVertexIndex * 3], pt);
2724 
2725  if (normals)
2726  {
2727  VuoPoint3d r = VuoPoint3d_makeFromArray(&normals[n * 3]);
2728  VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2729  VuoPoint3d_setArray(&triangleNormals[triangleVertexIndex * 3], rt);
2730  }
2731 
2732  if (textureCoordinates)
2733  VuoPoint2d_setArray(&triangleTextureCoordinates[triangleVertexIndex * 2], VuoPoint2d_makeFromArray(&textureCoordinates[n * 2]));
2734 
2735  ++triangleVertexIndex;
2736  }
2737 
2738  if (elementAssemblyMethod == VuoMesh_IndividualTriangles)
2739  {
2740  if (elementCount)
2741  for (unsigned int n = 0; n < elementCount; ++n)
2742  triangleElements[triangleElementIndex++] = indexOffset + elements[n];
2743  else
2744  for (unsigned int n = 0; n < vertexCount; ++n)
2745  triangleElements[triangleElementIndex++] = indexOffset + n;
2746  }
2747  else if (elementAssemblyMethod == VuoMesh_TriangleStrip)
2748  {
2749  // Expand the triangle strip to individual triangles.
2750  if (elementCount)
2751  for (unsigned int n = 2; n < elementCount; ++n)
2752  if (n%2 == 0)
2753  {
2754  triangleElements[triangleElementIndex++] = indexOffset + elements[n-2];
2755  triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2756  triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2757  }
2758  else
2759  {
2760  triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2761  triangleElements[triangleElementIndex++] = indexOffset + elements[n-2];
2762  triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2763  }
2764  else
2765  for (unsigned int n = 0; n < vertexCount; ++n)
2766  if (n%2 == 0)
2767  {
2768  triangleElements[triangleElementIndex++] = indexOffset + n-2;
2769  triangleElements[triangleElementIndex++] = indexOffset + n-1;
2770  triangleElements[triangleElementIndex++] = indexOffset + n ;
2771  }
2772  else
2773  {
2774  triangleElements[triangleElementIndex++] = indexOffset + n-1;
2775  triangleElements[triangleElementIndex++] = indexOffset + n-2;
2776  triangleElements[triangleElementIndex++] = indexOffset + n ;
2777  }
2778  }
2779  else if (elementAssemblyMethod == VuoMesh_TriangleFan)
2780  {
2781  // Expand the triangle fan to individual triangles.
2782  if (elementCount)
2783  for (unsigned int n = 2; n < elementCount; ++n)
2784  {
2785  triangleElements[triangleElementIndex++] = indexOffset + elements[0 ];
2786  triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2787  triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2788  }
2789  else
2790  for (unsigned int n = 2; n < vertexCount; ++n)
2791  {
2792  triangleElements[triangleElementIndex++] = indexOffset + 0;
2793  triangleElements[triangleElementIndex++] = indexOffset + n-1;
2794  triangleElements[triangleElementIndex++] = indexOffset + n;
2795  }
2796  }
2797  }
2798  else if (elementAssemblyMethod == VuoMesh_IndividualLines
2799  || elementAssemblyMethod == VuoMesh_LineStrip)
2800  {
2801  unsigned long indexOffset = lineVertexIndex;
2802  for (unsigned int n = 0; n < vertexCount; ++n)
2803  {
2804  VuoPoint3d p = VuoPoint3d_makeFromArray(&positions[n * 3]);
2805  VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2806  VuoPoint3d_setArray(&linePositions[lineVertexIndex * 3], pt);
2807 
2808  if (normals)
2809  {
2810  VuoPoint3d r = VuoPoint3d_makeFromArray(&normals[n * 3]);
2811  VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2812  VuoPoint3d_setArray(&lineNormals[lineVertexIndex * 3], rt);
2813  }
2814 
2815  if (textureCoordinates)
2816  VuoPoint2d_setArray(&lineTextureCoordinates[lineVertexIndex], VuoPoint2d_makeFromArray(&textureCoordinates[n * 2]));
2817 
2818  ++lineVertexIndex;
2819  }
2820 
2821  if (elementAssemblyMethod == VuoMesh_IndividualLines)
2822  {
2823  if (elementCount)
2824  for (unsigned int n = 0; n < elementCount; ++n)
2825  lineElements[lineElementIndex++] = indexOffset + elements[n];
2826  else
2827  for (unsigned int n = 0; n < vertexCount; ++n)
2828  lineElements[lineElementIndex++] = indexOffset + n;
2829  }
2830  else if (elementAssemblyMethod == VuoMesh_LineStrip)
2831  {
2832  // Expand the line strip to individual lines.
2833  if (elementCount)
2834  for (unsigned int n = 1; n < elementCount; ++n)
2835  {
2836  lineElements[lineElementIndex++] = indexOffset + elements[n-1];
2837  lineElements[lineElementIndex++] = indexOffset + elements[n ];
2838  }
2839  else
2840  for (unsigned int n = 1; n < vertexCount; ++n)
2841  {
2842  lineElements[lineElementIndex++] = indexOffset + n-1;
2843  lineElements[lineElementIndex++] = indexOffset + n;
2844  }
2845  }
2846  }
2847  else if (elementAssemblyMethod == VuoMesh_Points)
2848  {
2849  unsigned long indexOffset = pointVertexIndex;
2850  for (unsigned int n = 0; n < vertexCount; ++n)
2851  {
2852  VuoPoint3d p = VuoPoint3d_makeFromArray(&positions[n * 3]);
2853  VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2854  VuoPoint3d_setArray(&pointPositions[pointVertexIndex * 3], pt);
2855 
2856  if (normals)
2857  {
2858  VuoPoint3d r = VuoPoint3d_makeFromArray(&normals[n * 3]);
2859  VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2860  VuoPoint3d_setArray(&pointNormals[pointVertexIndex * 3], rt);
2861  }
2862 
2863  if (textureCoordinates)
2864  VuoPoint2d_setArray(&pointTextureCoordinates[pointVertexIndex * 2], VuoPoint2d_makeFromArray(&textureCoordinates[n * 2]));
2865 
2866  ++pointVertexIndex;
2867  }
2868 
2869  if (elementCount)
2871  for (unsigned int n = 0; n < elementCount; ++n)
2872  pointElements[pointElementIndex++] = indexOffset + elements[n];
2873  else
2874  {
2875  for (unsigned int n = 0; n < vertexCount; ++n)
2876  pointElements[pointElementIndex++] = indexOffset + n;
2877  }
2878  }
2879 
2880  return true;
2881  });
2882 
2883 
2884  VuoMesh triangleMesh = nullptr;
2885  VuoMesh lineMesh = nullptr;
2886  VuoMesh pointMesh = nullptr;
2887  if (trianglePrimitiveCount)
2888  {
2889  if (!anyTextureCoordinates)
2890  {
2891  free(triangleTextureCoordinates);
2892  triangleTextureCoordinates = nullptr;
2893  }
2894  triangleMesh = VuoMesh_makeFromCPUBuffers(triangleVertexCount, trianglePositions, triangleNormals, triangleTextureCoordinates, nullptr, trianglePrimitiveCount * 3, triangleElements, VuoMesh_IndividualTriangles);
2895  VuoMesh_setFaceCulling(triangleMesh, triangleFaceCulling);
2896  }
2897  if (linePrimitiveCount)
2898  {
2899  if (!anyTextureCoordinates)
2900  {
2901  free(lineTextureCoordinates);
2902  lineTextureCoordinates = nullptr;
2903  }
2904  lineMesh = VuoMesh_makeFromCPUBuffers(lineVertexCount, linePositions, lineNormals, lineTextureCoordinates, nullptr, linePrimitiveCount * 2, lineElements, VuoMesh_IndividualLines);
2905  VuoMesh_setPrimitiveSize(lineMesh, linePrimitiveSize);
2906  }
2907  if (pointPrimitiveCount)
2908  {
2909  if (!anyTextureCoordinates)
2910  {
2911  free(pointTextureCoordinates);
2912  pointTextureCoordinates = nullptr;
2913  }
2914  pointMesh = VuoMesh_makeFromCPUBuffers(pointVertexCount, pointPositions, pointNormals, pointTextureCoordinates, nullptr, pointPrimitiveCount, pointElements, VuoMesh_Points);
2915  VuoMesh_setPrimitiveSize(pointMesh, pointPrimitiveSize);
2916  }
2917 
2918  if (triangleMesh && !lineMesh && !pointMesh)
2919  return VuoSceneObject_makeMesh(triangleMesh, NULL, VuoTransform_makeIdentity());
2920  else if (!triangleMesh && lineMesh && !pointMesh)
2921  return VuoSceneObject_makeMesh(lineMesh, NULL, VuoTransform_makeIdentity());
2922  else if (!triangleMesh && !lineMesh && pointMesh)
2923  return VuoSceneObject_makeMesh(pointMesh, NULL, VuoTransform_makeIdentity());
2924  else
2925  {
2927  if (triangleMesh)
2929  if (lineMesh)
2931  if (pointMesh)
2933  return VuoSceneObject_makeGroup(childObjects, VuoTransform_makeIdentity());
2934  }
2935 
2936  return NULL;
2937 }
2938 
2939 #define CSGJS_HEADER_ONLY
2940 #include "csgjs.cc"
2941 
2945 static csgjs_model VuoSceneObject_getCsgjsModel(const VuoSceneObject so)
2946 {
2947  if (!so)
2948  return csgjs_model();
2949 
2951  VuoSceneObject_internal *f = (VuoSceneObject_internal *)flat;
2952  if (!f->mesh)
2953  return csgjs_model();
2954 
2955  VuoLocal(flat);
2956 
2958  return csgjs_model();
2959 
2960  unsigned int vertexCount, elementCount, *elements;
2961  float *positions, *normals, *textureCoordinates;
2962  VuoMesh_getCPUBuffers(f->mesh, &vertexCount, &positions, &normals, &textureCoordinates, nullptr, &elementCount, &elements);
2963 
2964  csgjs_model cm;
2965  for (unsigned int n = 0; n < vertexCount; ++n)
2966  {
2967  csgjs_vertex v;
2968  v.pos = csgjs_vector(positions[n * 3], positions[n * 3 + 1], positions[n * 3 + 2]);
2969  if (normals)
2970  v.normal = csgjs_vector(normals[n * 3], normals[n * 3 + 1], normals[n * 3 + 2]);
2971  if (textureCoordinates)
2972  v.uv = csgjs_vector(textureCoordinates[n * 3], textureCoordinates[n * 3 + 1], 0);
2973  cm.vertices.push_back(v);
2974  }
2975  for (unsigned int n = 0; n < elementCount; ++n)
2976  cm.indices.push_back(elements[n]);
2977 
2978  return cm;
2979 }
2980 
2985 {
2986  unsigned int vertexCount = cm.vertices.size();
2987  unsigned int elementCount = cm.indices.size();
2988  unsigned int *elements;
2989  float *positions, *normals, *textureCoordinates;
2990  VuoMesh_allocateCPUBuffers(vertexCount, &positions, &normals, &textureCoordinates, nullptr, elementCount, &elements);
2991 
2992  const csgjs_vertex *vertex = &cm.vertices[0];
2993  for (unsigned int n = 0; n < vertexCount; ++n)
2994  {
2995  positions[n * 3 ] = vertex[n].pos.x;
2996  positions[n * 3 + 1] = vertex[n].pos.y;
2997  positions[n * 3 + 2] = vertex[n].pos.z;
2998  normals[n * 3 ] = vertex[n].normal.x;
2999  normals[n * 3 + 1] = vertex[n].normal.y;
3000  normals[n * 3 + 2] = vertex[n].normal.z;
3001  textureCoordinates[n * 2 ] = vertex[n].uv.x;
3002  textureCoordinates[n * 2 + 1] = vertex[n].uv.y;
3003  }
3004 
3005  const int *index = &cm.indices[0];
3006  for (unsigned int n = 0; n < elementCount; ++n)
3007  elements[n] = index[n];
3008 
3009  VuoMesh mesh = VuoMesh_makeFromCPUBuffers(vertexCount, positions, normals, textureCoordinates, nullptr, elementCount, elements, VuoMesh_IndividualTriangles);
3010 
3011  return VuoSceneObject_makeMesh(mesh, NULL, VuoTransform_makeIdentity());
3012 }
3013 
3018 static float convertQualityToEpsilon(float quality)
3019 {
3020  return pow(10, -VuoReal_clamp(quality, 0, 1) * 5.);
3021 }
3022 
3027 {
3028  float epsilon = convertQualityToEpsilon(quality);
3029 
3030  unsigned long objectCount = VuoListGetCount_VuoSceneObject(objects);
3031  if (objectCount == 0)
3032  return nullptr;
3033  if (objectCount == 1)
3034  return VuoListGetValue_VuoSceneObject(objects, 1);
3035 
3036  dispatch_queue_t queue = dispatch_queue_create("org.vuo.sceneobject.union", DISPATCH_QUEUE_CONCURRENT);
3037 
3038  csgjs_model *models = new csgjs_model[objectCount];
3039  for (unsigned long i = 0; i < objectCount; ++i)
3040  dispatch_async(queue, ^{
3042  });
3043 
3044  dispatch_barrier_sync(queue, ^{});
3045  dispatch_release(queue);
3046 
3047  csgjs_model cu = models[0];
3048  for (unsigned long i = 1; i < objectCount; ++i)
3049  cu = csgjs_union(cu, models[i], epsilon);
3050  delete[] models;
3052 }
3053 
3058 {
3059  float epsilon = convertQualityToEpsilon(quality);
3060 
3061  dispatch_semaphore_t finished = dispatch_semaphore_create(0);
3062 
3063  __block csgjs_model ca;
3064  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3066  dispatch_semaphore_signal(finished);
3067  });
3068 
3069  csgjs_model cb = VuoSceneObject_getCsgjsModel(b);
3070 
3071  dispatch_semaphore_wait(finished, DISPATCH_TIME_FOREVER);
3072  dispatch_release(finished);
3073 
3074  csgjs_model d = csgjs_difference(ca, cb, epsilon);
3076 }
3077 
3082 {
3083  float epsilon = convertQualityToEpsilon(quality);
3084 
3085  unsigned long objectCount = VuoListGetCount_VuoSceneObject(objects);
3086  if (objectCount == 0)
3087  return nullptr;
3088  if (objectCount == 1)
3089  return VuoListGetValue_VuoSceneObject(objects, 1);
3090 
3091  dispatch_queue_t queue = dispatch_queue_create("org.vuo.sceneobject.intersect", DISPATCH_QUEUE_CONCURRENT);
3092 
3093  csgjs_model *models = new csgjs_model[objectCount];
3094  for (unsigned long i = 0; i < objectCount; ++i)
3095  dispatch_async(queue, ^{
3097  });
3098 
3099  dispatch_barrier_sync(queue, ^{});
3100  dispatch_release(queue);
3101 
3102  csgjs_model ci = models[0];
3103  for (unsigned long i = 1; i < objectCount; ++i)
3104  ci = csgjs_intersection(ci, models[i], epsilon);
3105  delete[] models;
3107 }