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  "VuoFont",
33  "VuoMesh",
34  "VuoMeshUtility",
35  "VuoPoint3d",
36  "VuoShader",
37  "VuoText",
38  "VuoTransform",
39  "VuoList_VuoImage",
40  "VuoList_VuoSceneObject"
41  ]
42  });
43 }
44 #endif
45 
54 typedef struct
55 {
57 
58  // Data for all scene objects
59  uint64_t id;
60  VuoText name;
61  VuoTransform transform;
62 
63  // Mesh
64  VuoMesh mesh;
65  VuoShader shader;
66  bool isRealSize;
67  bool preservePhysicalSize;
68  VuoBlendMode blendMode;
69 
70  union
71  {
72  VuoList_VuoSceneObject childObjects;
73 
74  struct
75  {
76  float fieldOfView;
77  float width;
78  float distanceMin;
79  float distanceMax;
80  float confocalDistance;
81  float intraocularDistance;
82  float vignetteWidth;
83  float vignetteSharpness;
84  } camera;
85 
86  struct
87  {
88  VuoColor color;
89  float brightness;
90  float range;
91  float cone;
92  float sharpness;
93  } light;
94 
95  struct
96  {
97  VuoText text;
98  VuoFont font;
99  bool scaleWithScene;
100  float wrapWidth;
101  } text;
102  };
103 } VuoSceneObject_internal;
104 
110 {
111  static uint64_t id = 0;
112  return __sync_add_and_fetch(&id, 1);
113 }
114 
120 void VuoSceneObject_free(void *sceneObject)
121 {
122  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
123 
124  VuoRelease(so->name);
125  VuoRelease(so->mesh);
126  VuoRelease(so->shader);
127  if (so->type == VuoSceneObjectSubType_Group)
128  VuoRelease(so->childObjects);
129  else if (so->type == VuoSceneObjectSubType_Text)
130  {
131  VuoRelease(so->text.text);
132  VuoFont_release(so->text.font);
133  }
134 
135  free(so);
136 }
137 
142 {
143  VuoSceneObject_internal *so = (VuoSceneObject_internal *)calloc(1, sizeof(VuoSceneObject_internal));
145 
146  so->id = 0;
147  so->type = VuoSceneObjectSubType_Empty;
148 
149  so->mesh = NULL;
150  so->shader = NULL;
151  so->isRealSize = false;
152  so->preservePhysicalSize = false;
153  so->blendMode = VuoBlendMode_Normal;
154 
155  so->name = NULL;
156  so->transform = VuoTransform_makeIdentity();
157 
158  return (VuoSceneObject)so;
159 }
160 
165 {
166  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
167 
168  so->type = VuoSceneObjectSubType_Group;
169 
171 
172  so->transform = transform;
173 
174  return (VuoSceneObject)so;
175 }
176 
181 {
182  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
183 
184  so->type = VuoSceneObjectSubType_Mesh;
185 
187 
188  if (mesh && !shader)
190  else
192 
193  so->transform = transform;
194 
195  return (VuoSceneObject)so;
196 }
197 
212 VuoSceneObject VuoSceneObject_makeQuad(VuoShader shader, VuoPoint3d center, VuoPoint3d rotation, VuoReal width, VuoReal height)
213 {
216  shader,
218  center,
219  VuoPoint3d_multiply(rotation, M_PI/180.),
220  VuoPoint3d_make(width,height,1)
221  ));
222 }
223 
238 VuoSceneObject VuoSceneObject_makeQuadWithNormals(VuoShader shader, VuoPoint3d center, VuoPoint3d rotation, VuoReal width, VuoReal height)
239 {
242  shader,
244  center,
245  VuoPoint3d_multiply(rotation, M_PI/180.),
246  VuoPoint3d_make(width,height,1)
247  ));
248 }
249 
255 VuoSceneObject VuoSceneObject_makeImage(VuoImage image, VuoPoint3d center, VuoPoint3d rotation, VuoReal width, VuoReal alpha)
256 {
257  if (!image)
258  return nullptr;
259 
261  VuoShader_makeUnlitImageShader(image, alpha),
262  center,
263  rotation,
264  width,
265  image->pixelsHigh * width/image->pixelsWide
266  );
267 
268  return object;
269 }
270 
276 VuoSceneObject VuoSceneObject_makeLitImage(VuoImage image, VuoPoint3d center, VuoPoint3d rotation, VuoReal width, VuoReal alpha, VuoColor highlightColor, VuoReal shininess)
277 {
278  if (!image)
279  return nullptr;
280 
282  VuoShader_makeLitImageShader(image, alpha, highlightColor, shininess),
283  center,
284  rotation,
285  width,
286  image->pixelsHigh * width/image->pixelsWide
287  );
288 }
289 
293 VuoSceneObject VuoSceneObject_makeCube(VuoTransform transform, VuoShader frontShader, VuoShader leftShader, VuoShader rightShader, VuoShader backShader, VuoShader topShader, VuoShader bottomShader)
294 {
296 
297  VuoMesh quadMesh = VuoMesh_makeQuad();
298 
299  // Front Face
300  {
302  quadMesh,
303  frontShader,
305  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
306  }
307 
308  // Left Face
309  {
311  quadMesh,
312  leftShader,
313  VuoTransform_makeEuler(VuoPoint3d_make(-.5,0,0), VuoPoint3d_make(0,-M_PI/2.,0), VuoPoint3d_make(1,1,1)));
314  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
315  }
316 
317  // Right Face
318  {
320  quadMesh,
321  rightShader,
323  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
324  }
325 
326  // Back Face
327  {
329  quadMesh,
330  backShader,
332  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
333  }
334 
335  // Top Face
336  {
338  quadMesh,
339  topShader,
341  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
342  }
343 
344  // Bottom Face
345  {
347  quadMesh,
348  bottomShader,
350  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
351  }
352 
353  return VuoSceneObject_makeGroup(cubeChildObjects, transform);
354 }
355 
356 
361 {
362  return VuoSceneObject_makeMesh(VuoMesh_makeCube(), shader, transform);
363 }
364 
373 VuoSceneObject VuoSceneObject_makeText(VuoText text, VuoFont font, VuoBoolean scaleWithScene, float wrapWidth)
374 {
375  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
376  so->type = VuoSceneObjectSubType_Text;
379  so->text.scaleWithScene = scaleWithScene;
380  so->text.wrapWidth = wrapWidth;
381  return (VuoSceneObject)so;
382 }
383 
387 VuoSceneObject VuoSceneObject_makePerspectiveCamera(VuoText name, VuoTransform transform, float fieldOfView, float distanceMin, float distanceMax)
388 {
389  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
390  so->type = VuoSceneObjectSubType_PerspectiveCamera;
392  so->transform = transform;
393  so->camera.fieldOfView = fieldOfView;
394  so->camera.distanceMin = distanceMin;
395  so->camera.distanceMax = distanceMax;
396  return (VuoSceneObject)so;
397 }
398 
402 VuoSceneObject VuoSceneObject_makeStereoCamera(VuoText name, VuoTransform transform, VuoReal fieldOfView, VuoReal distanceMin, VuoReal distanceMax, VuoReal confocalDistance, VuoReal intraocularDistance)
403 {
404  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
405  so->type = VuoSceneObjectSubType_StereoCamera;
407  so->transform = transform;
408  so->camera.fieldOfView = fieldOfView;
409  so->camera.distanceMin = distanceMin;
410  so->camera.distanceMax = distanceMax;
411  so->camera.confocalDistance = confocalDistance;
412  so->camera.intraocularDistance = intraocularDistance;
413  return (VuoSceneObject)so;
414 }
415 
419 VuoSceneObject VuoSceneObject_makeOrthographicCamera(VuoText name, VuoTransform transform, float width, float distanceMin, float distanceMax)
420 {
421  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
422  so->type = VuoSceneObjectSubType_OrthographicCamera;
424  so->transform = transform;
425  so->camera.width = width;
426  so->camera.distanceMin = distanceMin;
427  so->camera.distanceMax = distanceMax;
428  return (VuoSceneObject)so;
429 }
430 
434 VuoSceneObject VuoSceneObject_makeFisheyeCamera(VuoText name, VuoTransform transform, VuoReal fieldOfView, VuoReal vignetteWidth, VuoReal vignetteSharpness)
435 {
436  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
437  so->type = VuoSceneObjectSubType_FisheyeCamera;
439  so->transform = transform;
440  so->camera.fieldOfView = fieldOfView;
441 
442  // 0 and 1000 come from "Realtime Dome Imaging and Interaction" by Bailey/Clothier/Gebbie 2006.
443  so->camera.distanceMin = 0;
444  so->camera.distanceMax = 1000;
445 
446  so->camera.vignetteWidth = vignetteWidth;
447  so->camera.vignetteSharpness = vignetteSharpness;
448 
449  return (VuoSceneObject)so;
450 }
451 
456 {
458  VuoPoint3d_make(0,0,1),
459  VuoPoint3d_make(0,0,0),
460  VuoPoint3d_make(1,1,1)
461  );
463  VuoText_make("default camera"),
464  transform,
465  90,
466  0.1,
467  10.0
468  );
469 }
470 
480 bool VuoSceneObject_find(VuoSceneObject sceneObject, VuoText nameToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
481 {
482  if (!sceneObject)
483  return false;
484 
485  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
486 
487  if (VuoText_areEqual(so->name, nameToMatch))
488  {
489  *foundObject = (VuoSceneObject)so;
490  return true;
491  }
492 
493  if (so->type == VuoSceneObjectSubType_Group)
494  {
495  VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
496 
497  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
498  for (unsigned long i = 1; i <= childObjectCount; ++i)
499  {
500  VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
501  if (VuoSceneObject_find(childObject, nameToMatch, ancestorObjects, foundObject))
502  return true;
503  }
504 
505  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
506  }
507 
508  return false;
509 }
510 
521 bool VuoSceneObject_findById(VuoSceneObject sceneObject, uint64_t idToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
522 {
523  if (!sceneObject)
524  return false;
525 
526  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
527 
528  if (so->id == idToMatch)
529  {
530  *foundObject = (VuoSceneObject)so;
531  return true;
532  }
533 
534  if (so->type == VuoSceneObjectSubType_Group)
535  {
536  VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
537 
538  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
539  for (unsigned long i = 1; i <= childObjectCount; ++i)
540  {
541  VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
542  if (VuoSceneObject_findById(childObject, idToMatch, ancestorObjects, foundObject))
543  return true;
544  }
545 
546  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
547  }
548 
549  return false;
550 }
551 
562 bool VuoSceneObject_findWithType(VuoSceneObject sceneObject, VuoSceneObjectSubType typeToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
563 {
564  if (!sceneObject)
565  return false;
566 
567  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
568 
569  if (so->type == typeToMatch)
570  {
571  *foundObject = (VuoSceneObject)so;
572  return true;
573  }
574 
575  if (so->type == VuoSceneObjectSubType_Group)
576  {
577  VuoListAppendValue_VuoSceneObject(ancestorObjects, sceneObject);
578 
579  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
580 
581  for (unsigned long i = 1; i <= childObjectCount; ++i)
582  {
583  VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so->childObjects, i);
584 
585  if (VuoSceneObject_findWithType(childObject, typeToMatch, ancestorObjects, foundObject))
586  return true;
587  }
588 
589  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
590  }
591 
592  return false;
593 }
594 
603 bool VuoSceneObject_findCamera(VuoSceneObject sceneObject, VuoText nameToMatch, VuoSceneObject *foundCamera)
604 {
605  if (!sceneObject)
606  return false;
607 
608  __block bool didFindCamera = false;
609  VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
610  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
611  if ((co->type == VuoSceneObjectSubType_PerspectiveCamera
612  || co->type == VuoSceneObjectSubType_StereoCamera
613  || co->type == VuoSceneObjectSubType_OrthographicCamera
614  || co->type == VuoSceneObjectSubType_FisheyeCamera)
615  && (!nameToMatch || (co->name && nameToMatch && strstr(co->name, nameToMatch))))
616  {
617  *foundCamera = currentObject;
618  VuoSceneObject_setTransform(*foundCamera, VuoTransform_makeFromMatrix4x4(modelviewMatrix));
619  didFindCamera = true;
620  return false;
621  }
622  return true;
623  });
624 
625  return didFindCamera;
626 }
627 
632 {
633  if (!sceneObject)
634  return false;
635 
636  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
637  return so->type != VuoSceneObjectSubType_Empty;
638 }
639 
644 {
645  if (strcmp(typeString,"empty")==0)
646  return VuoSceneObjectSubType_Empty;
647  else if (strcmp(typeString,"group")==0)
648  return VuoSceneObjectSubType_Group;
649  else if (strcmp(typeString,"mesh")==0)
650  return VuoSceneObjectSubType_Mesh;
651  else if (strcmp(typeString,"camera-perspective")==0)
652  return VuoSceneObjectSubType_PerspectiveCamera;
653  else if (strcmp(typeString,"camera-stereo")==0)
654  return VuoSceneObjectSubType_StereoCamera;
655  else if (strcmp(typeString,"camera-orthographic")==0)
656  return VuoSceneObjectSubType_OrthographicCamera;
657  else if (strcmp(typeString,"camera-fisheye")==0)
658  return VuoSceneObjectSubType_FisheyeCamera;
659  else if (strcmp(typeString,"light-ambient")==0)
660  return VuoSceneObjectSubType_AmbientLight;
661  else if (strcmp(typeString,"light-point")==0)
662  return VuoSceneObjectSubType_PointLight;
663  else if (strcmp(typeString,"light-spot")==0)
664  return VuoSceneObjectSubType_Spotlight;
665  else if (strcmp(typeString,"text")==0)
666  return VuoSceneObjectSubType_Text;
667 
668  return VuoSceneObjectSubType_Empty;
669 }
670 
675 {
676  switch (type)
677  {
678  case VuoSceneObjectSubType_Group:
679  return "group";
680  case VuoSceneObjectSubType_Mesh:
681  return "mesh";
682  case VuoSceneObjectSubType_PerspectiveCamera:
683  return "camera-perspective";
684  case VuoSceneObjectSubType_StereoCamera:
685  return "camera-stereo";
686  case VuoSceneObjectSubType_OrthographicCamera:
687  return "camera-orthographic";
688  case VuoSceneObjectSubType_FisheyeCamera:
689  return "camera-fisheye";
690  case VuoSceneObjectSubType_AmbientLight:
691  return "light-ambient";
692  case VuoSceneObjectSubType_PointLight:
693  return "light-point";
694  case VuoSceneObjectSubType_Spotlight:
695  return "light-spot";
696  case VuoSceneObjectSubType_Text:
697  return "text";
698  // VuoSceneObjectSubType_Empty
699  default:
700  return "empty";
701  }
702 }
703 
709 {
710  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
711  so->type = VuoSceneObjectSubType_AmbientLight;
712  VuoSceneObject_setName((VuoSceneObject)so, VuoText_make("Ambient Light"));
713  so->light.color = color;
714  so->light.brightness = brightness;
715  return (VuoSceneObject)so;
716 }
717 
728 VuoSceneObject VuoSceneObject_makePointLight(VuoColor color, float brightness, VuoPoint3d position, float range, float sharpness)
729 {
730  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
731  so->type = VuoSceneObjectSubType_PointLight;
732  VuoText t = VuoText_make("Point Light");
734  so->light.color = color;
735  so->light.brightness = brightness;
736  so->light.range = range;
737  so->light.sharpness = MAX(MIN(sharpness,1),0);
738  so->transform = VuoTransform_makeEuler(position, VuoPoint3d_make(0,0,0), VuoPoint3d_make(1,1,1));
739  return (VuoSceneObject)so;
740 }
741 
753 VuoSceneObject VuoSceneObject_makeSpotlight(VuoColor color, float brightness, VuoTransform transform, float cone, float range, float sharpness)
754 {
755  VuoSceneObject_internal *so = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
756  so->type = VuoSceneObjectSubType_Spotlight;
758  so->light.color = color;
759  so->light.brightness = brightness;
760  so->light.cone = cone;
761  so->light.range = range;
762  so->light.sharpness = MAX(MIN(sharpness,1),0);
763  so->transform = transform;
764  return (VuoSceneObject)so;
765 }
766 
775 void VuoSceneObject_findLights(VuoSceneObject sceneObject, VuoColor *ambientColor, float *ambientBrightness, VuoList_VuoSceneObject *pointLights, VuoList_VuoSceneObject *spotLights)
776 {
777  __block VuoList_VuoColor ambientColors = VuoListCreate_VuoColor();
778  VuoRetain(ambientColors);
779 
780  *ambientBrightness = 0;
781  *pointLights = VuoListCreate_VuoSceneObject();
782  *spotLights = VuoListCreate_VuoSceneObject();
783 
784  VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
785  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
786  if (co->type == VuoSceneObjectSubType_AmbientLight)
787  {
788  VuoListAppendValue_VuoColor(ambientColors, co->light.color);
789  *ambientBrightness += co->light.brightness;
790  }
791  else if (co->type == VuoSceneObjectSubType_PointLight)
792  {
795  VuoListAppendValue_VuoSceneObject(*pointLights, l);
796  }
797  else if (co->type == VuoSceneObjectSubType_Spotlight)
798  {
801  VuoListAppendValue_VuoSceneObject(*spotLights, l);
802  }
803  return true;
804  });
805 
806  if (!VuoListGetCount_VuoColor(ambientColors)
807  && !VuoListGetCount_VuoSceneObject(*pointLights)
808  && !VuoListGetCount_VuoSceneObject(*spotLights))
809  {
810  *ambientColor = VuoColor_makeWithRGBA(1,1,1,1);
811  *ambientBrightness = 0.05;
812 
813  // https://en.wikipedia.org/wiki/Three-point_lighting
814 
815  VuoSceneObject keyLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .70, VuoPoint3d_make(-1,1,1), 5, .5);
816  VuoListAppendValue_VuoSceneObject(*pointLights, keyLight);
817 
818  VuoSceneObject fillLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .20, VuoPoint3d_make(.5,0,1), 5, 0);
819  VuoListAppendValue_VuoSceneObject(*pointLights, fillLight);
820 
821  VuoSceneObject backLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .15, VuoPoint3d_make(1,.75,-.5), 5, 0);
822  VuoListAppendValue_VuoSceneObject(*pointLights, backLight);
823  }
824  else
825  *ambientColor = VuoColor_average(ambientColors);
826 
827  VuoRelease(ambientColors);
828 }
829 
833 typedef struct
834 {
835  long objectCount;
836  const VuoSceneObject *objects;
837  float modelviewMatrix[16];
839 
852 void VuoSceneObject_visit(const VuoSceneObject object, bool (^function)(const VuoSceneObject currentObject, float modelviewMatrix[16]))
853 {
854  if (!object)
855  return;
856 
857  VuoSceneObject_treeState rootState;
858  rootState.objectCount = 1;
859  rootState.objects = &object;
860  VuoTransform_getMatrix(VuoTransform_makeIdentity(), rootState.modelviewMatrix);
861 
862  std::list<VuoSceneObject_treeState> objectsToVisit(1, rootState);
863  while (!objectsToVisit.empty())
864  {
865  VuoSceneObject_treeState currentState = objectsToVisit.front();
866  objectsToVisit.pop_front();
867 
868  for (long i = 0; i < currentState.objectCount; ++i)
869  {
870  VuoSceneObject_internal *currentObject = (VuoSceneObject_internal *)currentState.objects[i];
871  if (!currentObject)
872  continue;
873 
874  float localModelviewMatrix[16];
875  VuoTransform_getMatrix(currentObject->transform, localModelviewMatrix);
876  float compositeModelviewMatrix[16];
877  VuoTransform_multiplyMatrices4x4(localModelviewMatrix, currentState.modelviewMatrix, compositeModelviewMatrix);
878 
879  if (!function((VuoSceneObject)currentObject, compositeModelviewMatrix))
880  return;
881 
882  if (currentObject->type == VuoSceneObjectSubType_Group)
883  {
884  // Prepend this object's childObjects to the objectsToVisit queue.
885  long childObjectCount = VuoListGetCount_VuoSceneObject(currentObject->childObjects);
886  if (childObjectCount)
887  {
888  VuoSceneObject_treeState childState;
889  childState.objectCount = childObjectCount;
890  childState.objects = VuoListGetData_VuoSceneObject(currentObject->childObjects);
891  memcpy(childState.modelviewMatrix, compositeModelviewMatrix, sizeof(float[16]));
892  objectsToVisit.push_front(childState);
893  }
894  }
895  }
896  }
897 }
898 
902 static void VuoSceneObject_applyInternal(VuoSceneObject sceneObject, void (^function)(VuoSceneObject currentObject, float modelviewMatrix[16]), float modelviewMatrix[16])
903 {
904  if (!sceneObject)
905  return;
906 
907  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
908 
909  float localModelviewMatrix[16];
910  VuoTransform_getMatrix(so->transform, localModelviewMatrix);
911  float compositeModelviewMatrix[16];
912  VuoTransform_multiplyMatrices4x4(localModelviewMatrix, modelviewMatrix, compositeModelviewMatrix);
913 
914  function(sceneObject, compositeModelviewMatrix);
915 
916  if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
917  {
918  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
919  for (unsigned long i = 1; i <= childObjectCount; ++i)
920  {
921  VuoSceneObject o = VuoListGetValue_VuoSceneObject(so->childObjects, i);
922  VuoSceneObject_applyInternal(o, function, compositeModelviewMatrix);
923  VuoListSetValue_VuoSceneObject(so->childObjects, o, i, false);
924  }
925  }
926 }
927 
937 void VuoSceneObject_apply(VuoSceneObject object, void (^function)(VuoSceneObject currentObject, float modelviewMatrix[16]))
938 {
939  if (!object)
940  return;
941 
942  float localModelviewMatrix[16];
943  VuoTransform_getMatrix(VuoTransform_makeIdentity(), localModelviewMatrix);
944 
945  VuoSceneObject_applyInternal(object, function, localModelviewMatrix);
946 }
947 
954 {
955  if (!object)
956  return;
957 
958  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
959  so->transform = VuoTransform_composite(so->transform, transform);
960 }
961 
967 void VuoSceneObject_translate(VuoSceneObject object, VuoPoint3d translation)
968 {
969  if (!object)
970  return;
971 
972  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
973  so->transform.translation += translation;
974 }
975 
981 void VuoSceneObject_scale(VuoSceneObject object, VuoPoint3d scale)
982 {
983  if (!object)
984  return;
985 
986  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
987  so->transform.scale *= scale;
988 }
989 
996 {
997  if (!object)
998  return nullptr;
999 
1000  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1001  return so->name;
1002 }
1003 
1013 {
1014  if (!object)
1015  return nullptr;
1016 
1017  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1018  if (so->type != VuoSceneObjectSubType_Group)
1019  return nullptr;
1020 
1021  return so->childObjects;
1022 }
1023 
1030 {
1031  if (!object)
1032  return VuoBlendMode_Normal;
1033 
1034  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1035  return so->blendMode;
1036 }
1037 
1044 {
1045  if (!object)
1046  return VuoSceneObjectSubType_Empty;
1047 
1048  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1049  return so->type;
1050 }
1051 
1058 uint64_t VuoSceneObject_getId(const VuoSceneObject object)
1059 {
1060  if (!object)
1061  return 0;
1062 
1063  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1064  return so->id;
1065 }
1066 
1076 {
1077  if (!object)
1078  return nullptr;
1079 
1080  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1081  return so->shader;
1082 }
1083 
1091 {
1092  if (!object)
1093  return false;
1094 
1095  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1096  return so->isRealSize;
1097 }
1098 
1107 {
1108  if (!object)
1109  return false;
1110 
1111  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1112  return so->preservePhysicalSize;
1113 }
1114 
1124 {
1125  if (!object)
1126  return nullptr;
1127 
1128  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1129  return so->mesh;
1130 }
1131 
1138 {
1139  if (!object)
1140  return nullptr;
1141 
1142  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1143  return so->text.text;
1144 }
1145 
1152 {
1153  if (!object)
1154  return VuoFont_makeDefault();
1155 
1156  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1157  return so->text.font;
1158 }
1159 
1168 {
1169  if (!object)
1170  return false;
1171 
1172  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1173  return so->text.scaleWithScene;
1174 }
1175 
1182 {
1183  if (!object)
1184  return 0;
1185 
1186  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1187  return so->text.wrapWidth;
1188 }
1189 
1196 {
1197  if (!object)
1198  return 0;
1199 
1200  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1201  return so->camera.fieldOfView;
1202 }
1203 
1210 {
1211  if (!object)
1212  return 0;
1213 
1214  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1215  return so->camera.width;
1216 }
1217 
1224 {
1225  if (!object)
1226  return 0;
1227 
1228  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1229  return so->camera.distanceMin;
1230 }
1231 
1238 {
1239  if (!object)
1240  return 0;
1241 
1242  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1243  return so->camera.distanceMax;
1244 }
1245 
1252 {
1253  if (!object)
1254  return 0;
1255 
1256  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1257  return so->camera.vignetteWidth;
1258 }
1259 
1266 {
1267  if (!object)
1268  return 0;
1269 
1270  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1271  return so->camera.vignetteSharpness;
1272 }
1273 
1280 {
1281  if (!object)
1282  return 0;
1283 
1284  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1285  return so->camera.intraocularDistance;
1286 }
1287 
1294 {
1295  if (!object)
1296  return 0;
1297 
1298  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1299  return so->camera.confocalDistance;
1300 }
1301 
1308 {
1309  if (!object)
1310  return (VuoColor){0,0,0,0};
1311 
1312  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1313  return so->light.color;
1314 }
1315 
1322 {
1323  if (!object)
1324  return 0;
1325 
1326  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1327  return so->light.brightness;
1328 }
1329 
1336 {
1337  if (!object)
1338  return 0;
1339 
1340  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1341  return so->light.range;
1342 }
1343 
1350 {
1351  if (!object)
1352  return 0;
1353 
1354  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1355  return so->light.sharpness;
1356 }
1357 
1364 {
1365  if (!object)
1366  return 0;
1367 
1368  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1369  return so->light.cone;
1370 }
1371 
1378 {
1379  if (!object)
1380  return VuoTransform_makeIdentity();
1381 
1382  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1383  return so->transform;
1384 }
1385 
1392 {
1393  if (!object)
1394  return (VuoPoint3d){0,0,0};
1395 
1396  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1397  return so->transform.translation;
1398 }
1399 
1406 {
1407  if (!object)
1408  return;
1409 
1410  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1411  so->type = type;
1412 }
1413 
1420 void VuoSceneObject_setId(VuoSceneObject object, uint64_t id)
1421 {
1422  if (!object)
1423  return;
1424 
1425  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1426  so->id = id;
1427 }
1428 
1435 {
1436  if (!object)
1437  return;
1438 
1439  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1440  VuoRetain(name);
1441  VuoRelease(so->name);
1442  so->name = name;
1443 }
1444 
1451 {
1452  if (!object)
1453  return;
1454 
1455  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1456  if (so->type != VuoSceneObjectSubType_Group)
1457  return;
1458 
1459  VuoRetain(childObjects);
1460  VuoRelease(so->childObjects);
1461  so->childObjects = childObjects;
1462 }
1463 
1470 {
1471  if (!object)
1472  return;
1473 
1474  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1475  VuoRetain(mesh);
1476  VuoRelease(so->mesh);
1477  so->mesh = mesh;
1478 }
1479 
1486 {
1487  if (!object)
1488  return;
1489 
1490  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1491  so->transform = transform;
1492 }
1493 
1499 void VuoSceneObject_setTranslation(VuoSceneObject object, VuoPoint3d translation)
1500 {
1501  if (!object)
1502  return;
1503 
1504  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1505  so->transform.translation = translation;
1506 }
1507 
1513 void VuoSceneObject_setScale(VuoSceneObject object, VuoPoint3d scale)
1514 {
1515  if (!object)
1516  return;
1517 
1518  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1519  so->transform.scale = scale;
1520 }
1521 
1528 {
1529  if (!object)
1530  return;
1531 
1532  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1533  VuoRetain(shader);
1534  VuoRelease(so->shader);
1535  so->shader = shader;
1536 }
1537 
1542 {
1543  if (!object)
1544  return;
1545 
1546  VuoSceneObject_apply(object, ^(VuoSceneObject currentObject, float modelviewMatrix[16]){
1547  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
1548  VuoMesh_setFaceCulling(co->mesh, faceCulling);
1549  });
1550 }
1551 
1565 {
1566  if (!object)
1567  return;
1568 
1569  VuoSceneObject_apply(object, ^(VuoSceneObject currentObject, float modelviewMatrix[16]){
1570  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
1571  co->blendMode = blendMode;
1572  });
1573 }
1574 
1581 void VuoSceneObject_setRealSize(VuoSceneObject object, bool isRealSize)
1582 {
1583  if (!object)
1584  return;
1585 
1586  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1587  so->isRealSize = isRealSize;
1588 }
1589 
1596 void VuoSceneObject_setPreservePhysicalSize(VuoSceneObject object, bool shouldPreservePhysicalSize)
1597 {
1598  if (!object)
1599  return;
1600 
1601  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1602  so->preservePhysicalSize = shouldPreservePhysicalSize;
1603 }
1604 
1611 {
1612  if (!object)
1613  return;
1614 
1615  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1616  VuoRetain(text);
1617  VuoRelease(so->text.text);
1618  so->text.text = text;
1619 }
1620 
1627 {
1628  if (!object)
1629  return;
1630 
1631  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1632  VuoFont_retain(font);
1633  VuoFont_release(so->text.font);
1634  so->text.font = font;
1635 }
1636 
1643 {
1644  if (!object)
1645  return;
1646 
1647  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1648  so->camera.fieldOfView = fieldOfView;
1649 }
1650 
1657 {
1658  if (!object)
1659  return;
1660 
1661  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1662  so->camera.distanceMin = distanceMin;
1663 }
1664 
1671 {
1672  if (!object)
1673  return;
1674 
1675  VuoSceneObject_internal *so = (VuoSceneObject_internal *)object;
1676  so->camera.distanceMax = distanceMax;
1677 }
1678 
1691 {
1692  if (!object)
1693  return nullptr;
1694 
1695  VuoSceneObject_internal *o = (VuoSceneObject_internal *)object;
1696 
1697  VuoSceneObject_internal *co = (VuoSceneObject_internal *)VuoSceneObject_makeEmpty();
1698 
1699  co->type = o->type;
1700  co->id = o->id;
1701  VuoSceneObject_setName((VuoSceneObject)co, o->name);
1702  co->transform = o->transform;
1704  VuoSceneObject_setShader((VuoSceneObject)co, o->shader); // @todo
1705  co->isRealSize = o->isRealSize;
1706  co->preservePhysicalSize = o->preservePhysicalSize;
1707  co->blendMode = o->blendMode;
1708 
1709  if (o->type == VuoSceneObjectSubType_Group && o->childObjects)
1710  {
1711  co->childObjects = VuoListCreate_VuoSceneObject();
1712  VuoRetain(co->childObjects);
1713  VuoListForeach_VuoSceneObject(o->childObjects, ^(const VuoSceneObject object){
1714  VuoListAppendValue_VuoSceneObject(co->childObjects, VuoSceneObject_copy(object));
1715  return true;
1716  });
1717  }
1718 
1719  if (o->type == VuoSceneObjectSubType_PerspectiveCamera
1720  || o->type == VuoSceneObjectSubType_StereoCamera
1721  || o->type == VuoSceneObjectSubType_OrthographicCamera
1722  || o->type == VuoSceneObjectSubType_FisheyeCamera)
1723  {
1724  co->camera.fieldOfView = o->camera.fieldOfView;
1725  co->camera.width = o->camera.width;
1726  co->camera.distanceMin = o->camera.distanceMin;
1727  co->camera.distanceMax = o->camera.distanceMax;
1728  co->camera.confocalDistance = o->camera.confocalDistance;
1729  co->camera.intraocularDistance = o->camera.intraocularDistance;
1730  co->camera.vignetteWidth = o->camera.vignetteWidth;
1731  co->camera.vignetteSharpness = o->camera.vignetteSharpness;
1732  }
1733  else if (o->type == VuoSceneObjectSubType_AmbientLight
1734  || o->type == VuoSceneObjectSubType_PointLight
1735  || o->type == VuoSceneObjectSubType_Spotlight)
1736  {
1737  co->light.color = o->light.color;
1738  co->light.brightness = o->light.brightness;
1739  co->light.range = o->light.range;
1740  co->light.cone = o->light.cone;
1741  co->light.sharpness = o->light.sharpness;
1742  }
1743  else if (o->type == VuoSceneObjectSubType_Text)
1744  {
1745  VuoSceneObject_setText((VuoSceneObject)co, o->text.text);
1746  VuoSceneObject_setTextFont((VuoSceneObject)co, o->text.font);
1747  co->text.scaleWithScene = o->text.scaleWithScene;
1748  co->text.wrapWidth = o->text.wrapWidth;
1749  }
1750 
1751  return (VuoSceneObject)co;
1752 }
1753 
1758 {
1759  if (!so)
1760  return VuoBox_make((VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0});
1761 
1762  __block bool haveGlobalBounds = false;
1763  __block VuoBox globalBounds;
1764 
1765  VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
1766  VuoBox bounds;
1767  bool foundBounds = VuoSceneObject_meshBounds(currentObject, &bounds, modelviewMatrix);
1768  if (foundBounds)
1769  {
1770  globalBounds = haveGlobalBounds ? VuoBox_encapsulate(globalBounds, bounds) : bounds;
1771  haveGlobalBounds = true;
1772  }
1773  return true;
1774  });
1775 
1776  if (haveGlobalBounds)
1777  return globalBounds;
1778  else
1779  return VuoBox_make( (VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0} );
1780 }
1781 
1785 bool VuoSceneObject_meshBounds(const VuoSceneObject sceneObject, VuoBox *bounds, float matrix[16])
1786 {
1787  if (!sceneObject)
1788  return false;
1789 
1790  if (VuoSceneObject_getVertexCount(sceneObject) < 1)
1791  return false;
1792 
1793  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1794  if (so->isRealSize
1795  || so->type == VuoSceneObjectSubType_Text)
1796  {
1797  // We don't know what the actual rendered size of the realSize layer will be,
1798  // but we can at least include its center point.
1799  *bounds = VuoBox_make(VuoPoint3d_make(matrix[12], matrix[13], matrix[14]), VuoPoint3d_make(0,0,0));
1800  }
1801  else
1802  {
1803  *bounds = VuoMesh_bounds(so->mesh, matrix);
1804 
1805  if (so->shader)
1806  {
1807  bounds->size.x *= so->shader->objectScale;
1808  bounds->size.y *= so->shader->objectScale;
1809  bounds->size.z *= so->shader->objectScale;
1810  }
1811  }
1812 
1813  return true;
1814 }
1815 
1820 {
1821  if (!sceneObject)
1822  return;
1823 
1824  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1825  VuoBox bounds = VuoSceneObject_bounds(sceneObject);
1826  so->transform.translation = VuoPoint3d_subtract(so->transform.translation, bounds.center);
1827 }
1828 
1835 {
1836  if (!sceneObject)
1837  return;
1838  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
1839  VuoBox bounds = VuoSceneObject_bounds(sceneObject);
1840 
1841  float scale = fmax(fmax(bounds.size.x, bounds.size.y), bounds.size.z);
1842  if (fabs(scale) < 0.00001)
1843  return;
1844 
1845  so->transform.scale = VuoPoint3d_multiply(so->transform.scale, 1./scale);
1846  so->transform.translation = VuoPoint3d_multiply(so->transform.translation, 1./scale);
1847 }
1848 
1875 {
1876  json_object *o = NULL;
1877 
1878  int id = 0;
1879  if (json_object_object_get_ex(js, "id", &o))
1880  id = json_object_get_int64(o);
1881 
1882  VuoSceneObjectSubType type = VuoSceneObjectSubType_Empty;
1883  if (json_object_object_get_ex(js, "type", &o))
1884  type = VuoSceneObject_typeFromCString(json_object_get_string(o));
1885 
1886  VuoMesh mesh = NULL;
1887  if (json_object_object_get_ex(js, "mesh", &o))
1888  mesh = VuoMesh_makeFromJson(o);
1889 
1890  VuoShader shader = NULL;
1891  if (json_object_object_get_ex(js, "shader", &o))
1892  shader = VuoShader_makeFromJson(o);
1893 
1894  bool isRealSize = false;
1895  if (json_object_object_get_ex(js, "isRealSize", &o))
1896  isRealSize = VuoBoolean_makeFromJson(o);
1897 
1898  bool preservePhysicalSize = false;
1899  if (json_object_object_get_ex(js, "preservePhysicalSize", &o))
1900  preservePhysicalSize = VuoBoolean_makeFromJson(o);
1901 
1902  VuoBlendMode blendMode = VuoBlendMode_Normal;
1903  if (json_object_object_get_ex(js, "blendMode", &o))
1904  blendMode = VuoBlendMode_makeFromJson(o);
1905 
1906  VuoList_VuoSceneObject childObjects = NULL;
1907  if (json_object_object_get_ex(js, "childObjects", &o))
1908  childObjects = VuoList_VuoSceneObject_makeFromJson(o);
1909 
1910  float cameraFieldOfView;
1911  if (json_object_object_get_ex(js, "cameraFieldOfView", &o))
1912  cameraFieldOfView = json_object_get_double(o);
1913 
1914  float cameraWidth;
1915  if (json_object_object_get_ex(js, "cameraWidth", &o))
1916  cameraWidth = json_object_get_double(o);
1917 
1918  float cameraDistanceMin;
1919  if (json_object_object_get_ex(js, "cameraDistanceMin", &o))
1920  cameraDistanceMin = json_object_get_double(o);
1921 
1922  float cameraDistanceMax;
1923  if (json_object_object_get_ex(js, "cameraDistanceMax", &o))
1924  cameraDistanceMax = json_object_get_double(o);
1925 
1926  float cameraConfocalDistance;
1927  if (json_object_object_get_ex(js, "cameraConfocalDistance", &o))
1928  cameraConfocalDistance = json_object_get_double(o);
1929 
1930  float cameraIntraocularDistance;
1931  if (json_object_object_get_ex(js, "cameraIntraocularDistance", &o))
1932  cameraIntraocularDistance = json_object_get_double(o);
1933 
1934  float cameraVignetteWidth;
1935  if (json_object_object_get_ex(js, "cameraVignetteWidth", &o))
1936  cameraVignetteWidth = json_object_get_double(o);
1937 
1938  float cameraVignetteSharpness;
1939  if (json_object_object_get_ex(js, "cameraVignetteSharpness", &o))
1940  cameraVignetteSharpness = json_object_get_double(o);
1941 
1942  VuoColor lightColor;
1943  if (json_object_object_get_ex(js, "lightColor", &o))
1944  lightColor = VuoColor_makeFromJson(o);
1945 
1946  float lightBrightness;
1947  if (json_object_object_get_ex(js, "lightBrightness", &o))
1948  lightBrightness = json_object_get_double(o);
1949 
1950  float lightCone;
1951  if (json_object_object_get_ex(js, "lightCone", &o))
1952  lightCone = json_object_get_double(o);
1953 
1954  float lightRange;
1955  if (json_object_object_get_ex(js, "lightRange", &o))
1956  lightRange = json_object_get_double(o);
1957 
1958  float lightSharpness;
1959  if (json_object_object_get_ex(js, "lightSharpness", &o))
1960  lightSharpness = json_object_get_double(o);
1961 
1962  VuoText name = NULL;
1963  if (json_object_object_get_ex(js, "name", &o))
1964  name = VuoText_makeFromJson(o);
1965 
1966  json_object_object_get_ex(js, "transform", &o);
1967  VuoTransform transform = VuoTransform_makeFromJson(o);
1968 
1969  VuoText text = NULL;
1970  if (json_object_object_get_ex(js, "text", &o))
1971  text = VuoText_makeFromJson(o);
1972 
1973  VuoFont font;
1974  if (json_object_object_get_ex(js, "textFont", &o))
1975  font = VuoFont_makeFromJson(o);
1976 
1977  bool scaleWithScene = false;
1978  if (json_object_object_get_ex(js, "textScaleWithScene", &o))
1979  scaleWithScene = VuoBoolean_makeFromJson(o);
1980 
1981  float wrapWidth = INFINITY;
1982  if (json_object_object_get_ex(js, "textWrapWidth", &o))
1983  wrapWidth = json_object_get_double(o);
1984 
1985  VuoSceneObject obj;
1986  switch (type)
1987  {
1988  case VuoSceneObjectSubType_Empty:
1989  obj = nullptr;
1990  break;
1991  case VuoSceneObjectSubType_Group:
1992  obj = VuoSceneObject_makeGroup(childObjects, transform);
1993  break;
1994  case VuoSceneObjectSubType_Mesh:
1995  {
1996  obj = VuoSceneObject_makeMesh(mesh, shader, transform);
1997  VuoSceneObject_internal *so = (VuoSceneObject_internal *)obj;
1998  so->isRealSize = isRealSize;
1999  so->preservePhysicalSize = preservePhysicalSize;
2000  so->blendMode = blendMode;
2001  VuoSceneObject_setName(obj, name);
2002  break;
2003  }
2004  case VuoSceneObjectSubType_PerspectiveCamera:
2006  name,
2007  transform,
2008  cameraFieldOfView,
2009  cameraDistanceMin,
2010  cameraDistanceMax
2011  );
2012  break;
2013  case VuoSceneObjectSubType_StereoCamera:
2015  name,
2016  transform,
2017  cameraFieldOfView,
2018  cameraDistanceMin,
2019  cameraDistanceMax,
2020  cameraConfocalDistance,
2021  cameraIntraocularDistance
2022  );
2023  break;
2024  case VuoSceneObjectSubType_OrthographicCamera:
2026  name,
2027  transform,
2028  cameraWidth,
2029  cameraDistanceMin,
2030  cameraDistanceMax
2031  );
2032  break;
2033  case VuoSceneObjectSubType_FisheyeCamera:
2035  name,
2036  transform,
2037  cameraFieldOfView,
2038  cameraVignetteWidth,
2039  cameraVignetteSharpness
2040  );
2041  break;
2042  case VuoSceneObjectSubType_AmbientLight:
2043  obj = VuoSceneObject_makeAmbientLight(lightColor, lightBrightness);
2044  break;
2045  case VuoSceneObjectSubType_PointLight:
2046  obj = VuoSceneObject_makePointLight(lightColor, lightBrightness, transform.translation, lightRange, lightSharpness);
2047  break;
2048  case VuoSceneObjectSubType_Spotlight:
2049  obj = VuoSceneObject_makeSpotlight(lightColor, lightBrightness, transform, lightCone, lightRange, lightSharpness);
2050  break;
2051  case VuoSceneObjectSubType_Text:
2052  obj = VuoSceneObject_makeText(text, font, scaleWithScene, wrapWidth);
2053  VuoSceneObject_setTransform(obj, transform);
2054  VuoSceneObject_setMesh(obj, mesh);
2055  break;
2056  }
2057 
2058  VuoSceneObject_setId(obj, id);
2059 
2060  return obj;
2061 }
2062 
2067 {
2068  if (!sceneObject)
2069  return nullptr;
2070 
2071  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2072 
2073  json_object *js = json_object_new_object();
2074 
2075  json_object_object_add(js, "id", json_object_new_int64(so->id));
2076  json_object_object_add(js, "type", json_object_new_string(VuoSceneObject_cStringForType(so->type)));
2077 
2078  switch (so->type)
2079  {
2080  case VuoSceneObjectSubType_Empty:
2081  break;
2082 
2083  case VuoSceneObjectSubType_Mesh:
2084  if (so->mesh)
2085  json_object_object_add(js, "mesh", VuoMesh_getJson(so->mesh));
2086 
2087  if (so->shader)
2088  json_object_object_add(js, "shader", VuoShader_getJson(so->shader));
2089 
2090  json_object_object_add(js, "isRealSize", VuoBoolean_getJson(so->isRealSize));
2091 
2092  json_object_object_add(js, "preservePhysicalSize", VuoBoolean_getJson(so->preservePhysicalSize));
2093 
2094  if (so->blendMode != VuoBlendMode_Normal)
2095  json_object_object_add(js, "blendMode", VuoBlendMode_getJson(so->blendMode));
2096  break;
2097 
2098  case VuoSceneObjectSubType_Group:
2099  if (so->childObjects)
2100  json_object_object_add(js, "childObjects", VuoList_VuoSceneObject_getJson(so->childObjects));
2101  break;
2102 
2103  case VuoSceneObjectSubType_PerspectiveCamera:
2104  case VuoSceneObjectSubType_StereoCamera:
2105  case VuoSceneObjectSubType_OrthographicCamera:
2106  case VuoSceneObjectSubType_FisheyeCamera:
2107  {
2108  if (so->type != VuoSceneObjectSubType_FisheyeCamera)
2109  {
2110  json_object_object_add(js, "cameraDistanceMin", json_object_new_double(so->camera.distanceMin));
2111  json_object_object_add(js, "cameraDistanceMax", json_object_new_double(so->camera.distanceMax));
2112  }
2113 
2114  if (so->type == VuoSceneObjectSubType_PerspectiveCamera
2115  || so->type == VuoSceneObjectSubType_StereoCamera
2116  || so->type == VuoSceneObjectSubType_FisheyeCamera)
2117  json_object_object_add(js, "cameraFieldOfView", json_object_new_double(so->camera.fieldOfView));
2118 
2119  if (so->type == VuoSceneObjectSubType_StereoCamera)
2120  {
2121  json_object_object_add(js, "cameraConfocalDistance", json_object_new_double(so->camera.confocalDistance));
2122  json_object_object_add(js, "cameraIntraocularDistance", json_object_new_double(so->camera.intraocularDistance));
2123  }
2124 
2125  if (so->type == VuoSceneObjectSubType_OrthographicCamera)
2126  json_object_object_add(js, "cameraWidth", json_object_new_double(so->camera.width));
2127 
2128  if (so->type == VuoSceneObjectSubType_FisheyeCamera)
2129  {
2130  json_object_object_add(js, "cameraVignetteWidth", json_object_new_double(so->camera.vignetteWidth));
2131  json_object_object_add(js, "cameraVignetteSharpness", json_object_new_double(so->camera.vignetteSharpness));
2132  }
2133 
2134  break;
2135  }
2136 
2137  case VuoSceneObjectSubType_AmbientLight:
2138  case VuoSceneObjectSubType_PointLight:
2139  case VuoSceneObjectSubType_Spotlight:
2140  {
2141  json_object_object_add(js, "lightColor", VuoColor_getJson(so->light.color));
2142  json_object_object_add(js, "lightBrightness", json_object_new_double(so->light.brightness));
2143 
2144  if (so->type == VuoSceneObjectSubType_PointLight
2145  || so->type == VuoSceneObjectSubType_Spotlight)
2146  {
2147  json_object_object_add(js, "lightRange", json_object_new_double(so->light.range));
2148  json_object_object_add(js, "lightSharpness", json_object_new_double(so->light.sharpness));
2149  }
2150  if (so->type == VuoSceneObjectSubType_Spotlight)
2151  json_object_object_add(js, "lightCone", json_object_new_double(so->light.cone));
2152 
2153  break;
2154  }
2155 
2156  case VuoSceneObjectSubType_Text:
2157  {
2158  if (so->text.text)
2159  json_object_object_add(js, "text", VuoText_getJson(so->text.text));
2160 
2161  json_object_object_add(js, "textFont", VuoFont_getJson(so->text.font));
2162 
2163  if (so->mesh)
2164  json_object_object_add(js, "mesh", VuoMesh_getJson(so->mesh));
2165 
2166  json_object_object_add(js, "textScaleWithScene", VuoBoolean_getJson(so->text.scaleWithScene));
2167  json_object_object_add(js, "textWrapWidth", VuoReal_getJson(so->text.wrapWidth));
2168 
2169  break;
2170  }
2171  }
2172 
2173  if (so->name)
2174  json_object_object_add(js, "name", VuoText_getJson(so->name));
2175 
2176  if (so->type != VuoSceneObjectSubType_AmbientLight)
2177  json_object_object_add(js, "transform", VuoTransform_getJson(so->transform));
2178 
2179  return js;
2180 }
2181 
2185 unsigned long VuoSceneObject_getVertexCount(const VuoSceneObject sceneObject)
2186 {
2187  if (!sceneObject)
2188  return 0;
2189 
2190  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2191  if (!so->mesh)
2192  return 0;
2193 
2194  unsigned int vertexCount;
2195  VuoMesh_getCPUBuffers(so->mesh, &vertexCount, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
2196  return vertexCount;
2197 }
2198 
2202 unsigned long VuoSceneObject_getElementCount(const VuoSceneObject sceneObject)
2203 {
2204  if (!sceneObject)
2205  return 0;
2206 
2207  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2208  if (!so->mesh)
2209  return 0;
2210 
2211  unsigned int elementCount;
2212  VuoMesh_getCPUBuffers(so->mesh, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &elementCount, nullptr);
2213  return elementCount;
2214 }
2215 
2221 void VuoSceneObject_getStatistics(const VuoSceneObject sceneObject, unsigned long *descendantCount, unsigned long *totalVertexCount, unsigned long *totalElementCount)
2222 {
2223  if (!sceneObject)
2224  return;
2225 
2226  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2227  unsigned long childObjectCount = 0;
2228  if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2229  childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2230  *descendantCount += childObjectCount;
2231  *totalVertexCount += VuoSceneObject_getVertexCount(sceneObject);
2232  *totalElementCount += VuoSceneObject_getElementCount(sceneObject);
2233 
2234  if (so->type == VuoSceneObjectSubType_Group)
2235  for (unsigned long i = 1; i <= childObjectCount; ++i)
2236  VuoSceneObject_getStatistics(VuoListGetValue_VuoSceneObject(so->childObjects, i), descendantCount, totalVertexCount, totalElementCount);
2237 }
2238 
2243 {
2244  if (!sceneObject)
2245  return nullptr;
2246 
2247  // Exploit json_object's set-containing-only-unique-items data structure.
2248  __block json_object *names = json_object_new_object();
2249  VuoSceneObject_visit(sceneObject, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2250  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2251  if (co->shader)
2252  json_object_object_add(names, co->shader->name, NULL);
2253  return true;
2254  });
2255 
2257  json_object_object_foreach(names, key, val)
2258  VuoListAppendValue_VuoText(nameList, VuoText_make(key));
2259  json_object_put(names);
2260  return nameList;
2261 }
2262 
2267 {
2268  if (!sceneObject)
2269  return strdup("no object");
2270 
2271  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2272 
2273  if (so->type == VuoSceneObjectSubType_Text)
2274  {
2275  char *fontSummary = VuoFont_getSummary(so->text.font);
2276  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);
2277  free(fontSummary);
2278  return textSummary;
2279  }
2280 
2281  if (so->type == VuoSceneObjectSubType_PerspectiveCamera
2282  || so->type == VuoSceneObjectSubType_StereoCamera
2283  || so->type == VuoSceneObjectSubType_OrthographicCamera
2284  || so->type == VuoSceneObjectSubType_FisheyeCamera)
2285  {
2286  const char *type = VuoSceneObject_cStringForType(so->type);
2287 
2288  float cameraViewValue = 0;
2289  const char *cameraViewString = "";
2290  if (so->type == VuoSceneObjectSubType_PerspectiveCamera)
2291  {
2292  cameraViewValue = so->camera.fieldOfView;
2293  cameraViewString = "° field of view";
2294  }
2295  else if (so->type == VuoSceneObjectSubType_StereoCamera)
2296  {
2297  cameraViewValue = so->camera.fieldOfView;
2298  cameraViewString = "° field of view (stereoscopic)";
2299  }
2300  else if (so->type == VuoSceneObjectSubType_OrthographicCamera)
2301  {
2302  cameraViewValue = so->camera.width;
2303  cameraViewString = " unit width";
2304  }
2305  else if (so->type == VuoSceneObjectSubType_FisheyeCamera)
2306  {
2307  cameraViewValue = so->camera.fieldOfView;
2308  cameraViewString = "° field of view (fisheye)";
2309  }
2310 
2311  char *translationString = VuoPoint3d_getSummary(so->transform.translation);
2312 
2313  const char *rotationLabel;
2314  char *rotationString;
2315  if (so->transform.type == VuoTransformTypeEuler)
2316  {
2317  rotationLabel = "rotated";
2318  rotationString = VuoPoint3d_getSummary(VuoPoint3d_multiply(so->transform.rotationSource.euler, -180.f/M_PI));
2319  }
2320  else
2321  {
2322  rotationLabel = "target";
2323  rotationString = VuoPoint3d_getSummary(so->transform.rotationSource.target);
2324  }
2325 
2326  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>",
2327  type, so->name ? so->name : "",
2328  translationString,
2329  rotationLabel, rotationString,
2330  cameraViewValue, cameraViewString,
2331  so->camera.distanceMin, so->camera.distanceMax);
2332  free(rotationString);
2333  free(translationString);
2334  return valueAsString;
2335  }
2336 
2337  if (so->type == VuoSceneObjectSubType_AmbientLight
2338  || so->type == VuoSceneObjectSubType_PointLight
2339  || so->type == VuoSceneObjectSubType_Spotlight)
2340  {
2341  const char *type = VuoSceneObject_cStringForType(so->type);
2342  char *colorString = VuoColor_getShortSummary(so->light.color);
2343 
2344  char *positionRangeString;
2345  if (so->type == VuoSceneObjectSubType_PointLight
2346  || so->type == VuoSceneObjectSubType_Spotlight)
2347  {
2348  char *positionString = VuoPoint3d_getSummary(so->transform.translation);
2349 
2350  positionRangeString = VuoText_format("<div>position (%s)</div><div>range %g units (%g sharpness)</div>",
2351  positionString, so->light.range, so->light.sharpness);
2352 
2353  free(positionString);
2354  }
2355  else
2356  positionRangeString = strdup("");
2357 
2358  char *directionConeString;
2359  if (so->type == VuoSceneObjectSubType_Spotlight)
2360  {
2361  VuoPoint3d direction = VuoTransform_getDirection(so->transform);
2362  char *directionString = VuoPoint3d_getSummary(direction);
2363 
2364  directionConeString = VuoText_format("<div>direction (%s)</div><div>cone %g°</div>",
2365  directionString, so->light.cone * 180./M_PI);
2366 
2367  free(directionString);
2368  }
2369  else
2370  directionConeString = strdup("");
2371 
2372  char *valueAsString = VuoText_format("<div>%s</div><div>color %s</div><div>brightness %g</div>%s%s",
2373  type, colorString, so->light.brightness, positionRangeString, directionConeString);
2374 
2375  free(directionConeString);
2376  free(positionRangeString);
2377  free(colorString);
2378 
2379  return valueAsString;
2380  }
2381 
2382  unsigned long vertexCount = VuoSceneObject_getVertexCount(sceneObject);
2383  unsigned long elementCount = VuoSceneObject_getElementCount(sceneObject);
2384 
2385  char *transform = VuoTransform_getSummary(so->transform);
2386 
2387  unsigned long childObjectCount = 0;
2388  if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2389  childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2390  const char *childObjectPlural = childObjectCount == 1 ? "" : "s";
2391 
2392  char *descendants;
2393  if (childObjectCount)
2394  {
2395  unsigned long descendantCount = 0;
2396  unsigned long totalVertexCount = 0;
2397  unsigned long totalElementCount = 0;
2398  VuoSceneObject_getStatistics(sceneObject, &descendantCount, &totalVertexCount, &totalElementCount);
2399  const char *descendantPlural = descendantCount == 1 ? "" : "s";
2400 
2401  descendants = VuoText_format("<div>%ld descendant%s</div><div>total, including descendants:</div><div>%ld vertices, %ld elements</div>",
2402  descendantCount, descendantPlural, totalVertexCount, totalElementCount);
2403  }
2404  else
2405  descendants = strdup("");
2406 
2407  VuoList_VuoText shaderNames = VuoSceneObject_findShaderNames(sceneObject);
2408  VuoRetain(shaderNames);
2409  char *shaderNamesSummary;
2410  if (VuoListGetCount_VuoText(shaderNames))
2411  {
2412  VuoInteger shaderNameCount = VuoListGetCount_VuoText(shaderNames);
2413  const char *header = "<div>shaders:<ul>";
2414  VuoInteger shaderNameLength = strlen(header);
2415  for (VuoInteger i = 1; i <= shaderNameCount; ++i)
2416  shaderNameLength += strlen("<li>") + strlen(VuoListGetValue_VuoText(shaderNames, i)) + strlen("</li>");
2417  shaderNameLength += strlen("</ul></div>");
2418 
2419  shaderNamesSummary = (char *)malloc(shaderNameLength + 1);
2420  char *t = shaderNamesSummary;
2421  t = strcpy(t, header) + strlen(header);
2422  for (VuoInteger i = 1; i <= shaderNameCount; ++i)
2423  {
2424  t = strcpy(t, "<li>") + strlen("<li>");
2425  t = strcpy(t, VuoListGetValue_VuoText(shaderNames, i)) + strlen(VuoListGetValue_VuoText(shaderNames, i));
2426  t = strcpy(t, "</li>") + strlen("</li>");
2427  }
2428  t = strcpy(t, "</ul></div>");
2429  }
2430  else
2431  shaderNamesSummary = strdup("");
2432  VuoRelease(shaderNames);
2433 
2434  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",
2435  so->name ? so->name : "",
2436  vertexCount, elementCount,
2437  transform,
2438  so->id,
2439  childObjectCount, childObjectPlural,
2440  descendants, shaderNamesSummary);
2441 
2442  free(descendants);
2443  free(transform);
2444  free(shaderNamesSummary);
2445 
2446  return valueAsString;
2447 }
2448 
2452 static void VuoSceneObject_dump_internal(const VuoSceneObject sceneObject, unsigned int level)
2453 {
2454  VuoSceneObject_internal *so = (VuoSceneObject_internal *)sceneObject;
2455 
2456  for (unsigned int i=0; i<level; ++i)
2457  fprintf(stderr, "\t");
2458 
2459  if (!sceneObject)
2460  {
2461  fprintf(stderr, "no object\n");
2462  return;
2463  }
2464 
2465  fprintf(stderr, "%s (%s) ", VuoSceneObject_cStringForType(so->type), VuoTransform_getSummary(so->transform));
2466  if (so->type == VuoSceneObjectSubType_Mesh)
2467  fprintf(stderr, "%lu vertices, %lu elements, shader '%s' (%p)", VuoSceneObject_getVertexCount(sceneObject), VuoSceneObject_getElementCount(sceneObject), so->shader ? so->shader->name : "", so->shader);
2468  fprintf(stderr, "\n");
2469 
2470  if (so->type == VuoSceneObjectSubType_Group && so->childObjects)
2471  {
2472  unsigned int childObjectCount = VuoListGetCount_VuoSceneObject(so->childObjects);
2473  for (unsigned int i=1; i<=childObjectCount; ++i)
2474  VuoSceneObject_dump_internal(VuoListGetValue_VuoSceneObject(so->childObjects, i), level+1);
2475  }
2476 }
2477 
2482 {
2484 }
2485 
2496 VuoSceneObject VuoSceneObject_flatten(const VuoSceneObject so, bool calculateTangents)
2497 {
2498  if (!so)
2499  return nullptr;
2500 
2501  // Count the vertices.
2502 
2503  __block unsigned long triangleVertexCount = 0;
2504  __block unsigned long trianglePrimitiveCount = 0;
2505  __block VuoMesh_FaceCulling triangleFaceCulling = VuoMesh_CullBackfaces;
2506 
2507  __block unsigned long lineVertexCount = 0;
2508  __block unsigned long linePrimitiveCount = 0;
2509  __block double linePrimitiveSize = 0;
2510 
2511  __block unsigned long pointVertexCount = 0;
2512  __block unsigned long pointPrimitiveCount = 0;
2513  __block double pointPrimitiveSize = 0;
2514 
2515  VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2516  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2517  if (!co->mesh)
2518  return true;
2519 
2520  VuoMesh_ElementAssemblyMethod elementAssemblyMethod = VuoMesh_getElementAssemblyMethod(co->mesh);
2521 
2522  unsigned int vertexCount;
2523  VuoMesh_getCPUBuffers(co->mesh, &vertexCount, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
2524 
2525  if (elementAssemblyMethod == VuoMesh_IndividualTriangles
2526  || elementAssemblyMethod == VuoMesh_TriangleStrip
2527  || elementAssemblyMethod == VuoMesh_TriangleFan)
2528  {
2529  triangleVertexCount += vertexCount;
2530  trianglePrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2531  triangleFaceCulling = VuoMesh_getFaceCulling(co->mesh);
2532  }
2533  else if (elementAssemblyMethod == VuoMesh_IndividualLines
2534  || elementAssemblyMethod == VuoMesh_LineStrip)
2535  {
2536  lineVertexCount += vertexCount;
2537  linePrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2538  linePrimitiveSize = VuoMesh_getPrimitiveSize(co->mesh);
2539  }
2540  else if (elementAssemblyMethod == VuoMesh_Points)
2541  {
2542  pointVertexCount += vertexCount;
2543  pointPrimitiveCount += VuoMesh_getSplitPrimitiveCount(co->mesh);
2544  pointPrimitiveSize = VuoMesh_getPrimitiveSize(co->mesh);
2545  }
2546 
2547  return true;
2548  });
2549 // VLog("triangles: %ldv %ldp lines: %ldv %ldp points: %ldv %ldp", triangleVertexCount, trianglePrimitiveCount, lineVertexCount, linePrimitiveCount, pointVertexCount, pointPrimitiveCount);
2550 
2551  if (!trianglePrimitiveCount && !linePrimitiveCount && !pointPrimitiveCount)
2552  return nullptr;
2553 
2554  // Allocate the buffers.
2555  unsigned int *triangleElements = nullptr;
2556  VuoPoint4d *trianglePositions = nullptr, *triangleNormals = nullptr, *triangleTextureCoordinates = nullptr;
2557  unsigned int *lineElements = nullptr;
2558  VuoPoint4d *linePositions = nullptr, *lineNormals = nullptr, *lineTextureCoordinates = nullptr;
2559  unsigned int *pointElements = nullptr;
2560  VuoPoint4d *pointPositions = nullptr, *pointNormals = nullptr, *pointTextureCoordinates = nullptr;
2561  if (trianglePrimitiveCount)
2562  VuoMesh_allocateCPUBuffers(triangleVertexCount, &trianglePositions, &triangleNormals, nullptr, nullptr, &triangleTextureCoordinates, trianglePrimitiveCount * 3, &triangleElements);
2563  if (linePrimitiveCount)
2564  VuoMesh_allocateCPUBuffers(lineVertexCount, &linePositions, &lineNormals, nullptr, nullptr, &lineTextureCoordinates, linePrimitiveCount * 2, &lineElements);
2565  if (pointPrimitiveCount)
2566  VuoMesh_allocateCPUBuffers(pointVertexCount, &pointPositions, &pointNormals, nullptr, nullptr, &pointTextureCoordinates, pointPrimitiveCount, &pointElements);
2567 
2568  // Copy the vertex attributes.
2569  __block unsigned long triangleVertexIndex = 0;
2570  __block unsigned long triangleElementIndex = 0;
2571  __block unsigned long lineVertexIndex = 0;
2572  __block unsigned long lineElementIndex = 0;
2573  __block unsigned long pointVertexIndex = 0;
2574  __block unsigned long pointElementIndex = 0;
2575  __block bool anyTextureCoordinates = false;
2576  VuoSceneObject_visit(so, ^(const VuoSceneObject currentObject, float modelviewMatrix[16]){
2577  VuoSceneObject_internal *co = (VuoSceneObject_internal *)currentObject;
2578  if (!co->mesh)
2579  return true;
2580 
2581  VuoMesh_ElementAssemblyMethod elementAssemblyMethod = VuoMesh_getElementAssemblyMethod(co->mesh);
2582 
2583  unsigned int vertexCount, elementCount, *elements;
2584  VuoPoint4d *positions, *normals, *textureCoordinates;
2585  VuoMesh_getCPUBuffers(co->mesh, &vertexCount, &positions, &normals, nullptr, nullptr, &textureCoordinates, &elementCount, &elements);
2586 
2587  if (textureCoordinates)
2588  anyTextureCoordinates = true;
2589 
2590  if (elementAssemblyMethod == VuoMesh_IndividualTriangles
2591  || elementAssemblyMethod == VuoMesh_TriangleStrip
2592  || elementAssemblyMethod == VuoMesh_TriangleFan)
2593  {
2594  unsigned long indexOffset = triangleVertexIndex;
2595  for (unsigned int n = 0; n < vertexCount; ++n)
2596  {
2597  VuoPoint3d p = positions[n].xyz;
2598  VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2599  trianglePositions[triangleVertexIndex] = (VuoPoint4d){pt.x, pt.y, pt.z, 1};
2600 
2601  if (normals)
2602  {
2603  VuoPoint3d r = normals[n].xyz;
2604  VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2605  triangleNormals[triangleVertexIndex] = (VuoPoint4d){rt.x, rt.y, rt.z, 1};
2606  }
2607 
2608  if (textureCoordinates)
2609  triangleTextureCoordinates[triangleVertexIndex] = textureCoordinates[n];
2610 
2611  ++triangleVertexIndex;
2612  }
2613 
2614  if (elementAssemblyMethod == VuoMesh_IndividualTriangles)
2615  {
2616  if (elementCount)
2617  for (unsigned int n = 0; n < elementCount; ++n)
2618  triangleElements[triangleElementIndex++] = indexOffset + elements[n];
2619  else
2620  for (unsigned int n = 0; n < vertexCount; ++n)
2621  triangleElements[triangleElementIndex++] = indexOffset + n;
2622  }
2623  else if (elementAssemblyMethod == VuoMesh_TriangleStrip)
2624  {
2625  // Expand the triangle strip to individual triangles.
2626  if (elementCount)
2627  for (unsigned int n = 2; n < elementCount; ++n)
2628  if (n%2 == 0)
2629  {
2630  triangleElements[triangleElementIndex++] = indexOffset + elements[n-2];
2631  triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2632  triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2633  }
2634  else
2635  {
2636  triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2637  triangleElements[triangleElementIndex++] = indexOffset + elements[n-2];
2638  triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2639  }
2640  else
2641  for (unsigned int n = 0; n < vertexCount; ++n)
2642  if (n%2 == 0)
2643  {
2644  triangleElements[triangleElementIndex++] = indexOffset + n-2;
2645  triangleElements[triangleElementIndex++] = indexOffset + n-1;
2646  triangleElements[triangleElementIndex++] = indexOffset + n ;
2647  }
2648  else
2649  {
2650  triangleElements[triangleElementIndex++] = indexOffset + n-1;
2651  triangleElements[triangleElementIndex++] = indexOffset + n-2;
2652  triangleElements[triangleElementIndex++] = indexOffset + n ;
2653  }
2654  }
2655  else if (elementAssemblyMethod == VuoMesh_TriangleFan)
2656  {
2657  // Expand the triangle fan to individual triangles.
2658  if (elementCount)
2659  for (unsigned int n = 2; n < elementCount; ++n)
2660  {
2661  triangleElements[triangleElementIndex++] = indexOffset + elements[0 ];
2662  triangleElements[triangleElementIndex++] = indexOffset + elements[n-1];
2663  triangleElements[triangleElementIndex++] = indexOffset + elements[n ];
2664  }
2665  else
2666  for (unsigned int n = 2; n < vertexCount; ++n)
2667  {
2668  triangleElements[triangleElementIndex++] = indexOffset + 0;
2669  triangleElements[triangleElementIndex++] = indexOffset + n-1;
2670  triangleElements[triangleElementIndex++] = indexOffset + n;
2671  }
2672  }
2673  }
2674  else if (elementAssemblyMethod == VuoMesh_IndividualLines
2675  || elementAssemblyMethod == VuoMesh_LineStrip)
2676  {
2677  unsigned long indexOffset = lineVertexIndex;
2678  for (unsigned int n = 0; n < vertexCount; ++n)
2679  {
2680  VuoPoint3d p = positions[n].xyz;
2681  VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2682  linePositions[lineVertexIndex] = (VuoPoint4d){pt.x, pt.y, pt.z, 1};
2683 
2684  if (normals)
2685  {
2686  VuoPoint3d r = normals[n].xyz;
2687  VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2688  lineNormals[lineVertexIndex] = (VuoPoint4d){rt.x, rt.y, rt.z, 1};
2689  }
2690 
2691  if (textureCoordinates)
2692  lineTextureCoordinates[lineVertexIndex] = textureCoordinates[n];
2693 
2694  ++lineVertexIndex;
2695  }
2696 
2697  if (elementAssemblyMethod == VuoMesh_IndividualLines)
2698  {
2699  if (elementCount)
2700  for (unsigned int n = 0; n < elementCount; ++n)
2701  lineElements[lineElementIndex++] = indexOffset + elements[n];
2702  else
2703  for (unsigned int n = 0; n < vertexCount; ++n)
2704  lineElements[lineElementIndex++] = indexOffset + n;
2705  }
2706  else if (elementAssemblyMethod == VuoMesh_LineStrip)
2707  {
2708  // Expand the line strip to individual lines.
2709  if (elementCount)
2710  for (unsigned int n = 1; n < elementCount; ++n)
2711  {
2712  lineElements[lineElementIndex++] = indexOffset + elements[n-1];
2713  lineElements[lineElementIndex++] = indexOffset + elements[n ];
2714  }
2715  else
2716  for (unsigned int n = 1; n < vertexCount; ++n)
2717  {
2718  lineElements[lineElementIndex++] = indexOffset + n-1;
2719  lineElements[lineElementIndex++] = indexOffset + n;
2720  }
2721  }
2722  }
2723  else if (elementAssemblyMethod == VuoMesh_Points)
2724  {
2725  unsigned long indexOffset = pointVertexIndex;
2726  for (unsigned int n = 0; n < vertexCount; ++n)
2727  {
2728  VuoPoint3d p = positions[n].xyz;
2729  VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
2730  pointPositions[pointVertexIndex] = (VuoPoint4d){pt.x, pt.y, pt.z, 1};
2731 
2732  if (normals)
2733  {
2734  VuoPoint3d r = normals[n].xyz;
2735  VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
2736  pointNormals[pointVertexIndex] = (VuoPoint4d){rt.x, rt.y, rt.z, 1};
2737  }
2738 
2739  if (textureCoordinates)
2740  pointTextureCoordinates[pointVertexIndex] = textureCoordinates[n];
2741 
2742  ++pointVertexIndex;
2743  }
2744 
2745  if (elementCount)
2747  for (unsigned int n = 0; n < elementCount; ++n)
2748  pointElements[pointElementIndex++] = indexOffset + elements[n];
2749  else
2750  {
2751  for (unsigned int n = 0; n < vertexCount; ++n)
2752  pointElements[pointElementIndex++] = indexOffset + n;
2753  }
2754  }
2755 
2756  return true;
2757  });
2758 
2759 
2760  VuoMesh triangleMesh = nullptr;
2761  VuoMesh lineMesh = nullptr;
2762  VuoMesh pointMesh = nullptr;
2763  if (trianglePrimitiveCount)
2764  {
2765  if (!anyTextureCoordinates)
2766  {
2767  free(triangleTextureCoordinates);
2768  triangleTextureCoordinates = nullptr;
2769  }
2770  triangleMesh = VuoMesh_makeFromCPUBuffers(triangleVertexCount, trianglePositions, triangleNormals, nullptr, nullptr, triangleTextureCoordinates, trianglePrimitiveCount * 3, triangleElements, VuoMesh_IndividualTriangles);
2771  VuoMesh_setFaceCulling(triangleMesh, triangleFaceCulling);
2772  if (calculateTangents)
2773  VuoMeshUtility_calculateTangents(triangleMesh);
2774  }
2775  if (linePrimitiveCount)
2776  {
2777  if (!anyTextureCoordinates)
2778  {
2779  free(lineTextureCoordinates);
2780  lineTextureCoordinates = nullptr;
2781  }
2782  lineMesh = VuoMesh_makeFromCPUBuffers(lineVertexCount, linePositions, lineNormals, nullptr, nullptr, lineTextureCoordinates, linePrimitiveCount * 2, lineElements, VuoMesh_IndividualLines);
2783  VuoMesh_setPrimitiveSize(lineMesh, linePrimitiveSize);
2784  }
2785  if (pointPrimitiveCount)
2786  {
2787  if (!anyTextureCoordinates)
2788  {
2789  free(pointTextureCoordinates);
2790  pointTextureCoordinates = nullptr;
2791  }
2792  pointMesh = VuoMesh_makeFromCPUBuffers(pointVertexCount, pointPositions, pointNormals, nullptr, nullptr, pointTextureCoordinates, pointPrimitiveCount, pointElements, VuoMesh_Points);
2793  VuoMesh_setPrimitiveSize(pointMesh, pointPrimitiveSize);
2794  }
2795 
2796  if (triangleMesh && !lineMesh && !pointMesh)
2797  return VuoSceneObject_makeMesh(triangleMesh, NULL, VuoTransform_makeIdentity());
2798  else if (!triangleMesh && lineMesh && !pointMesh)
2799  return VuoSceneObject_makeMesh(lineMesh, NULL, VuoTransform_makeIdentity());
2800  else if (!triangleMesh && !lineMesh && pointMesh)
2801  return VuoSceneObject_makeMesh(pointMesh, NULL, VuoTransform_makeIdentity());
2802  else
2803  {
2805  if (triangleMesh)
2807  if (lineMesh)
2809  if (pointMesh)
2811  return VuoSceneObject_makeGroup(childObjects, VuoTransform_makeIdentity());
2812  }
2813 }
2814 
2815 #define CSGJS_HEADER_ONLY
2816 #include "csgjs.cc"
2817 
2821 static csgjs_model VuoSceneObject_getCsgjsModel(const VuoSceneObject so)
2822 {
2823  if (!so)
2824  return csgjs_model();
2825 
2826  VuoSceneObject flat = VuoSceneObject_flatten(so, false);
2827  VuoSceneObject_internal *f = (VuoSceneObject_internal *)flat;
2828  if (!f->mesh)
2829  return csgjs_model();
2830 
2831  VuoLocal(flat);
2832 
2834  return csgjs_model();
2835 
2836  unsigned int vertexCount, elementCount, *elements;
2837  VuoPoint4d *positions, *normals, *textureCoordinates;
2838  VuoMesh_getCPUBuffers(f->mesh, &vertexCount, &positions, &normals, nullptr, nullptr, &textureCoordinates, &elementCount, &elements);
2839 
2840  csgjs_model cm;
2841  for (unsigned int n = 0; n < vertexCount; ++n)
2842  {
2843  csgjs_vertex v;
2844  v.pos = csgjs_vector(positions[n].x, positions[n].y, positions[n].z);
2845  if (normals)
2846  v.normal = csgjs_vector(normals[n].x, normals[n].y, normals[n].z);
2847  if (textureCoordinates)
2848  v.uv = csgjs_vector(textureCoordinates[n].x, textureCoordinates[n].y, 0);
2849  cm.vertices.push_back(v);
2850  }
2851  for (unsigned int n = 0; n < elementCount; ++n)
2852  cm.indices.push_back(elements[n]);
2853 
2854  return cm;
2855 }
2856 
2861 {
2862  unsigned int vertexCount = cm.vertices.size();
2863  unsigned int elementCount = cm.indices.size();
2864  unsigned int *elements;
2865  VuoPoint4d *positions, *normals, *textureCoordinates;
2866  VuoMesh_allocateCPUBuffers(vertexCount, &positions, &normals, nullptr, nullptr, &textureCoordinates, elementCount, &elements);
2867 
2868  const csgjs_vertex *vertex = &cm.vertices[0];
2869  for (unsigned int n = 0; n < vertexCount; ++n)
2870  {
2871  positions[n] = (VuoPoint4d){vertex[n].pos.x,vertex[n].pos.y,vertex[n].pos.z,1};
2872  normals[n] = (VuoPoint4d){vertex[n].normal.x,vertex[n].normal.y,vertex[n].normal.z,1};
2873  textureCoordinates[n] = (VuoPoint4d){vertex[n].uv.x,vertex[n].uv.y,vertex[n].uv.z,1};
2874  }
2875 
2876  const int *index = &cm.indices[0];
2877  for (unsigned int n = 0; n < elementCount; ++n)
2878  elements[n] = index[n];
2879 
2880  VuoMesh mesh = VuoMesh_makeFromCPUBuffers(vertexCount, positions, normals, nullptr, nullptr, textureCoordinates, elementCount, elements, VuoMesh_IndividualTriangles);
2881 
2883 
2884  return VuoSceneObject_makeMesh(mesh, NULL, VuoTransform_makeIdentity());
2885 }
2886 
2891 static float convertQualityToEpsilon(float quality)
2892 {
2893  return pow(10, -VuoReal_clamp(quality, 0, 1) * 5.);
2894 }
2895 
2900 {
2901  float epsilon = convertQualityToEpsilon(quality);
2902 
2903  unsigned long objectCount = VuoListGetCount_VuoSceneObject(objects);
2904  if (objectCount == 0)
2905  return nullptr;
2906  if (objectCount == 1)
2907  return VuoListGetValue_VuoSceneObject(objects, 1);
2908 
2909  dispatch_queue_t queue = dispatch_queue_create("org.vuo.sceneobject.union", DISPATCH_QUEUE_CONCURRENT);
2910 
2911  csgjs_model *models = new csgjs_model[objectCount];
2912  for (unsigned long i = 0; i < objectCount; ++i)
2913  dispatch_async(queue, ^{
2915  });
2916 
2917  dispatch_barrier_sync(queue, ^{});
2918  dispatch_release(queue);
2919 
2920  csgjs_model cu = models[0];
2921  for (unsigned long i = 1; i < objectCount; ++i)
2922  cu = csgjs_union(cu, models[i], epsilon);
2923  delete[] models;
2925 }
2926 
2931 {
2932  float epsilon = convertQualityToEpsilon(quality);
2933 
2934  dispatch_semaphore_t finished = dispatch_semaphore_create(0);
2935 
2936  __block csgjs_model ca;
2937  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2939  dispatch_semaphore_signal(finished);
2940  });
2941 
2942  csgjs_model cb = VuoSceneObject_getCsgjsModel(b);
2943 
2944  dispatch_semaphore_wait(finished, DISPATCH_TIME_FOREVER);
2945  dispatch_release(finished);
2946 
2947  csgjs_model d = csgjs_difference(ca, cb, epsilon);
2949 }
2950 
2955 {
2956  float epsilon = convertQualityToEpsilon(quality);
2957 
2958  unsigned long objectCount = VuoListGetCount_VuoSceneObject(objects);
2959  if (objectCount == 0)
2960  return nullptr;
2961  if (objectCount == 1)
2962  return VuoListGetValue_VuoSceneObject(objects, 1);
2963 
2964  dispatch_queue_t queue = dispatch_queue_create("org.vuo.sceneobject.intersect", DISPATCH_QUEUE_CONCURRENT);
2965 
2966  csgjs_model *models = new csgjs_model[objectCount];
2967  for (unsigned long i = 0; i < objectCount; ++i)
2968  dispatch_async(queue, ^{
2970  });
2971 
2972  dispatch_barrier_sync(queue, ^{});
2973  dispatch_release(queue);
2974 
2975  csgjs_model ci = models[0];
2976  for (unsigned long i = 1; i < objectCount; ++i)
2977  ci = csgjs_intersection(ci, models[i], epsilon);
2978  delete[] models;
2980 }