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 
52 {
53  static uint64_t id = 0;
54  return __sync_add_and_fetch(&id, 1);
55 }
56 
61 {
63 
64  o.id = 0;
65  o.type = VuoSceneObjectSubType_Empty;
66 
67  o.mesh = NULL;
68  o.shader = NULL;
69  o.isRealSize = false;
70  o.preservePhysicalSize = false;
71  o.blendMode = VuoBlendMode_Normal;
72 
73  o.childObjects = NULL;
74 
75  o.name = NULL;
76  o.transform = VuoTransform_makeIdentity();
77 
78  o.text = NULL;
79  o.font = (VuoFont){NULL, 0, false, (VuoColor){0,0,0,0}, VuoHorizontalAlignment_Left, 0, 0};
80  o.scaleWithScene = false;
81  o.wrapWidth = INFINITY;
82 
83  return o;
84 }
85 
90 {
92 
93  o.id = 0;
94  o.type = VuoSceneObjectSubType_Group;
95 
96  o.mesh = NULL;
97  o.shader = NULL;
98  o.isRealSize = false;
99  o.preservePhysicalSize = false;
100  o.blendMode = VuoBlendMode_Normal;
101 
102  o.childObjects = childObjects;
103 
104  o.name = NULL;
105  o.transform = transform;
106 
107  o.text = NULL;
108  o.font = (VuoFont){NULL, 0, false, (VuoColor){0,0,0,0}, VuoHorizontalAlignment_Left, 0, 0};
109  o.scaleWithScene = false;
110  o.wrapWidth = INFINITY;
111 
112  return o;
113 }
114 
119 {
120  VuoSceneObject o;
121 
122  o.id = 0;
123  o.type = VuoSceneObjectSubType_Mesh;
124 
125  o.mesh = mesh;
126 
127  if (mesh && !shader)
128  o.shader = VuoShader_makeDefaultShader();
129  else
130  o.shader = shader;
131 
132  o.isRealSize = false;
133  o.preservePhysicalSize = false;
134  o.blendMode = VuoBlendMode_Normal;
135 
136  o.childObjects = childObjects;
137 
138  o.name = NULL;
139 
140  o.transform = transform;
141 
142  o.text = NULL;
143  o.font = (VuoFont){NULL, 0, false, (VuoColor){0,0,0,0}, VuoHorizontalAlignment_Left, 0, 0};
144  o.scaleWithScene = false;
145  o.wrapWidth = INFINITY;
146 
147  return o;
148 }
149 
164 VuoSceneObject VuoSceneObject_makeQuad(VuoShader shader, VuoPoint3d center, VuoPoint3d rotation, VuoReal width, VuoReal height)
165 {
166  return VuoSceneObject_make(
168  shader,
170  center,
171  VuoPoint3d_multiply(rotation, M_PI/180.),
172  VuoPoint3d_make(width,height,1)
173  ),
174  NULL
175  );
176 }
177 
192 VuoSceneObject VuoSceneObject_makeQuadWithNormals(VuoShader shader, VuoPoint3d center, VuoPoint3d rotation, VuoReal width, VuoReal height)
193 {
194  return VuoSceneObject_make(
196  shader,
198  center,
199  VuoPoint3d_multiply(rotation, M_PI/180.),
200  VuoPoint3d_make(width,height,1)
201  ),
202  NULL
203  );
204 }
205 
211 VuoSceneObject VuoSceneObject_makeImage(VuoImage image, VuoPoint3d center, VuoPoint3d rotation, VuoReal width, VuoReal alpha)
212 {
213  if (!image)
214  return VuoSceneObject_makeEmpty();
215 
217  VuoShader_makeUnlitImageShader(image, alpha),
218  center,
219  rotation,
220  width,
221  image->pixelsHigh * width/image->pixelsWide
222  );
223 
224  return object;
225 }
226 
232 VuoSceneObject VuoSceneObject_makeLitImage(VuoImage image, VuoPoint3d center, VuoPoint3d rotation, VuoReal width, VuoReal alpha, VuoColor highlightColor, VuoReal shininess)
233 {
234  if (!image)
235  return VuoSceneObject_makeEmpty();
236 
238  VuoShader_makeLitImageShader(image, alpha, highlightColor, shininess),
239  center,
240  rotation,
241  width,
242  image->pixelsHigh * width/image->pixelsWide
243  );
244 }
245 
249 VuoSceneObject VuoSceneObject_makeCube(VuoTransform transform, VuoShader frontShader, VuoShader leftShader, VuoShader rightShader, VuoShader backShader, VuoShader topShader, VuoShader bottomShader)
250 {
252 
253  VuoMesh quadMesh = VuoMesh_makeQuad();
254 
255  // Front Face
256  {
258  quadMesh,
259  frontShader,
261  NULL
262  );
263  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
264  }
265 
266  // Left Face
267  {
269  quadMesh,
270  leftShader,
272  NULL
273  );
274  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
275  }
276 
277  // Right Face
278  {
280  quadMesh,
281  rightShader,
283  NULL
284  );
285  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
286  }
287 
288  // Back Face
289  {
291  quadMesh,
292  backShader,
294  NULL
295  );
296  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
297  }
298 
299  // Top Face
300  {
302  quadMesh,
303  topShader,
305  NULL
306  );
307  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
308  }
309 
310  // Bottom Face
311  {
313  quadMesh,
314  bottomShader,
316  NULL
317  );
318  VuoListAppendValue_VuoSceneObject(cubeChildObjects, so);
319  }
320 
321  return VuoSceneObject_make(NULL, NULL, transform, cubeChildObjects);
322 }
323 
324 
329 {
330  return VuoSceneObject_make(VuoMesh_makeCube(), shader, transform, NULL);
331 }
332 
341 VuoSceneObject VuoSceneObject_makeText(VuoText text, VuoFont font, VuoBoolean scaleWithScene, float wrapWidth)
342 {
344  o.type = VuoSceneObjectSubType_Text;
345  o.text = text;
346  o.font = font;
347  o.scaleWithScene = scaleWithScene;
348  o.wrapWidth = wrapWidth;
349  return o;
350 }
351 
355 VuoSceneObject VuoSceneObject_makePerspectiveCamera(VuoText name, VuoTransform transform, float fieldOfView, float distanceMin, float distanceMax)
356 {
358  o.type = VuoSceneObjectSubType_PerspectiveCamera;
359  o.name = name;
360  o.transform = transform;
361  o.cameraFieldOfView = fieldOfView;
362  o.cameraDistanceMin = distanceMin;
363  o.cameraDistanceMax = distanceMax;
364  return o;
365 }
366 
370 VuoSceneObject VuoSceneObject_makeStereoCamera(VuoText name, VuoTransform transform, VuoReal fieldOfView, VuoReal distanceMin, VuoReal distanceMax, VuoReal confocalDistance, VuoReal intraocularDistance)
371 {
373  o.type = VuoSceneObjectSubType_StereoCamera;
374  o.name = name;
375  o.transform = transform;
376  o.cameraFieldOfView = fieldOfView;
377  o.cameraDistanceMin = distanceMin;
378  o.cameraDistanceMax = distanceMax;
379  o.cameraConfocalDistance = confocalDistance;
380  o.cameraIntraocularDistance = intraocularDistance;
381  return o;
382 }
383 
387 VuoSceneObject VuoSceneObject_makeOrthographicCamera(VuoText name, VuoTransform transform, float width, float distanceMin, float distanceMax)
388 {
390  o.type = VuoSceneObjectSubType_OrthographicCamera;
391  o.name = name;
392  o.transform = transform;
393  o.cameraWidth = width;
394  o.cameraDistanceMin = distanceMin;
395  o.cameraDistanceMax = distanceMax;
396  return o;
397 }
398 
402 VuoSceneObject VuoSceneObject_makeFisheyeCamera(VuoText name, VuoTransform transform, VuoReal fieldOfView, VuoReal vignetteWidth, VuoReal vignetteSharpness)
403 {
405  o.type = VuoSceneObjectSubType_FisheyeCamera;
406  o.name = name;
407  o.transform = transform;
408  o.cameraFieldOfView = fieldOfView;
409 
410  // 0 and 1000 come from "Realtime Dome Imaging and Interaction" by Bailey/Clothier/Gebbie 2006.
411  o.cameraDistanceMin = 0;
412  o.cameraDistanceMax = 1000;
413 
414  o.cameraVignetteWidth = vignetteWidth;
415  o.cameraVignetteSharpness = vignetteSharpness;
416 
417  return o;
418 }
419 
424 {
426  VuoPoint3d_make(0,0,1),
427  VuoPoint3d_make(0,0,0),
428  VuoPoint3d_make(1,1,1)
429  );
431  VuoText_make("default camera"),
432  transform,
433  90,
434  0.1,
435  10.0
436  );
437 }
438 
448 bool VuoSceneObject_find(VuoSceneObject so, VuoText nameToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
449 {
450  if (VuoText_areEqual(so.name, nameToMatch))
451  {
452  *foundObject = so;
453  return true;
454  }
455 
456  VuoListAppendValue_VuoSceneObject(ancestorObjects, so);
457 
458  unsigned long childObjectCount = (so.childObjects ? VuoListGetCount_VuoSceneObject(so.childObjects) : 0);
459  for (unsigned long i = 1; i <= childObjectCount; ++i)
460  {
461  VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so.childObjects, i);
462  if (VuoSceneObject_find(childObject, nameToMatch, ancestorObjects, foundObject))
463  return true;
464  }
465 
466  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
467 
468  return false;
469 }
470 
481 bool VuoSceneObject_findById(VuoSceneObject so, uint64_t idToMatch, VuoList_VuoSceneObject ancestorObjects, VuoSceneObject *foundObject)
482 {
483  if (so.id == idToMatch)
484  {
485  *foundObject = so;
486  return true;
487  }
488 
489  VuoListAppendValue_VuoSceneObject(ancestorObjects, so);
490 
491  unsigned long childObjectCount = (so.childObjects ? VuoListGetCount_VuoSceneObject(so.childObjects) : 0);
492  for (unsigned long i = 1; i <= childObjectCount; ++i)
493  {
494  VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so.childObjects, i);
495  if (VuoSceneObject_findById(childObject, idToMatch, ancestorObjects, foundObject))
496  return true;
497  }
498 
499  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
500 
501  return false;
502 }
503 
515 {
516  if (so.type == typeToMatch)
517  {
518  *foundObject = so;
519  return true;
520  }
521 
522  VuoListAppendValue_VuoSceneObject(ancestorObjects, so);
523 
524  unsigned long childObjectCount = (so.childObjects ? VuoListGetCount_VuoSceneObject(so.childObjects) : 0);
525 
526  for (unsigned long i = 1; i <= childObjectCount; ++i)
527  {
528  VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so.childObjects, i);
529 
530  if (VuoSceneObject_findWithType(childObject, typeToMatch, ancestorObjects, foundObject))
531  return true;
532  }
533 
534  VuoListRemoveLastValue_VuoSceneObject(ancestorObjects);
535 
536  return false;
537 }
538 
548 {
549  __block bool didFindCamera = false;
550  VuoSceneObject_visit(so, ^(const VuoSceneObject *currentObject, float modelviewMatrix[16]){
551  if ((currentObject->type == VuoSceneObjectSubType_PerspectiveCamera
552  || currentObject->type == VuoSceneObjectSubType_StereoCamera
553  || currentObject->type == VuoSceneObjectSubType_OrthographicCamera
554  || currentObject->type == VuoSceneObjectSubType_FisheyeCamera)
555  && (!nameToMatch || (currentObject->name && nameToMatch && strstr(currentObject->name, nameToMatch))))
556  {
557  *foundCamera = *currentObject;
558  foundCamera->transform = VuoTransform_makeFromMatrix4x4(modelviewMatrix);
559  didFindCamera = true;
560  return false;
561  }
562  return true;
563  });
564 
565  return didFindCamera;
566 }
567 
574 {
575  if (so.type != VuoSceneObjectSubType_Empty)
576  return true;
577 
578  if (so.childObjects)
579  {
580  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(so.childObjects);
581  for (unsigned long i = 1; i <= childObjectCount; ++i)
582  {
583  VuoSceneObject childObject = VuoListGetValue_VuoSceneObject(so.childObjects, i);
584  if (VuoSceneObject_isPopulated(childObject))
585  return true;
586  }
587  }
588 
589  return false;
590 }
591 
596 {
597  if (strcmp(typeString,"empty")==0)
598  return VuoSceneObjectSubType_Empty;
599  else if (strcmp(typeString,"group")==0)
600  return VuoSceneObjectSubType_Group;
601  else if (strcmp(typeString,"mesh")==0)
602  return VuoSceneObjectSubType_Mesh;
603  else if (strcmp(typeString,"camera-perspective")==0)
604  return VuoSceneObjectSubType_PerspectiveCamera;
605  else if (strcmp(typeString,"camera-stereo")==0)
606  return VuoSceneObjectSubType_StereoCamera;
607  else if (strcmp(typeString,"camera-orthographic")==0)
608  return VuoSceneObjectSubType_OrthographicCamera;
609  else if (strcmp(typeString,"camera-fisheye")==0)
610  return VuoSceneObjectSubType_FisheyeCamera;
611  else if (strcmp(typeString,"light-ambient")==0)
612  return VuoSceneObjectSubType_AmbientLight;
613  else if (strcmp(typeString,"light-point")==0)
614  return VuoSceneObjectSubType_PointLight;
615  else if (strcmp(typeString,"light-spot")==0)
616  return VuoSceneObjectSubType_Spotlight;
617  else if (strcmp(typeString,"text")==0)
618  return VuoSceneObjectSubType_Text;
619 
620  return VuoSceneObjectSubType_Empty;
621 }
622 
627 {
628  switch (type)
629  {
630  case VuoSceneObjectSubType_Group:
631  return "group";
632  case VuoSceneObjectSubType_Mesh:
633  return "mesh";
634  case VuoSceneObjectSubType_PerspectiveCamera:
635  return "camera-perspective";
636  case VuoSceneObjectSubType_StereoCamera:
637  return "camera-stereo";
638  case VuoSceneObjectSubType_OrthographicCamera:
639  return "camera-orthographic";
640  case VuoSceneObjectSubType_FisheyeCamera:
641  return "camera-fisheye";
642  case VuoSceneObjectSubType_AmbientLight:
643  return "light-ambient";
644  case VuoSceneObjectSubType_PointLight:
645  return "light-point";
646  case VuoSceneObjectSubType_Spotlight:
647  return "light-spot";
648  case VuoSceneObjectSubType_Text:
649  return "text";
650  // VuoSceneObjectSubType_Empty
651  default:
652  return "empty";
653  }
654 }
655 
661 {
663  o.name = VuoText_make("Ambient Light");
664  o.type = VuoSceneObjectSubType_AmbientLight;
665  o.lightColor = color;
666  o.lightBrightness = brightness;
667  return o;
668 }
669 
680 VuoSceneObject VuoSceneObject_makePointLight(VuoColor color, float brightness, VuoPoint3d position, float range, float sharpness)
681 {
683  o.name = VuoText_make("Point Light");
684  o.type = VuoSceneObjectSubType_PointLight;
685  o.lightColor = color;
686  o.lightBrightness = brightness;
687  o.lightRange = range;
688  o.lightSharpness = MAX(MIN(sharpness,1),0);
689  o.transform = VuoTransform_makeEuler(position, VuoPoint3d_make(0,0,0), VuoPoint3d_make(1,1,1));
690  return o;
691 }
692 
704 VuoSceneObject VuoSceneObject_makeSpotlight(VuoColor color, float brightness, VuoTransform transform, float cone, float range, float sharpness)
705 {
707  o.name = VuoText_make("Spot Light");
708  o.type = VuoSceneObjectSubType_Spotlight;
709  o.lightColor = color;
710  o.lightBrightness = brightness;
711  o.lightCone = cone;
712  o.lightRange = range;
713  o.lightSharpness = MAX(MIN(sharpness,1),0);
714  o.transform = transform;
715  return o;
716 }
717 
725 void VuoSceneObject_findLights(VuoSceneObject so, VuoColor *ambientColor, float *ambientBrightness, VuoList_VuoSceneObject *pointLights, VuoList_VuoSceneObject *spotLights)
726 {
727  __block VuoList_VuoColor ambientColors = VuoListCreate_VuoColor();
728  VuoRetain(ambientColors);
729 
730  *ambientBrightness = 0;
731  *pointLights = VuoListCreate_VuoSceneObject();
732  *spotLights = VuoListCreate_VuoSceneObject();
733 
734  VuoSceneObject_visit(so, ^(const VuoSceneObject *currentObject, float modelviewMatrix[16]){
735  if (currentObject->type == VuoSceneObjectSubType_AmbientLight)
736  {
737  VuoListAppendValue_VuoColor(ambientColors, currentObject->lightColor);
738  *ambientBrightness += currentObject->lightBrightness;
739  }
740  else if (currentObject->type == VuoSceneObjectSubType_PointLight)
741  {
742  VuoSceneObject l = *currentObject;
743  l.transform = VuoTransform_makeFromMatrix4x4(modelviewMatrix);
744  VuoListAppendValue_VuoSceneObject(*pointLights, l);
745  }
746  else if (currentObject->type == VuoSceneObjectSubType_Spotlight)
747  {
748  VuoSceneObject l = *currentObject;
749  l.transform = VuoTransform_makeFromMatrix4x4(modelviewMatrix);
750  VuoListAppendValue_VuoSceneObject(*spotLights, l);
751  }
752  return true;
753  });
754 
755  if (!VuoListGetCount_VuoColor(ambientColors)
756  && !VuoListGetCount_VuoSceneObject(*pointLights)
757  && !VuoListGetCount_VuoSceneObject(*spotLights))
758  {
759  *ambientColor = VuoColor_makeWithRGBA(1,1,1,1);
760  *ambientBrightness = 0.05;
761 
762  // https://en.wikipedia.org/wiki/Three-point_lighting
763 
764  VuoSceneObject keyLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .70, VuoPoint3d_make(-1,1,1), 5, .5);
765  VuoListAppendValue_VuoSceneObject(*pointLights, keyLight);
766 
767  VuoSceneObject fillLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .20, VuoPoint3d_make(.5,0,1), 5, 0);
768  VuoListAppendValue_VuoSceneObject(*pointLights, fillLight);
769 
770  VuoSceneObject backLight = VuoSceneObject_makePointLight(VuoColor_makeWithRGBA(1,1,1,1), .15, VuoPoint3d_make(1,.75,-.5), 5, 0);
771  VuoListAppendValue_VuoSceneObject(*pointLights, backLight);
772  }
773  else
774  *ambientColor = VuoColor_average(ambientColors);
775 
776  VuoRelease(ambientColors);
777 }
778 
782 typedef struct
783 {
784  long objectCount;
785  const VuoSceneObject *objects;
786  float modelviewMatrix[16];
788 
799 void VuoSceneObject_visit(const VuoSceneObject object, bool (^function)(const VuoSceneObject *currentObject, float modelviewMatrix[16]))
800 {
801  VuoSceneObject_treeState rootState;
802  rootState.objectCount = 1;
803  rootState.objects = &object;
804  VuoTransform_getMatrix(VuoTransform_makeIdentity(), rootState.modelviewMatrix);
805 
806  std::list<VuoSceneObject_treeState> objectsToVisit(1, rootState);
807  while (!objectsToVisit.empty())
808  {
809  VuoSceneObject_treeState currentState = objectsToVisit.front();
810  objectsToVisit.pop_front();
811 
812  for (long i = 0; i < currentState.objectCount; ++i)
813  {
814  float localModelviewMatrix[16];
815  VuoTransform_getMatrix(currentState.objects[i].transform, localModelviewMatrix);
816  float compositeModelviewMatrix[16];
817  VuoTransform_multiplyMatrices4x4(localModelviewMatrix, currentState.modelviewMatrix, compositeModelviewMatrix);
818 
819  if (!function(&currentState.objects[i], compositeModelviewMatrix))
820  return;
821 
822  // Prepend this object's childObjects to the objectsToVisit queue.
823  long childObjectCount = VuoListGetCount_VuoSceneObject(currentState.objects[i].childObjects);
824  if (childObjectCount)
825  {
826  VuoSceneObject_treeState childState;
827  childState.objectCount = childObjectCount;
828  childState.objects = VuoListGetData_VuoSceneObject(currentState.objects[i].childObjects);
829  memcpy(childState.modelviewMatrix, compositeModelviewMatrix, sizeof(float[16]));
830  objectsToVisit.push_front(childState);
831  }
832  }
833  }
834 }
835 
839 static void VuoSceneObject_applyInternal(VuoSceneObject *object, void (^function)(VuoSceneObject *currentObject, float modelviewMatrix[16]), float modelviewMatrix[16])
840 {
841  float localModelviewMatrix[16];
842  VuoTransform_getMatrix(object->transform, localModelviewMatrix);
843  float compositeModelviewMatrix[16];
844  VuoTransform_multiplyMatrices4x4(localModelviewMatrix, modelviewMatrix, compositeModelviewMatrix);
845 
846  function(object, compositeModelviewMatrix);
847 
848  if (object->childObjects)
849  {
850  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(object->childObjects);
851  for (unsigned long i = 1; i <= childObjectCount; ++i)
852  {
853  VuoSceneObject o = VuoListGetValue_VuoSceneObject(object->childObjects, i);
854  VuoSceneObject_applyInternal(&o, function, compositeModelviewMatrix);
855  VuoListSetValue_VuoSceneObject(object->childObjects, o, i, false);
856  }
857  }
858 }
859 
867 void VuoSceneObject_apply(VuoSceneObject *object, void (^function)(VuoSceneObject *currentObject, float modelviewMatrix[16]))
868 {
869  float localModelviewMatrix[16];
870  VuoTransform_getMatrix(VuoTransform_makeIdentity(), localModelviewMatrix);
871 
872  VuoSceneObject_applyInternal(object, function, localModelviewMatrix);
873 }
874 
882 void VuoSceneObject_setFaceCullingMode(VuoSceneObject *object, unsigned int faceCullingMode)
883 {
884  VuoSceneObject_apply(object, ^(VuoSceneObject *currentObject, float modelviewMatrix[16]){
885  if (currentObject->mesh)
886  for (unsigned long i = 0; i < currentObject->mesh->submeshCount; ++i)
887  currentObject->mesh->submeshes[i].faceCullingMode = faceCullingMode;
888  });
889 }
890 
904 {
905  VuoSceneObject_apply(object, ^(VuoSceneObject *currentObject, float modelviewMatrix[16]){
906  currentObject->blendMode = blendMode;
907  });
908 }
909 
920 {
921  VuoSceneObject copiedObject = object;
922 
923  copiedObject.mesh = VuoMesh_copy(object.mesh);
924 
925  if (object.childObjects)
926  {
927  copiedObject.childObjects = VuoListCreate_VuoSceneObject();
928  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(object.childObjects);
929  for (unsigned long i = 1; i <= childObjectCount; ++i)
930  VuoListAppendValue_VuoSceneObject(copiedObject.childObjects, VuoSceneObject_copy(VuoListGetValue_VuoSceneObject(object.childObjects, i)));
931  }
932 
933  copiedObject.name = VuoText_make(object.name);
934 
935  return copiedObject;
936 }
937 
942 {
943  __block bool haveGlobalBounds = false;
944  __block VuoBox globalBounds;
945 
946  VuoSceneObject_visit(so, ^(const VuoSceneObject *currentObject, float modelviewMatrix[16]){
947  VuoBox bounds;
948  bool foundBounds = VuoSceneObject_meshBounds(*currentObject, &bounds, modelviewMatrix);
949  if (foundBounds)
950  {
951  globalBounds = haveGlobalBounds ? VuoBox_encapsulate(globalBounds, bounds) : bounds;
952  haveGlobalBounds = true;
953  }
954  return true;
955  });
956 
957  if (haveGlobalBounds)
958  return globalBounds;
959  else
960  return VuoBox_make( (VuoPoint3d){0,0,0}, (VuoPoint3d){0,0,0} );
961 }
962 
966 bool VuoSceneObject_meshBounds(const VuoSceneObject so, VuoBox *bounds, float matrix[16])
967 {
968  if (VuoSceneObject_getVertexCount(so) < 1)
969  return false;
970 
971  if (so.isRealSize
972  || so.type == VuoSceneObjectSubType_Text)
973  {
974  // We don't know what the actual rendered size of the realSize layer will be,
975  // but we can at least include its center point.
976  *bounds = VuoBox_make(VuoPoint3d_make(matrix[12], matrix[13], matrix[14]), VuoPoint3d_make(0,0,0));
977  }
978  else
979  {
980  *bounds = VuoMesh_bounds(so.mesh, matrix);
981 
982  if (so.shader)
983  {
984  bounds->size.x *= so.shader->objectScale;
985  bounds->size.y *= so.shader->objectScale;
986  bounds->size.z *= so.shader->objectScale;
987  }
988  }
989 
990  return true;
991 }
992 
997 {
998  VuoBox bounds = VuoSceneObject_bounds(*so);
999  so->transform.translation = VuoPoint3d_subtract(so->transform.translation, bounds.center);
1000 }
1001 
1008 {
1009  VuoBox bounds = VuoSceneObject_bounds(*so);
1010 
1011  float scale = fmax(fmax(bounds.size.x, bounds.size.y), bounds.size.z);
1012  if (fabs(scale) < 0.00001)
1013  return;
1014 
1015  so->transform.scale = VuoPoint3d_multiply(so->transform.scale, 1./scale);
1016  so->transform.translation = VuoPoint3d_multiply(so->transform.translation, 1./scale);
1017 }
1018 
1045 {
1046  json_object *o = NULL;
1047 
1048  int id = 0;
1049  if (json_object_object_get_ex(js, "id", &o))
1050  id = json_object_get_int64(o);
1051 
1052  VuoSceneObjectSubType type = VuoSceneObjectSubType_Empty;
1053  if (json_object_object_get_ex(js, "type", &o))
1054  type = VuoSceneObject_typeFromCString(json_object_get_string(o));
1055 
1056  VuoMesh mesh = NULL;
1057  if (json_object_object_get_ex(js, "mesh", &o))
1058  mesh = VuoMesh_makeFromJson(o);
1059 
1060  VuoShader shader = NULL;
1061  if (json_object_object_get_ex(js, "shader", &o))
1062  shader = VuoShader_makeFromJson(o);
1063 
1064  bool isRealSize = false;
1065  if (json_object_object_get_ex(js, "isRealSize", &o))
1066  isRealSize = VuoBoolean_makeFromJson(o);
1067 
1068  bool preservePhysicalSize = false;
1069  if (json_object_object_get_ex(js, "preservePhysicalSize", &o))
1070  preservePhysicalSize = VuoBoolean_makeFromJson(o);
1071 
1072  VuoBlendMode blendMode = VuoBlendMode_Normal;
1073  if (json_object_object_get_ex(js, "blendMode", &o))
1074  blendMode = VuoBlendMode_makeFromJson(o);
1075 
1076  VuoList_VuoSceneObject childObjects = NULL;
1077  if (json_object_object_get_ex(js, "childObjects", &o))
1078  childObjects = VuoList_VuoSceneObject_makeFromJson(o);
1079 
1080  float cameraFieldOfView;
1081  if (json_object_object_get_ex(js, "cameraFieldOfView", &o))
1082  cameraFieldOfView = json_object_get_double(o);
1083 
1084  float cameraWidth;
1085  if (json_object_object_get_ex(js, "cameraWidth", &o))
1086  cameraWidth = json_object_get_double(o);
1087 
1088  float cameraDistanceMin;
1089  if (json_object_object_get_ex(js, "cameraDistanceMin", &o))
1090  cameraDistanceMin = json_object_get_double(o);
1091 
1092  float cameraDistanceMax;
1093  if (json_object_object_get_ex(js, "cameraDistanceMax", &o))
1094  cameraDistanceMax = json_object_get_double(o);
1095 
1096  float cameraConfocalDistance;
1097  if (json_object_object_get_ex(js, "cameraConfocalDistance", &o))
1098  cameraConfocalDistance = json_object_get_double(o);
1099 
1100  float cameraIntraocularDistance;
1101  if (json_object_object_get_ex(js, "cameraIntraocularDistance", &o))
1102  cameraIntraocularDistance = json_object_get_double(o);
1103 
1104  float cameraVignetteWidth;
1105  if (json_object_object_get_ex(js, "cameraVignetteWidth", &o))
1106  cameraVignetteWidth = json_object_get_double(o);
1107 
1108  float cameraVignetteSharpness;
1109  if (json_object_object_get_ex(js, "cameraVignetteSharpness", &o))
1110  cameraVignetteSharpness = json_object_get_double(o);
1111 
1112  VuoColor lightColor;
1113  if (json_object_object_get_ex(js, "lightColor", &o))
1114  lightColor = VuoColor_makeFromJson(o);
1115 
1116  float lightBrightness;
1117  if (json_object_object_get_ex(js, "lightBrightness", &o))
1118  lightBrightness = json_object_get_double(o);
1119 
1120  float lightCone;
1121  if (json_object_object_get_ex(js, "lightCone", &o))
1122  lightCone = json_object_get_double(o);
1123 
1124  float lightRange;
1125  if (json_object_object_get_ex(js, "lightRange", &o))
1126  lightRange = json_object_get_double(o);
1127 
1128  float lightSharpness;
1129  if (json_object_object_get_ex(js, "lightSharpness", &o))
1130  lightSharpness = json_object_get_double(o);
1131 
1132  VuoText name = NULL;
1133  if (json_object_object_get_ex(js, "name", &o))
1134  name = VuoText_makeFromJson(o);
1135 
1136  json_object_object_get_ex(js, "transform", &o);
1137  VuoTransform transform = VuoTransform_makeFromJson(o);
1138 
1139  VuoText text = NULL;
1140  if (json_object_object_get_ex(js, "text", &o))
1141  text = VuoText_makeFromJson(o);
1142 
1143  VuoFont font;
1144  if (json_object_object_get_ex(js, "font", &o))
1145  font = VuoFont_makeFromJson(o);
1146 
1147  bool scaleWithScene = false;
1148  if (json_object_object_get_ex(js, "scaleWithScene", &o))
1149  scaleWithScene = VuoBoolean_makeFromJson(o);
1150 
1151  float wrapWidth = INFINITY;
1152  if (json_object_object_get_ex(js, "wrapWidth", &o))
1153  wrapWidth = json_object_get_double(o);
1154 
1155  VuoSceneObject obj;
1156  switch (type)
1157  {
1158  case VuoSceneObjectSubType_Empty:
1159  obj = VuoSceneObject_makeEmpty();
1160  break;
1161  case VuoSceneObjectSubType_Group:
1162  obj = VuoSceneObject_makeGroup(childObjects, transform);
1163  break;
1164  case VuoSceneObjectSubType_Mesh:
1165  obj = VuoSceneObject_make(mesh, shader, transform, childObjects);
1166  obj.isRealSize = isRealSize;
1167  obj.preservePhysicalSize = preservePhysicalSize;
1168  obj.blendMode = blendMode;
1169  obj.name = name;
1170  break;
1171  case VuoSceneObjectSubType_PerspectiveCamera:
1173  name,
1174  transform,
1175  cameraFieldOfView,
1176  cameraDistanceMin,
1177  cameraDistanceMax
1178  );
1179  break;
1180  case VuoSceneObjectSubType_StereoCamera:
1182  name,
1183  transform,
1184  cameraFieldOfView,
1185  cameraDistanceMin,
1186  cameraDistanceMax,
1187  cameraConfocalDistance,
1188  cameraIntraocularDistance
1189  );
1190  break;
1191  case VuoSceneObjectSubType_OrthographicCamera:
1193  name,
1194  transform,
1195  cameraWidth,
1196  cameraDistanceMin,
1197  cameraDistanceMax
1198  );
1199  break;
1200  case VuoSceneObjectSubType_FisheyeCamera:
1202  name,
1203  transform,
1204  cameraFieldOfView,
1205  cameraVignetteWidth,
1206  cameraVignetteSharpness
1207  );
1208  break;
1209  case VuoSceneObjectSubType_AmbientLight:
1210  obj = VuoSceneObject_makeAmbientLight(lightColor, lightBrightness);
1211  break;
1212  case VuoSceneObjectSubType_PointLight:
1213  obj = VuoSceneObject_makePointLight(lightColor, lightBrightness, transform.translation, lightRange, lightSharpness);
1214  break;
1215  case VuoSceneObjectSubType_Spotlight:
1216  obj = VuoSceneObject_makeSpotlight(lightColor, lightBrightness, transform, lightCone, lightRange, lightSharpness);
1217  break;
1218  case VuoSceneObjectSubType_Text:
1219  obj = VuoSceneObject_makeText(text, font, scaleWithScene, wrapWidth);
1220  obj.transform = transform;
1221  obj.mesh = mesh;
1222  break;
1223  }
1224 
1225  obj.id = id;
1226 
1227  return obj;
1228 }
1229 
1234 {
1235  json_object *js = json_object_new_object();
1236 
1237  json_object_object_add(js, "id", json_object_new_int64(value.id));
1238  json_object_object_add(js, "type", json_object_new_string(VuoSceneObject_cStringForType(value.type)));
1239 
1240  switch (value.type)
1241  {
1242  case VuoSceneObjectSubType_Empty:
1243  break;
1244 
1245  case VuoSceneObjectSubType_Group:
1246  case VuoSceneObjectSubType_Mesh:
1247  {
1248  if (value.mesh)
1249  {
1250  json_object *meshObject = VuoMesh_getJson(value.mesh);
1251  json_object_object_add(js, "mesh", meshObject);
1252  }
1253 
1254  if (value.shader)
1255  {
1256  json_object *shaderObject = VuoShader_getJson(value.shader);
1257  json_object_object_add(js, "shader", shaderObject);
1258  }
1259 
1260  json_object *isRealSizeObject = VuoBoolean_getJson(value.isRealSize);
1261  json_object_object_add(js, "isRealSize", isRealSizeObject);
1262 
1263  json_object *preservePhysicalSizeObject = VuoBoolean_getJson(value.preservePhysicalSize);
1264  json_object_object_add(js, "preservePhysicalSize", preservePhysicalSizeObject);
1265 
1266  if (value.blendMode != VuoBlendMode_Normal)
1267  {
1268  json_object *blendModeObject = VuoBlendMode_getJson(value.blendMode);
1269  json_object_object_add(js, "blendMode", blendModeObject);
1270  }
1271 
1272  if (value.childObjects)
1273  {
1274  json_object *childObjectsObject = VuoList_VuoSceneObject_getJson(value.childObjects);
1275  json_object_object_add(js, "childObjects", childObjectsObject);
1276  }
1277 
1278  break;
1279  }
1280 
1281  case VuoSceneObjectSubType_PerspectiveCamera:
1282  case VuoSceneObjectSubType_StereoCamera:
1283  case VuoSceneObjectSubType_OrthographicCamera:
1284  case VuoSceneObjectSubType_FisheyeCamera:
1285  {
1286  if (value.type != VuoSceneObjectSubType_FisheyeCamera)
1287  {
1288  json_object_object_add(js, "cameraDistanceMin", json_object_new_double(value.cameraDistanceMin));
1289  json_object_object_add(js, "cameraDistanceMax", json_object_new_double(value.cameraDistanceMax));
1290  }
1291 
1292  if (value.type == VuoSceneObjectSubType_PerspectiveCamera
1293  || value.type == VuoSceneObjectSubType_StereoCamera
1294  || value.type == VuoSceneObjectSubType_FisheyeCamera)
1295  json_object_object_add(js, "cameraFieldOfView", json_object_new_double(value.cameraFieldOfView));
1296 
1297  if (value.type == VuoSceneObjectSubType_StereoCamera)
1298  {
1299  json_object_object_add(js, "cameraConfocalDistance", json_object_new_double(value.cameraConfocalDistance));
1300  json_object_object_add(js, "cameraIntraocularDistance", json_object_new_double(value.cameraIntraocularDistance));
1301  }
1302 
1303  if (value.type == VuoSceneObjectSubType_OrthographicCamera)
1304  json_object_object_add(js, "cameraWidth", json_object_new_double(value.cameraWidth));
1305 
1306  if (value.type == VuoSceneObjectSubType_FisheyeCamera)
1307  {
1308  json_object_object_add(js, "cameraVignetteWidth", json_object_new_double(value.cameraVignetteWidth));
1309  json_object_object_add(js, "cameraVignetteSharpness", json_object_new_double(value.cameraVignetteSharpness));
1310  }
1311 
1312  break;
1313  }
1314 
1315  case VuoSceneObjectSubType_AmbientLight:
1316  case VuoSceneObjectSubType_PointLight:
1317  case VuoSceneObjectSubType_Spotlight:
1318  {
1319  json_object_object_add(js, "lightColor", VuoColor_getJson(value.lightColor));
1320  json_object_object_add(js, "lightBrightness", json_object_new_double(value.lightBrightness));
1321 
1322  if (value.type == VuoSceneObjectSubType_PointLight
1323  || value.type == VuoSceneObjectSubType_Spotlight)
1324  {
1325  json_object_object_add(js, "lightRange", json_object_new_double(value.lightRange));
1326  json_object_object_add(js, "lightSharpness", json_object_new_double(value.lightSharpness));
1327  }
1328  if (value.type == VuoSceneObjectSubType_Spotlight)
1329  json_object_object_add(js, "lightCone", json_object_new_double(value.lightCone));
1330 
1331  break;
1332  }
1333 
1334  case VuoSceneObjectSubType_Text:
1335  {
1336  if (value.text)
1337  {
1338  json_object *textObject = VuoText_getJson(value.text);
1339  json_object_object_add(js, "text", textObject);
1340  }
1341 
1342  json_object *fontObject = VuoFont_getJson(value.font);
1343  json_object_object_add(js, "font", fontObject);
1344 
1345  if (value.mesh)
1346  {
1347  json_object *meshObject = VuoMesh_getJson(value.mesh);
1348  json_object_object_add(js, "mesh", meshObject);
1349  }
1350 
1351  json_object_object_add(js, "scaleWithScene", VuoBoolean_getJson(value.scaleWithScene));
1352  json_object_object_add(js, "wrapWidth", VuoReal_getJson(value.wrapWidth));
1353 
1354  break;
1355  }
1356  }
1357 
1358  if (value.name)
1359  {
1360  json_object *nameObject = VuoText_getJson(value.name);
1361  json_object_object_add(js, "name", nameObject);
1362  }
1363 
1364  if (value.type != VuoSceneObjectSubType_AmbientLight)
1365  {
1366  json_object *transformObject = VuoTransform_getJson(value.transform);
1367  json_object_object_add(js, "transform", transformObject);
1368  }
1369 
1370  return js;
1371 }
1372 
1377 {
1378  if (!value.mesh)
1379  return 0;
1380 
1381  unsigned long vertexCount = 0;
1382  for (unsigned int i = 0; i < value.mesh->submeshCount; ++i)
1383  vertexCount += value.mesh->submeshes[i].vertexCount;
1384 
1385  return vertexCount;
1386 }
1387 
1392 {
1393  if (!value.mesh)
1394  return 0;
1395 
1396  unsigned long elementCount = 0;
1397  for (unsigned int i = 0; i < value.mesh->submeshCount; ++i)
1398  elementCount += value.mesh->submeshes[i].elementCount;
1399 
1400  return elementCount;
1401 }
1402 
1408 void VuoSceneObject_getStatistics(const VuoSceneObject value, unsigned long *descendantCount, unsigned long *totalVertexCount, unsigned long *totalElementCount)
1409 {
1410  unsigned long childObjectCount = 0;
1411  if (value.childObjects)
1412  childObjectCount = VuoListGetCount_VuoSceneObject(value.childObjects);
1413  *descendantCount += childObjectCount;
1414  *totalVertexCount += VuoSceneObject_getVertexCount(value);
1415  *totalElementCount += VuoSceneObject_getElementCount(value);
1416 
1417  for (unsigned long i = 1; i <= childObjectCount; ++i)
1418  VuoSceneObject_getStatistics(VuoListGetValue_VuoSceneObject(value.childObjects, i), descendantCount, totalVertexCount, totalElementCount);
1419 }
1420 
1425 {
1426  // Exploit json_object's set-containing-only-unique-items data structure.
1427  __block json_object *names = json_object_new_object();
1428  VuoSceneObject_visit(object, ^(const VuoSceneObject *currentObject, float modelviewMatrix[16]){
1429  if (currentObject->shader)
1430  json_object_object_add(names, currentObject->shader->name, NULL);
1431  return true;
1432  });
1433 
1435  json_object_object_foreach(names, key, val)
1436  VuoListAppendValue_VuoText(nameList, VuoText_make(key));
1437  json_object_put(names);
1438  return nameList;
1439 }
1440 
1445 {
1446  if (value.type == VuoSceneObjectSubType_Text)
1447  {
1448  char *fontSummary = VuoFont_getSummary(value.font);
1449  char *textSummary = VuoText_format("<div>\"%s\"</div><div>%sat (%g,%g)</div><div>id %llu</div>", value.text, fontSummary, value.transform.translation.x, value.transform.translation.y, value.id);
1450  free(fontSummary);
1451  return textSummary;
1452  }
1453 
1454  if (value.type == VuoSceneObjectSubType_PerspectiveCamera
1455  || value.type == VuoSceneObjectSubType_StereoCamera
1456  || value.type == VuoSceneObjectSubType_OrthographicCamera
1457  || value.type == VuoSceneObjectSubType_FisheyeCamera)
1458  {
1459  const char *type = VuoSceneObject_cStringForType(value.type);
1460 
1461  float cameraViewValue = 0;
1462  const char *cameraViewString = "";
1463  if (value.type == VuoSceneObjectSubType_PerspectiveCamera)
1464  {
1465  cameraViewValue = value.cameraFieldOfView;
1466  cameraViewString = "° field of view";
1467  }
1468  else if (value.type == VuoSceneObjectSubType_StereoCamera)
1469  {
1470  cameraViewValue = value.cameraFieldOfView;
1471  cameraViewString = "° field of view (stereoscopic)";
1472  }
1473  else if (value.type == VuoSceneObjectSubType_OrthographicCamera)
1474  {
1475  cameraViewValue = value.cameraWidth;
1476  cameraViewString = " unit width";
1477  }
1478  else if (value.type == VuoSceneObjectSubType_FisheyeCamera)
1479  {
1480  cameraViewValue = value.cameraFieldOfView;
1481  cameraViewString = "° field of view (fisheye)";
1482  }
1483 
1484  char *translationString = VuoPoint3d_getSummary(value.transform.translation);
1485 
1486  const char *rotationLabel;
1487  char *rotationString;
1488  if (value.transform.type == VuoTransformTypeEuler)
1489  {
1490  rotationLabel = "rotated";
1491  rotationString = VuoPoint3d_getSummary(VuoPoint3d_multiply(value.transform.rotationSource.euler, -180.f/M_PI));
1492  }
1493  else
1494  {
1495  rotationLabel = "target";
1496  rotationString = VuoPoint3d_getSummary(value.transform.rotationSource.target);
1497  }
1498 
1499  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>",
1500  type, value.name ? value.name : "",
1501  translationString,
1502  rotationLabel, rotationString,
1503  cameraViewValue, cameraViewString,
1504  value.cameraDistanceMin, value.cameraDistanceMax);
1505  free(rotationString);
1506  free(translationString);
1507  return valueAsString;
1508  }
1509 
1510  if (value.type == VuoSceneObjectSubType_AmbientLight
1511  || value.type == VuoSceneObjectSubType_PointLight
1512  || value.type == VuoSceneObjectSubType_Spotlight)
1513  {
1514  const char *type = VuoSceneObject_cStringForType(value.type);
1515  char *colorString = VuoColor_getShortSummary(value.lightColor);
1516 
1517  char *positionRangeString;
1518  if (value.type == VuoSceneObjectSubType_PointLight
1519  || value.type == VuoSceneObjectSubType_Spotlight)
1520  {
1521  char *positionString = VuoPoint3d_getSummary(value.transform.translation);
1522 
1523  positionRangeString = VuoText_format("<div>position (%s)</div><div>range %g units (%g sharpness)</div>",
1524  positionString, value.lightRange, value.lightSharpness);
1525 
1526  free(positionString);
1527  }
1528  else
1529  positionRangeString = strdup("");
1530 
1531  char *directionConeString;
1532  if (value.type == VuoSceneObjectSubType_Spotlight)
1533  {
1534  VuoPoint3d direction = VuoTransform_getDirection(value.transform);
1535  char *directionString = VuoPoint3d_getSummary(direction);
1536 
1537  directionConeString = VuoText_format("<div>direction (%s)</div><div>cone %g°</div>",
1538  directionString, value.lightCone * 180./M_PI);
1539 
1540  free(directionString);
1541  }
1542  else
1543  directionConeString = strdup("");
1544 
1545  char *valueAsString = VuoText_format("<div>%s</div><div>color %s</div><div>brightness %g</div>%s%s",
1546  type, colorString, value.lightBrightness, positionRangeString, directionConeString);
1547 
1548  free(directionConeString);
1549  free(positionRangeString);
1550  free(colorString);
1551 
1552  return valueAsString;
1553  }
1554 
1555  unsigned long vertexCount = VuoSceneObject_getVertexCount(value);
1556  unsigned long elementCount = VuoSceneObject_getElementCount(value);
1557 
1558  char *transform = VuoTransform_getSummary(value.transform);
1559 
1560  unsigned long childObjectCount = 0;
1561  if (value.childObjects)
1562  childObjectCount = VuoListGetCount_VuoSceneObject(value.childObjects);
1563  const char *childObjectPlural = childObjectCount == 1 ? "" : "s";
1564 
1565  char *descendants;
1566  if (childObjectCount)
1567  {
1568  unsigned long descendantCount = 0;
1569  unsigned long totalVertexCount = 0;
1570  unsigned long totalElementCount = 0;
1571  VuoSceneObject_getStatistics(value, &descendantCount, &totalVertexCount, &totalElementCount);
1572  const char *descendantPlural = descendantCount == 1 ? "" : "s";
1573 
1574  descendants = VuoText_format("<div>%ld descendant%s</div><div>total, including descendants:</div><div>%ld vertices, %ld elements</div>",
1575  descendantCount, descendantPlural, totalVertexCount, totalElementCount);
1576  }
1577  else
1578  descendants = strdup("");
1579 
1580  VuoList_VuoText shaderNames = VuoSceneObject_findShaderNames(value);
1581  VuoRetain(shaderNames);
1582  char *shaderNamesSummary;
1583  if (VuoListGetCount_VuoText(shaderNames))
1584  {
1585  VuoInteger shaderNameCount = VuoListGetCount_VuoText(shaderNames);
1586  const char *header = "<div>shaders:<ul>";
1587  VuoInteger shaderNameLength = strlen(header);
1588  for (VuoInteger i = 1; i <= shaderNameCount; ++i)
1589  shaderNameLength += strlen("<li>") + strlen(VuoListGetValue_VuoText(shaderNames, i)) + strlen("</li>");
1590  shaderNameLength += strlen("</ul></div>");
1591 
1592  shaderNamesSummary = (char *)malloc(shaderNameLength + 1);
1593  char *t = shaderNamesSummary;
1594  t = strcpy(t, header) + strlen(header);
1595  for (VuoInteger i = 1; i <= shaderNameCount; ++i)
1596  {
1597  t = strcpy(t, "<li>") + strlen("<li>");
1598  t = strcpy(t, VuoListGetValue_VuoText(shaderNames, i)) + strlen(VuoListGetValue_VuoText(shaderNames, i));
1599  t = strcpy(t, "</li>") + strlen("</li>");
1600  }
1601  t = strcpy(t, "</ul></div>");
1602  }
1603  else
1604  shaderNamesSummary = strdup("");
1605  VuoRelease(shaderNames);
1606 
1607  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",
1608  value.name ? value.name : "",
1609  vertexCount, elementCount,
1610  transform,
1611  value.id,
1612  childObjectCount, childObjectPlural,
1613  descendants, shaderNamesSummary);
1614 
1615  free(descendants);
1616  free(transform);
1617  free(shaderNamesSummary);
1618 
1619  return valueAsString;
1620 }
1621 
1625 static void VuoSceneObject_dump_internal(const VuoSceneObject so, unsigned int level)
1626 {
1627  for (unsigned int i=0; i<level; ++i)
1628  fprintf(stderr, "\t");
1629 
1630  fprintf(stderr, "%s (%s) ", VuoSceneObject_cStringForType(so.type), VuoTransform_getSummary(so.transform));
1631  if (so.type == VuoSceneObjectSubType_Mesh)
1632  fprintf(stderr, "%lu vertices, %lu elements, shader '%s' (%p)", VuoSceneObject_getVertexCount(so), VuoSceneObject_getElementCount(so), so.shader ? so.shader->name : "", so.shader);
1633  fprintf(stderr, "\n");
1634 
1635  if (so.childObjects)
1636  {
1637  unsigned int childObjectCount = VuoListGetCount_VuoSceneObject(so.childObjects);
1638  for (unsigned int i=1; i<=childObjectCount; ++i)
1639  VuoSceneObject_dump_internal(VuoListGetValue_VuoSceneObject(so.childObjects, i), level+1);
1640  }
1641 }
1642 
1647 {
1649 }
1650 
1661 VuoSceneObject VuoSceneObject_flatten(const VuoSceneObject so, bool calculateTangents)
1662 {
1663  // Count the vertices.
1664 
1665  __block unsigned long triangleVertexCount = 0;
1666  __block unsigned long trianglePrimitiveCount = 0;
1667  __block unsigned int triangleFaceCullingMode = GL_BACK;
1668 
1669  __block unsigned long lineVertexCount = 0;
1670  __block unsigned long linePrimitiveCount = 0;
1671  __block double linePrimitiveSize = 0;
1672 
1673  __block unsigned long pointVertexCount = 0;
1674  __block unsigned long pointPrimitiveCount = 0;
1675  __block double pointPrimitiveSize = 0;
1676 
1677  VuoSceneObject_visit(so, ^(const VuoSceneObject *currentObject, float modelviewMatrix[16]){
1678  if (!currentObject->mesh)
1679  return true;
1680 
1681  for (unsigned int i = 0; i < currentObject->mesh->submeshCount; ++i)
1682  {
1683  VuoSubmesh submesh = currentObject->mesh->submeshes[i];
1687  {
1688  triangleVertexCount += submesh.vertexCount;
1689  trianglePrimitiveCount += VuoSubmesh_getSplitPrimitiveCount(submesh);
1690  triangleFaceCullingMode = submesh.faceCullingMode;
1691  }
1694  {
1695  lineVertexCount += submesh.vertexCount;
1696  linePrimitiveCount += VuoSubmesh_getSplitPrimitiveCount(submesh);
1697  linePrimitiveSize = submesh.primitiveSize;
1698  }
1699  else if (submesh.elementAssemblyMethod == VuoMesh_Points)
1700  {
1701  pointVertexCount += submesh.vertexCount;
1702  pointPrimitiveCount += VuoSubmesh_getSplitPrimitiveCount(submesh);
1703  pointPrimitiveSize = submesh.primitiveSize;
1704  }
1705  }
1706  return true;
1707  });
1708 // VLog("triangles: %ldv %ldp lines: %ldv %ldp points: %ldv %ldp", triangleVertexCount, trianglePrimitiveCount, lineVertexCount, linePrimitiveCount, pointVertexCount, pointPrimitiveCount);
1709 
1710  VuoSubmesh triangleSubmesh;
1711  VuoSubmesh lineSubmesh;
1712  VuoSubmesh pointSubmesh;
1713  int submeshCount = 0;
1714  if (trianglePrimitiveCount)
1715  {
1716  triangleSubmesh = VuoSubmesh_make(triangleVertexCount, trianglePrimitiveCount * 3);
1717  triangleSubmesh.elementAssemblyMethod = VuoMesh_IndividualTriangles;
1718  triangleSubmesh.faceCullingMode = triangleFaceCullingMode;
1719  ++submeshCount;
1720  }
1721  if (linePrimitiveCount)
1722  {
1723  lineSubmesh = VuoSubmesh_make(lineVertexCount, linePrimitiveCount * 2);
1725  lineSubmesh.primitiveSize = linePrimitiveSize;
1726  ++submeshCount;
1727  }
1728  if (pointPrimitiveCount)
1729  {
1730  pointSubmesh = VuoSubmesh_make(pointVertexCount, pointPrimitiveCount);
1731  pointSubmesh.elementAssemblyMethod = VuoMesh_Points;
1732  pointSubmesh.primitiveSize = pointPrimitiveSize;
1733  ++submeshCount;
1734  }
1735  if (!trianglePrimitiveCount && !linePrimitiveCount && !pointPrimitiveCount)
1736  return VuoSceneObject_makeEmpty();
1737 
1738 
1739  // Allocate the buffers.
1740  __block unsigned long triangleVertexIndex = 0;
1741  __block unsigned long triangleElementIndex = 0;
1742  __block unsigned long lineVertexIndex = 0;
1743  __block unsigned long lineElementIndex = 0;
1744  __block unsigned long pointVertexIndex = 0;
1745  __block unsigned long pointElementIndex = 0;
1746  __block bool anyTextureCoordinates = false;
1747  VuoSceneObject_visit(so, ^(const VuoSceneObject *currentObject, float modelviewMatrix[16]){
1748  if (!currentObject->mesh)
1749  return true;
1750 
1751  for (unsigned int i = 0; i < currentObject->mesh->submeshCount; ++i)
1752  {
1753  VuoSubmesh submesh = currentObject->mesh->submeshes[i];
1754 
1755  if (!submesh.positions)
1756  VuoSubmesh_download(&submesh);
1757 
1758  if (submesh.textureCoordinates)
1759  anyTextureCoordinates = true;
1760 
1764  {
1765  unsigned long indexOffset = triangleVertexIndex;
1766  for (unsigned int n = 0; n < submesh.vertexCount; ++n)
1767  {
1768  VuoPoint3d p = (VuoPoint3d){submesh.positions[n].x, submesh.positions[n].y, submesh.positions[n].z};
1769  VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
1770  triangleSubmesh.positions[triangleVertexIndex] = (VuoPoint4d){pt.x, pt.y, pt.z, 1};
1771 
1772  if (submesh.normals)
1773  {
1774  VuoPoint3d r = (VuoPoint3d){submesh.normals[n].x, submesh.normals[n].y, submesh.normals[n].z};
1775  VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
1776  triangleSubmesh.normals[triangleVertexIndex] = (VuoPoint4d){rt.x, rt.y, rt.z, 1};
1777  }
1778 
1779  if (submesh.textureCoordinates)
1780  triangleSubmesh.textureCoordinates[triangleVertexIndex] = submesh.textureCoordinates[n];
1781 
1782  ++triangleVertexIndex;
1783  }
1784 
1786  {
1787  if (submesh.elementCount)
1788  for (unsigned int n = 0; n < submesh.elementCount; ++n)
1789  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + submesh.elements[n];
1790  else
1791  for (unsigned int n = 0; n < submesh.vertexCount; ++n)
1792  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + n;
1793  }
1794  else if (submesh.elementAssemblyMethod == VuoMesh_TriangleStrip)
1795  {
1796  // Expand the triangle strip to individual triangles.
1797  if (submesh.elementCount)
1798  for (unsigned int n = 2; n < submesh.elementCount; ++n)
1799  if (n%2 == 0)
1800  {
1801  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + submesh.elements[n-2];
1802  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + submesh.elements[n-1];
1803  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + submesh.elements[n ];
1804  }
1805  else
1806  {
1807  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + submesh.elements[n-1];
1808  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + submesh.elements[n-2];
1809  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + submesh.elements[n ];
1810  }
1811  else
1812  for (unsigned int n = 0; n < submesh.vertexCount; ++n)
1813  if (n%2 == 0)
1814  {
1815  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + n-2;
1816  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + n-1;
1817  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + n ;
1818  }
1819  else
1820  {
1821  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + n-1;
1822  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + n-2;
1823  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + n ;
1824  }
1825  }
1826  else if (submesh.elementAssemblyMethod == VuoMesh_TriangleFan)
1827  {
1828  // Expand the triangle fan to individual triangles.
1829  if (submesh.elementCount)
1830  for (unsigned int n = 2; n < submesh.elementCount; ++n)
1831  {
1832  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + submesh.elements[0 ];
1833  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + submesh.elements[n-1];
1834  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + submesh.elements[n ];
1835  }
1836  else
1837  for (unsigned int n = 2; n < submesh.vertexCount; ++n)
1838  {
1839  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + 0;
1840  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + n-1;
1841  triangleSubmesh.elements[triangleElementIndex++] = indexOffset + n;
1842  }
1843  }
1844  }
1845  else if (submesh.elementAssemblyMethod == VuoMesh_IndividualLines
1846  || submesh.elementAssemblyMethod == VuoMesh_LineStrip)
1847  {
1848  unsigned long indexOffset = lineVertexIndex;
1849  for (unsigned int n = 0; n < submesh.vertexCount; ++n)
1850  {
1851  VuoPoint3d p = (VuoPoint3d){submesh.positions[n].x, submesh.positions[n].y, submesh.positions[n].z};
1852  VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
1853  lineSubmesh.positions[lineVertexIndex] = (VuoPoint4d){pt.x, pt.y, pt.z, 1};
1854 
1855  if (submesh.normals)
1856  {
1857  VuoPoint3d r = (VuoPoint3d){submesh.normals[n].x, submesh.normals[n].y, submesh.normals[n].z};
1858  VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
1859  lineSubmesh.normals[lineVertexIndex] = (VuoPoint4d){rt.x, rt.y, rt.z, 1};
1860  }
1861 
1862  if (submesh.textureCoordinates)
1863  lineSubmesh.textureCoordinates[lineVertexIndex] = submesh.textureCoordinates[n];
1864 
1865  ++lineVertexIndex;
1866  }
1867 
1868  if (submesh.elementAssemblyMethod == VuoMesh_IndividualLines)
1869  {
1870  if (submesh.elementCount)
1871  for (unsigned int n = 0; n < submesh.elementCount; ++n)
1872  lineSubmesh.elements[lineElementIndex++] = indexOffset + submesh.elements[n];
1873  else
1874  for (unsigned int n = 0; n < submesh.vertexCount; ++n)
1875  lineSubmesh.elements[lineElementIndex++] = indexOffset + n;
1876  }
1877  else if (submesh.elementAssemblyMethod == VuoMesh_LineStrip)
1878  {
1879  // Expand the line strip to individual lines.
1880  if (submesh.elementCount)
1881  for (unsigned int n = 1; n < submesh.elementCount; ++n)
1882  {
1883  lineSubmesh.elements[lineElementIndex++] = indexOffset + submesh.elements[n-1];
1884  lineSubmesh.elements[lineElementIndex++] = indexOffset + submesh.elements[n ];
1885  }
1886  else
1887  for (unsigned int n = 1; n < submesh.vertexCount; ++n)
1888  {
1889  lineSubmesh.elements[lineElementIndex++] = indexOffset + n-1;
1890  lineSubmesh.elements[lineElementIndex++] = indexOffset + n;
1891  }
1892  }
1893  }
1894  else if (submesh.elementAssemblyMethod == VuoMesh_Points)
1895  {
1896  unsigned long indexOffset = pointVertexIndex;
1897  for (unsigned int n = 0; n < submesh.vertexCount; ++n)
1898  {
1899  VuoPoint3d p = (VuoPoint3d){submesh.positions[n].x, submesh.positions[n].y, submesh.positions[n].z};
1900  VuoPoint3d pt = VuoTransform_transformPoint((float*)modelviewMatrix, p);
1901  pointSubmesh.positions[pointVertexIndex] = (VuoPoint4d){pt.x, pt.y, pt.z, 1};
1902 
1903  if (submesh.normals)
1904  {
1905  VuoPoint3d r = (VuoPoint3d){submesh.normals[n].x, submesh.normals[n].y, submesh.normals[n].z};
1906  VuoPoint3d rt = VuoTransform_transformVector((float*)modelviewMatrix, r);
1907  pointSubmesh.normals[pointVertexIndex] = (VuoPoint4d){rt.x, rt.y, rt.z, 1};
1908  }
1909 
1910  if (submesh.textureCoordinates)
1911  pointSubmesh.textureCoordinates[pointVertexIndex] = submesh.textureCoordinates[n];
1912 
1913  ++pointVertexIndex;
1914  }
1915 
1916  if (submesh.elementCount)
1918  for (unsigned int n = 0; n < submesh.elementCount; ++n)
1919  pointSubmesh.elements[pointElementIndex++] = indexOffset + submesh.elements[n];
1920  else
1921  {
1922  for (unsigned int n = 0; n < submesh.vertexCount; ++n)
1923  pointSubmesh.elements[pointElementIndex++] = indexOffset + n;
1924  }
1925  }
1926  }
1927  return true;
1928  });
1929 
1930 
1931  VuoMesh mesh = VuoMesh_make(submeshCount);
1932  int submeshIndex = 0;
1933  if (trianglePrimitiveCount)
1934  {
1935  if (calculateTangents)
1936  VuoMeshUtility_calculateTangents(&triangleSubmesh);
1937  if (!anyTextureCoordinates)
1938  {
1939  free(triangleSubmesh.textureCoordinates);
1940  triangleSubmesh.textureCoordinates = NULL;
1941  }
1942  mesh->submeshes[submeshIndex++] = triangleSubmesh;
1943  }
1944  if (linePrimitiveCount)
1945  {
1946  if (!anyTextureCoordinates)
1947  {
1948  free(lineSubmesh.textureCoordinates);
1949  lineSubmesh.textureCoordinates = NULL;
1950  }
1951  mesh->submeshes[submeshIndex++] = lineSubmesh;
1952  }
1953  if (pointPrimitiveCount)
1954  {
1955  if (!anyTextureCoordinates)
1956  {
1957  free(pointSubmesh.textureCoordinates);
1958  pointSubmesh.textureCoordinates = NULL;
1959  }
1960  mesh->submeshes[submeshIndex++] = pointSubmesh;
1961  }
1962 
1963  return VuoSceneObject_make(mesh, NULL, VuoTransform_makeIdentity(), NULL);
1964 }
1965 
1966 #define CSGJS_HEADER_ONLY
1967 #include "csgjs.cc"
1968 
1972 static csgjs_model VuoSceneObject_getCsgjsModel(const VuoSceneObject so)
1973 {
1974  VuoSceneObject flat = VuoSceneObject_flatten(so, false);
1975  if (!flat.mesh)
1976  return csgjs_model();
1977 
1978  VuoSceneObject_retain(flat);
1979  VuoDefer(^{ VuoSceneObject_release(flat); });
1980 
1982  return csgjs_model();
1983 
1984  VuoSubmesh submesh = flat.mesh->submeshes[0];
1985  csgjs_model cm;
1986  for (unsigned int n = 0; n < submesh.vertexCount; ++n)
1987  {
1988  csgjs_vertex v;
1989  v.pos = csgjs_vector(submesh.positions[n].x, submesh.positions[n].y, submesh.positions[n].z);
1990  if (submesh.normals)
1991  v.normal = csgjs_vector(submesh.normals[n].x, submesh.normals[n].y, submesh.normals[n].z);
1992  if (submesh.textureCoordinates)
1993  v.uv = csgjs_vector(submesh.textureCoordinates[n].x, submesh.textureCoordinates[n].y, 0);
1994  cm.vertices.push_back(v);
1995  }
1996  for (unsigned int n = 0; n < submesh.elementCount; ++n)
1997  cm.indices.push_back(submesh.elements[n]);
1998 
1999  return cm;
2000 }
2001 
2006 {
2007  VuoSubmesh submesh = VuoSubmesh_make(cm.vertices.size(), cm.indices.size());
2008 
2009  const csgjs_vertex *vertex = &cm.vertices[0];
2010  for (unsigned int n = 0; n < submesh.vertexCount; ++n)
2011  {
2012  submesh.positions[n] = (VuoPoint4d){vertex[n].pos.x,vertex[n].pos.y,vertex[n].pos.z,1};
2013  submesh.normals[n] = (VuoPoint4d){vertex[n].normal.x,vertex[n].normal.y,vertex[n].normal.z,1};
2014  submesh.textureCoordinates[n] = (VuoPoint4d){vertex[n].uv.x,vertex[n].uv.y,vertex[n].uv.z,1};
2015  }
2016 
2017  const int *index = &cm.indices[0];
2018  for (unsigned int n = 0; n < submesh.elementCount; ++n)
2019  submesh.elements[n] = index[n];
2020 
2022 
2023  VuoMesh mesh = VuoMesh_makeFromSingleSubmesh(submesh);
2024  return VuoSceneObject_make(mesh, NULL, VuoTransform_makeIdentity(), NULL);
2025 }
2026 
2031 static float convertQualityToEpsilon(float quality)
2032 {
2033  return pow(10, -VuoReal_clamp(quality, 0, 1) * 5.);
2034 }
2035 
2040 {
2041  float epsilon = convertQualityToEpsilon(quality);
2042 
2043  unsigned long objectCount = VuoListGetCount_VuoSceneObject(objects);
2044  if (objectCount == 0)
2045  return VuoSceneObject_makeEmpty();
2046  if (objectCount == 1)
2047  return VuoListGetValue_VuoSceneObject(objects, 1);
2048 
2049  dispatch_queue_t queue = dispatch_queue_create("org.vuo.sceneobject.union", DISPATCH_QUEUE_CONCURRENT);
2050 
2051  csgjs_model *models = new csgjs_model[objectCount];
2052  for (unsigned long i = 0; i < objectCount; ++i)
2053  dispatch_async(queue, ^{
2055  });
2056 
2057  dispatch_barrier_sync(queue, ^{});
2058  dispatch_release(queue);
2059 
2060  csgjs_model cu = models[0];
2061  for (unsigned long i = 1; i < objectCount; ++i)
2062  cu = csgjs_union(cu, models[i], epsilon);
2063  delete[] models;
2065 }
2066 
2071 {
2072  float epsilon = convertQualityToEpsilon(quality);
2073 
2074  dispatch_semaphore_t finished = dispatch_semaphore_create(0);
2075 
2076  __block csgjs_model ca;
2077  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2079  dispatch_semaphore_signal(finished);
2080  });
2081 
2082  csgjs_model cb = VuoSceneObject_getCsgjsModel(b);
2083 
2084  dispatch_semaphore_wait(finished, DISPATCH_TIME_FOREVER);
2085  dispatch_release(finished);
2086 
2087  csgjs_model d = csgjs_difference(ca, cb, epsilon);
2089 }
2090 
2095 {
2096  float epsilon = convertQualityToEpsilon(quality);
2097 
2098  unsigned long objectCount = VuoListGetCount_VuoSceneObject(objects);
2099  if (objectCount == 0)
2100  return VuoSceneObject_makeEmpty();
2101  if (objectCount == 1)
2102  return VuoListGetValue_VuoSceneObject(objects, 1);
2103 
2104  dispatch_queue_t queue = dispatch_queue_create("org.vuo.sceneobject.intersect", DISPATCH_QUEUE_CONCURRENT);
2105 
2106  csgjs_model *models = new csgjs_model[objectCount];
2107  for (unsigned long i = 0; i < objectCount; ++i)
2108  dispatch_async(queue, ^{
2110  });
2111 
2112  dispatch_barrier_sync(queue, ^{});
2113  dispatch_release(queue);
2114 
2115  csgjs_model ci = models[0];
2116  for (unsigned long i = 1; i < objectCount; ++i)
2117  ci = csgjs_intersection(ci, models[i], epsilon);
2118  delete[] models;
2120 }