Vuo  2.3.0
VuoSceneRenderer.cc
Go to the documentation of this file.
1 
10 #include <list>
11 #include <map>
12 using namespace std;
13 
14 #include "VuoSceneRenderer.h"
15 
16 #include "VuoCglPixelFormat.h"
17 #include "VuoImageText.h"
18 #include "VuoSceneText.h"
19 
20 #include <CoreServices/CoreServices.h>
21 
22 #include <OpenGL/CGLMacro.h>
24 #define glGenVertexArrays glGenVertexArraysAPPLE
25 #define glBindVertexArray glBindVertexArrayAPPLE
26 #define glDeleteVertexArrays glDeleteVertexArraysAPPLE
28 
29 #include "module.h"
30 
31 extern "C"
32 {
33 #ifdef VUO_COMPILER
35  "title" : "VuoSceneRenderer",
36  "dependencies" : [
37  "VuoBoolean",
38  "VuoImage",
39  "VuoImageColorDepth",
40  "VuoImageText",
41  "VuoSceneObject",
42  "VuoSceneText",
43  "VuoText",
44  "VuoList_VuoSceneObject",
45  "VuoGlContext"
46  ]
47  });
48 #endif
49 }
50 
51 #ifdef VUO_PROFILE
52 #include <string>
53 typedef std::pair<std::string, double> VuoProfileEntry;
54 static bool VuoProfileSort(const VuoProfileEntry &first, const VuoProfileEntry &second)
55 {
56  return first.second < second.second;
57 }
58 #endif
59 
60 typedef std::pair<VuoMesh, VuoShader> VuoSceneRendererMeshShader;
61 typedef std::map<VuoSceneRendererMeshShader, GLuint> VuoSceneRendererMeshShaderVAOs;
62 
67 {
68 public:
69  GLuint vao;
70 
71  enum
72  {
73  RealSize_Inherit,
74  RealSize_True,
75  } overrideIsRealSize;
77 };
78 
82 typedef struct
83 {
84  VuoSceneObject so;
86  float modelviewMatrix[16];
88 
93 {
94 public:
95  dispatch_semaphore_t scenegraphSemaphore;
96  unsigned int viewportWidth;
97  unsigned int viewportHeight;
98  float backingScaleFactor;
99 
103 
106  VuoShader sharedTextOverrideShader;
107 
108  list<VuoSceneRenderer_TreeRenderState> opaqueObjects;
109  list<VuoSceneRenderer_TreeRenderState> potentiallyTransparentObjects;
116 
118 
119  VuoShader vignetteShader;
120  VuoSceneObject vignetteQuad;
121  VuoSceneRendererInternal_object vignetteQuadInternal;
122 
123  float projectionMatrix[16];
124  float cameraMatrixInverse[16];
125  VuoText cameraName;
126  VuoSceneObject camera;
127  VuoBoolean useLeftCamera;
128 
129  VuoColor ambientColor;
130  float ambientBrightness;
131  VuoList_VuoSceneObject pointLights;
132  VuoList_VuoSceneObject spotLights;
133 
134  GLint glContextRendererID;
135 
140  struct glState
141  {
142  bool vGL_DEPTH_MASK;
143  bool vGL_DEPTH_TEST;
144 
145  bool vGL_CULL_FACE;
146  GLenum vGL_CULL_FACE_MODE;
147 
148  GLenum vGL_BLEND_SRC_RGB;
149  GLenum vGL_BLEND_DST_RGB;
150  GLenum vGL_BLEND_SRC_ALPHA;
151  GLenum vGL_BLEND_DST_ALPHA;
152 
153  GLenum vGL_BLEND_EQUATION_RGB;
154  GLenum vGL_BLEND_EQUATION_ALPHA;
155 
156  bool vGL_SAMPLE_ALPHA_TO_COVERAGE;
157  bool vGL_SAMPLE_ALPHA_TO_ONE;
158  } glState;
159 
160  // State for VuoSceneRenderer_renderInternal().
161  GLuint renderBuffer;
162  GLuint renderDepthBuffer;
163  GLuint outputFramebuffer;
164  GLuint outputFramebuffer2;
165 
166 #ifdef VUO_PROFILE
167  std::list<VuoProfileEntry> profileTimes;
168 #endif
169 };
170 
174 #define VuoSceneRenderer_setGL(cap, value) \
175  do { \
176  if (sceneRenderer->glState.v ## cap != value) \
177  { \
178  if (value) \
179  glEnable(cap); \
180  else \
181  glDisable(cap); \
182  sceneRenderer->glState.v ## cap = value; \
183  } \
184  } while(0)
185 
189 #define VuoSceneRenderer_setGLDepthMask(value) \
190  do { \
191  if (sceneRenderer->glState.vGL_DEPTH_MASK != value) \
192  { \
193  glDepthMask(value); \
194  sceneRenderer->glState.vGL_DEPTH_MASK = value; \
195  } \
196  } while(0)
197 
201 #define VuoSceneRenderer_setGLFaceCulling(value) \
202  do { \
203  if (sceneRenderer->glState.vGL_CULL_FACE_MODE != value) \
204  { \
205  glCullFace(value); \
206  sceneRenderer->glState.vGL_CULL_FACE_MODE = value; \
207  } \
208  } while(0)
209 
213 #define VuoSceneRenderer_setGLBlendFunction(srcRGB, dstRGB, srcAlpha, dstAlpha) \
214  do { \
215  if (sceneRenderer->glState.vGL_BLEND_SRC_RGB != srcRGB \
216  || sceneRenderer->glState.vGL_BLEND_DST_RGB != dstRGB \
217  || sceneRenderer->glState.vGL_BLEND_SRC_ALPHA != srcAlpha \
218  || sceneRenderer->glState.vGL_BLEND_DST_ALPHA != dstAlpha) \
219  { \
220  glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); \
221  sceneRenderer->glState.vGL_BLEND_SRC_RGB = srcRGB; \
222  sceneRenderer->glState.vGL_BLEND_DST_RGB = dstRGB; \
223  sceneRenderer->glState.vGL_BLEND_SRC_ALPHA = srcAlpha; \
224  sceneRenderer->glState.vGL_BLEND_DST_ALPHA = dstAlpha; \
225  } \
226  } while(0)
227 
231 #define VuoSceneRenderer_setGLBlendEquation(modeRGB, modeAlpha) \
232  do { \
233  if (sceneRenderer->glState.vGL_BLEND_EQUATION_RGB != modeRGB \
234  || sceneRenderer->glState.vGL_BLEND_EQUATION_ALPHA != modeAlpha) \
235  { \
236  glBlendEquationSeparate(modeRGB, modeAlpha); \
237  sceneRenderer->glState.vGL_BLEND_EQUATION_RGB = modeRGB; \
238  sceneRenderer->glState.vGL_BLEND_EQUATION_ALPHA = modeAlpha; \
239  } \
240  } while(0)
241 
243 
245 
246 #if VUO_PRO
247 #include "pro/VuoSceneRendererPro.h"
248 #endif
249 
255 VuoSceneRenderer VuoSceneRenderer_make(float backingScaleFactor)
256 {
257 // VDebugLog("backingScaleFactor=%g", backingScaleFactor);
259  VuoRegister(sceneRenderer, VuoSceneRenderer_destroy);
260 
261  sceneRenderer->scenegraphSemaphore = dispatch_semaphore_create(1);
262  sceneRenderer->rootSceneObjectPendingValid = false;
263  sceneRenderer->rootSceneObjectPendingUpdated = false;
264  sceneRenderer->rootSceneObjectValid = false;
265  sceneRenderer->sharedTextOverrideShader = NULL;
266  sceneRenderer->shouldSortByDepth = false;
267  sceneRenderer->cameraName = NULL;
268  sceneRenderer->camera = VuoSceneObject_makeDefaultCamera();
269  VuoSceneObject_retain(sceneRenderer->camera);
270  sceneRenderer->backingScaleFactor = backingScaleFactor;
271  sceneRenderer->viewportWidth = 0;
272  sceneRenderer->viewportHeight = 0;
273 
274  // sceneRenderer->glState gets initialized in VuoSceneRenderer_draw.
275 
276  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
277  sceneRenderer->vignetteShader = NULL;
278  sceneRenderer->vignetteQuad = nullptr;
279 #if VUO_PRO
280  VuoSceneRendererPro_init(sceneRenderer, cgl_ctx);
281 #endif
282 
283  glGenRenderbuffersEXT(1, &sceneRenderer->renderBuffer);
284  glGenRenderbuffersEXT(1, &sceneRenderer->renderDepthBuffer);
285  glGenFramebuffers(1, &sceneRenderer->outputFramebuffer);
286  glGenFramebuffers(1, &sceneRenderer->outputFramebuffer2);
287 
288 #pragma clang diagnostic push
289 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
290  CGLGetParameter(cgl_ctx, kCGLCPCurrentRendererID, &sceneRenderer->glContextRendererID);
291 #pragma clang diagnostic pop
292  });
293 
294  return (VuoSceneRenderer)sceneRenderer;
295 }
296 
298 
304 {
305  VuoSceneObject camera;
306  if (sceneRenderer->rootSceneObjectValid)
307  {
308  if (VuoSceneObject_findCamera(sceneRenderer->rootSceneObject, sceneRenderer->cameraName, &camera))
309  {
310  VuoSceneObject_retain(camera);
311  VuoSceneObject_release(sceneRenderer->camera);
312  sceneRenderer->camera = camera;
313  return;
314  }
315 
316  if (sceneRenderer->cameraName && strlen(sceneRenderer->cameraName) != 0)
317  // Search again, and this time just pick the first camera we find.
318  if (VuoSceneObject_findCamera(sceneRenderer->rootSceneObject, "", &camera))
319  {
320  VuoSceneObject_retain(camera);
321  VuoSceneObject_release(sceneRenderer->camera);
322  sceneRenderer->camera = camera;
323  return;
324  }
325  }
326 
328  VuoSceneObject_retain(camera);
329  VuoSceneObject_release(sceneRenderer->camera);
330  sceneRenderer->camera = camera;
331 }
332 
339 void VuoSceneRenderer_regenerateProjectionMatrix(VuoSceneRenderer sr, unsigned int width, unsigned int height)
340 {
341 // VDebugLog("%dx%d", width, height);
342  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
343 
344  dispatch_semaphore_wait(sceneRenderer->scenegraphSemaphore, DISPATCH_TIME_FOREVER);
345 
346  // Store the viewport size (in case we need to regenerate the projection matrix later)
347  sceneRenderer->viewportWidth = width;
348  sceneRenderer->viewportHeight = height;
349 
352 
353  dispatch_semaphore_signal(sceneRenderer->scenegraphSemaphore);
354 }
355 
362 {
363  float cameraDistanceMin = VuoSceneObject_getCameraDistanceMin(sceneRenderer->camera);
364  float cameraDistanceMax = VuoSceneObject_getCameraDistanceMax(sceneRenderer->camera);
365 
366  // Build a projection matrix for a camera located at the origin, facing along the -z axis.
367  float aspectRatio = (float)sceneRenderer->viewportWidth/(float)sceneRenderer->viewportHeight;
368  VuoSceneObjectSubType type = VuoSceneObject_getType(sceneRenderer->camera);
369  if (type == VuoSceneObjectSubType_PerspectiveCamera)
370  {
371  float halfFieldOfView = (VuoSceneObject_getCameraFieldOfView(sceneRenderer->camera) * (float)M_PI / 180.f) / 2.f;
372 
373  // left matrix column
374  sceneRenderer->projectionMatrix[ 0] = 1.f/tanf(halfFieldOfView);
375  sceneRenderer->projectionMatrix[ 1] = 0;
376  sceneRenderer->projectionMatrix[ 2] = 0;
377  sceneRenderer->projectionMatrix[ 3] = 0;
378 
379  sceneRenderer->projectionMatrix[ 4] = 0;
380  sceneRenderer->projectionMatrix[ 5] = aspectRatio/tanf(halfFieldOfView);
381  sceneRenderer->projectionMatrix[ 6] = 0;
382  sceneRenderer->projectionMatrix[ 7] = 0;
383 
384  sceneRenderer->projectionMatrix[ 8] = 0;
385  sceneRenderer->projectionMatrix[ 9] = 0;
386  sceneRenderer->projectionMatrix[10] = (cameraDistanceMax + cameraDistanceMin) / (cameraDistanceMin - cameraDistanceMax);
387  sceneRenderer->projectionMatrix[11] = -1.f;
388 
389  // right matrix column
390  sceneRenderer->projectionMatrix[12] = 0;
391  sceneRenderer->projectionMatrix[13] = 0;
392  sceneRenderer->projectionMatrix[14] = 2.f * cameraDistanceMax * cameraDistanceMin / (cameraDistanceMin - cameraDistanceMax);
393  sceneRenderer->projectionMatrix[15] = 0;
394  }
395  else if (type == VuoSceneObjectSubType_StereoCamera)
396  {
397  float cameraIntraocularDistance = VuoSceneObject_getCameraIntraocularDistance(sceneRenderer->camera);
398  float halfFieldOfView = (VuoSceneObject_getCameraFieldOfView(sceneRenderer->camera) * (float)M_PI / 180.f) / 2.f;
399  float top = cameraDistanceMin * tanf(halfFieldOfView);
400  float right = aspectRatio * top;
401  float frustumshift = (cameraIntraocularDistance / 2.f) * cameraDistanceMin / VuoSceneObject_getCameraConfocalDistance(sceneRenderer->camera);
402  if (!sceneRenderer->useLeftCamera)
403  frustumshift *= -1.f;
404 
405  // column 0
406  sceneRenderer->projectionMatrix[ 0] = 1.f/tanf(halfFieldOfView);
407  sceneRenderer->projectionMatrix[ 1] = 0;
408  sceneRenderer->projectionMatrix[ 2] = 0;
409  sceneRenderer->projectionMatrix[ 3] = 0;
410 
411  // column 1
412  sceneRenderer->projectionMatrix[ 4] = 0;
413  sceneRenderer->projectionMatrix[ 5] = aspectRatio/tanf(halfFieldOfView);
414  sceneRenderer->projectionMatrix[ 6] = 0;
415  sceneRenderer->projectionMatrix[ 7] = 0;
416 
417  // column 2
418  sceneRenderer->projectionMatrix[ 8] = 2.f * frustumshift / right;
419  sceneRenderer->projectionMatrix[ 9] = 0;
420  sceneRenderer->projectionMatrix[10] = (cameraDistanceMax + cameraDistanceMin) / (cameraDistanceMin - cameraDistanceMax);
421  sceneRenderer->projectionMatrix[11] = -1.f;
422 
423  // column 3
424  sceneRenderer->projectionMatrix[12] = (sceneRenderer->useLeftCamera ? 1.f : -1.f) * cameraIntraocularDistance / 2.f;
425  sceneRenderer->projectionMatrix[13] = 0;
426  sceneRenderer->projectionMatrix[14] = 2.f * cameraDistanceMax * cameraDistanceMin / (cameraDistanceMin - cameraDistanceMax);
427  sceneRenderer->projectionMatrix[15] = 0;
428  }
429  else if (type == VuoSceneObjectSubType_OrthographicCamera)
430  {
431  float cameraWidth = VuoSceneObject_getCameraWidth(sceneRenderer->camera);
432  float halfWidth = cameraWidth / 2.f;
433 
434  // left matrix column
435  sceneRenderer->projectionMatrix[ 0] = 1.f/halfWidth;
436  sceneRenderer->projectionMatrix[ 1] = 0;
437  sceneRenderer->projectionMatrix[ 2] = 0;
438  sceneRenderer->projectionMatrix[ 3] = 0;
439 
440  sceneRenderer->projectionMatrix[ 4] = 0;
441  sceneRenderer->projectionMatrix[ 5] = aspectRatio/halfWidth;
442  sceneRenderer->projectionMatrix[ 6] = 0;
443  sceneRenderer->projectionMatrix[ 7] = 0;
444 
445  sceneRenderer->projectionMatrix[ 8] = 0;
446  sceneRenderer->projectionMatrix[ 9] = 0;
447  sceneRenderer->projectionMatrix[10] = -2.f / (cameraDistanceMax - cameraDistanceMin);
448  sceneRenderer->projectionMatrix[11] = 0;
449 
450  // right matrix column
451  sceneRenderer->projectionMatrix[12] = 0;
452  sceneRenderer->projectionMatrix[13] = 0;
453  sceneRenderer->projectionMatrix[14] = -(cameraDistanceMax + cameraDistanceMin) / (cameraDistanceMax - cameraDistanceMin);
454  sceneRenderer->projectionMatrix[15] = 1;
455  }
456  else if (type == VuoSceneObjectSubType_FisheyeCamera)
457  {
458  bzero(sceneRenderer->projectionMatrix, sizeof(float)*16);
459 #if VUO_PRO
460  VuoSceneRendererPro_generateFisheyeProjectionMatrix(sceneRenderer, sceneRenderer->camera, aspectRatio);
461 #endif
462  }
463  else
464  VUserLog("Unknown type %d", type);
465 
466 
467  // Transform the scene by the inverse of the camera's transform.
468  // (Don't move the ship around the universe: move the universe around the ship.)
469  {
470  float cameraMatrix[16];
471  VuoTransform_getMatrix(VuoSceneObject_getTransform(sceneRenderer->camera), cameraMatrix);
472 
473  float invertedCameraMatrix[16];
474  VuoTransform_invertMatrix4x4(cameraMatrix, invertedCameraMatrix);
475 
476  VuoTransform_copyMatrix4x4(invertedCameraMatrix, sceneRenderer->cameraMatrixInverse);
477  }
478 }
479 
485 static void VuoSceneRenderer_addUniformSuffix(char *address, int i, const char *suffix)
486 {
487  if (i < 10)
488  *(address++) = '0' + i;
489  else
490  {
491  *(address++) = '1';
492  *(address++) = '0' + (i - 10);
493  }
494 
495  strcpy(address, suffix);
496 }
497 
503 void VuoSceneRenderer_checkDataBounds(CGLContextObj cgl_ctx, VuoMesh mesh, GLuint positionAttribute)
504 {
505  GLint enabled = -1;
506  glGetVertexAttribiv(positionAttribute, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled);
507  if (enabled != 1)
508  {
509  VUserLog("Error: Vertex attrib array isn't enabled.");
510  return;
511  }
512 
513  unsigned int vertexCount, combinedBuffer, elementCount;
514  VuoMesh_getGPUBuffers(mesh, &vertexCount, &combinedBuffer, nullptr, nullptr, nullptr, &elementCount, nullptr);
515 
516  GLint vaovbo = -1;
517  glGetVertexAttribiv(positionAttribute, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &vaovbo);
518  if (vaovbo != (GLint)combinedBuffer)
519  VUserLog("Error: GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING (%d) doesn't match submesh's VBO (%d).", vaovbo, combinedBuffer);
520 
521  GLint vertexBufferBound = -1;
522  glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vertexBufferBound);
523  if (vertexBufferBound)
524  VUserLog("Error: GL_ARRAY_BUFFER_BINDING is %d (it should probably be 0, since we're using a VAO).", vertexBufferBound);
525 
526  GLint elementBufferBound = -1;
527  glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &elementBufferBound);
528  if (elementCount && !elementBufferBound)
529  VUserLog("Error: GL_ELEMENT_ARRAY_BUFFER_BINDING is 0 (it should be nonzero, since we're trying to draw elements).");
530 
531  GLint combinedBufferSize = -1;
532  glBindBuffer(GL_ARRAY_BUFFER, combinedBuffer);
533  glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &combinedBufferSize);
534  glBindBuffer(GL_ARRAY_BUFFER, 0);
535  if (combinedBufferSize < 4)
536  VUserLog("Error: combinedBuffer is unrealistically small (%d bytes).", combinedBufferSize);
537 
538  GLint attribStride = -1;
539  glGetVertexAttribiv(positionAttribute, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &attribStride);
540 
541  GLint attribComponents = -1;
542  glGetVertexAttribiv(positionAttribute, GL_VERTEX_ATTRIB_ARRAY_SIZE, &attribComponents);
543 
544  GLint attribType = -1;
545  glGetVertexAttribiv(positionAttribute, GL_VERTEX_ATTRIB_ARRAY_TYPE, &attribType);
546 
547  void *attribBase = (void *)0;
548  glGetVertexAttribPointerv(positionAttribute, GL_VERTEX_ATTRIB_ARRAY_POINTER, &attribBase);
549 
550  unsigned int bytesPerComponent;
551  if (attribType == GL_UNSIGNED_INT)
552  bytesPerComponent = 1;
553  else if (attribType == GL_FLOAT)
554  bytesPerComponent = 4;
555  else
556  {
557  VUserLog("Error: Unknown combinedBuffer type %s.", VuoGl_stringForConstant((GLenum)attribType));
558  return;
559  }
560 
561  unsigned int minIndex = 0;
562  unsigned int maxIndex = 0;
563  if (elementCount)
564  {
565  unsigned int elementBufferSize = VuoMesh_getElementBufferSize(mesh);
566  if (elementBufferSize != elementCount * sizeof(unsigned int))
567  VUserLog("Error: elementBufferSize doesn't match elementCount.");
568 
569  unsigned int *b = (unsigned int *)malloc(elementBufferSize);
570  glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, elementBufferSize, b);
571 
572  for (unsigned int i = 0; i < elementCount; ++i)
573  {
574  if (b[i] > maxIndex)
575  maxIndex = b[i];
576  if (b[i] < minIndex)
577  minIndex = b[i];
578  }
579 
580  free(b);
581  }
582  else
583  maxIndex = vertexCount - 1;
584 
585  long startWithinVBO = (long)attribBase + (unsigned int)attribStride * minIndex;
586  long endWithinVBO = (long)attribBase +(unsigned int) attribStride * maxIndex + (unsigned int)attribComponents * bytesPerComponent;
587 
588  if (startWithinVBO < 0)
589  VUserLog("Error: start < 0");
590  if (endWithinVBO < 0)
591  VUserLog("Error: end < 0");
592  if (startWithinVBO > endWithinVBO)
593  VUserLog("Error: start > end");
594  if (endWithinVBO > combinedBufferSize)
595  VUserLog("Error: end > combinedBufferSize");
596 
597  VGL();
598 }
599 
608 static void VuoSceneRenderer_drawSceneObject(VuoSceneObject so, VuoSceneRendererInternal_object *soi, float projectionMatrix[16], float modelviewMatrix[16], VuoSceneRendererInternal *sceneRenderer, VuoGlContext glContext)
609 {
610  // Apply the overrides just within this function's scope.
611  bool isRealSize = VuoSceneObject_isRealSize(so);
612  if (soi->overrideIsRealSize != VuoSceneRendererInternal_object::RealSize_Inherit)
613  isRealSize = soi->overrideIsRealSize;
614  VuoShader shader = VuoSceneObject_getShader(so);
615  if (soi->overrideShader)
616  shader = soi->overrideShader;
617  if (!shader)
618  return;
619 
620 
621  if (VuoSceneObject_getType(so) == VuoSceneObjectSubType_Text && !VuoText_isEmpty(VuoSceneObject_getText(so)))
622  {
623  // Text has to be rasterized while rendering (instead of while uploading)
624  // since resizing the window (which doesn't re-upload) could cause the text to change size
625  // (and we want to ensure the text is crisp, so we can't just scale the pre-rasterized bitmap).
626 
627  float verticalScale = 1.;
628  float rotationZ = 0.;
629  float wrapWidth = VuoSceneObject_getTextWrapWidth(so);
632  {
633  font.pointSize *= (double)sceneRenderer->viewportWidth / (VuoGraphicsWindowDefaultWidth * sceneRenderer->backingScaleFactor);
634  wrapWidth *= (double)sceneRenderer->viewportWidth / (VuoGraphicsWindowDefaultWidth * sceneRenderer->backingScaleFactor);
635 
636  VuoTransform transform = VuoTransform_makeFromMatrix4x4(modelviewMatrix);
637  font.pointSize *= transform.scale.x;
638  wrapWidth *= transform.scale.x;
639  verticalScale = transform.scale.y / transform.scale.x;
640  rotationZ = VuoTransform_getEuler(transform).z;
641  }
642 
643  VuoPoint2d corners[4];
644  VuoImage textImage = VuoImage_makeText(VuoSceneObject_getText(so), font, sceneRenderer->backingScaleFactor, verticalScale, rotationZ, wrapWidth, corners);
645 
646  VuoPoint2d anchorOffset = VuoSceneText_getAnchorOffset(so, verticalScale, rotationZ, wrapWidth, sceneRenderer->viewportWidth, sceneRenderer->backingScaleFactor);
647  modelviewMatrix[12] += anchorOffset.x;
648  modelviewMatrix[13] += anchorOffset.y;
649 
650  VuoShader_setUniform_VuoImage(shader, "texture", textImage);
651  }
652 
653  VuoMesh mesh = VuoSceneObject_getMesh(so);
654  if (!mesh)
655  return;
656 
657  VuoImage image = VuoShader_getUniform_VuoImage(shader, "texture");
658  if (isRealSize && !image)
659  return;
660 
661  CGLContextObj cgl_ctx = (CGLContextObj)glContext;
662 
663 #ifdef VUO_PROFILE
664  GLuint timeElapsedQuery;
665  glGenQueries(1, &timeElapsedQuery);
666  glBeginQuery(GL_TIME_ELAPSED_EXT, timeElapsedQuery);
667 #endif
668 
669  GLint positionAttribute = -1;
670  if (VuoIsDebugEnabled())
671  if (!VuoShader_getAttributeLocations(shader, VuoMesh_getElementAssemblyMethod(mesh), cgl_ctx, &positionAttribute, NULL, NULL, NULL))
672  VDebugLog("Error: Couldn't fetch the 'position' attribute, needed to check data bounds.");
673 
674 
675  VuoGlProgram program;
676  if (!VuoShader_activate(shader, VuoMesh_getElementAssemblyMethod(mesh), cgl_ctx, &program))
677  {
678  VUserLog("Shader activation failed.");
679  return;
680  }
681 
682  {
683  GLint projectionMatrixUniform = VuoGlProgram_getUniformLocation(program, "projectionMatrix");
684  glUniformMatrix4fv(projectionMatrixUniform, 1, GL_FALSE, projectionMatrix);
685 
686  GLint useFisheyeProjectionUniform = VuoGlProgram_getUniformLocation(program, "useFisheyeProjection");
687  if (useFisheyeProjectionUniform != -1)
688  glUniform1i(useFisheyeProjectionUniform, VuoSceneObject_getType(sceneRenderer->camera) == VuoSceneObjectSubType_FisheyeCamera);
689 
690  GLint modelviewMatrixUniform = VuoGlProgram_getUniformLocation(program, "modelviewMatrix");
691 
692  if (isRealSize)
693  {
694  float billboardMatrix[16];
695  float *positions;
696  VuoMesh_getCPUBuffers(mesh, nullptr, &positions, nullptr, nullptr, nullptr, nullptr, nullptr);
697  VuoPoint2d mesh0 = (VuoPoint2d){ positions[0], positions[1] };
698  VuoTransform_getBillboardMatrix(image->pixelsWide, image->pixelsHigh, image->scaleFactor, VuoSceneObject_shouldPreservePhysicalSize(so), modelviewMatrix[12], modelviewMatrix[13], sceneRenderer->viewportWidth, sceneRenderer->viewportHeight, sceneRenderer->backingScaleFactor, mesh0, billboardMatrix);
699  glUniformMatrix4fv(modelviewMatrixUniform, 1, GL_FALSE, billboardMatrix);
700  }
701  else
702  glUniformMatrix4fv(modelviewMatrixUniform, 1, GL_FALSE, modelviewMatrix);
703 
704  GLint cameraMatrixInverseUniform = VuoGlProgram_getUniformLocation(program, "cameraMatrixInverse");
705  if (cameraMatrixInverseUniform != -1)
706  glUniformMatrix4fv(cameraMatrixInverseUniform, 1, GL_FALSE, sceneRenderer->cameraMatrixInverse);
707 
708  GLint cameraPositionUniform = VuoGlProgram_getUniformLocation(program, "cameraPosition");
709  if (cameraPositionUniform != -1)
710  {
711  VuoPoint3d p = VuoSceneObject_getTranslation(sceneRenderer->camera);
712  glUniform3f(cameraPositionUniform, p.x, p.y, p.z);
713  }
714 
715  GLint aspectRatioUniform = VuoGlProgram_getUniformLocation(program, "aspectRatio");
716  if (aspectRatioUniform != -1)
717  glUniform1f(aspectRatioUniform, (float)sceneRenderer->viewportWidth/(float)sceneRenderer->viewportHeight);
718 
719  GLint viewportSizeUniform = VuoGlProgram_getUniformLocation(program, "viewportSize");
720  if (viewportSizeUniform != -1)
721  glUniform2f(viewportSizeUniform, (float)sceneRenderer->viewportWidth, (float)sceneRenderer->viewportHeight);
722 
723  GLint ambientColorUniform = VuoGlProgram_getUniformLocation(program, "ambientColor");
724  if (ambientColorUniform != -1)
725  // Unlike typical VuoColors, convert these from sRGB-gamma2.2 into sRGB-linear,
726  // since that's what lighting.glsl expects.
727  glUniform4f(ambientColorUniform,
728  pow(sceneRenderer->ambientColor.r, 2.2) * sceneRenderer->ambientColor.a,
729  pow(sceneRenderer->ambientColor.g, 2.2) * sceneRenderer->ambientColor.a,
730  pow(sceneRenderer->ambientColor.b, 2.2) * sceneRenderer->ambientColor.a,
731  sceneRenderer->ambientColor.a);
732  GLint ambientBrightnessUniform = VuoGlProgram_getUniformLocation(program, "ambientBrightness");
733  if (ambientBrightnessUniform != -1)
734  glUniform1f(ambientBrightnessUniform, pow(sceneRenderer->ambientBrightness, 2.2));
735 
736  size_t uniformSize = 27; // strlen("pointLights[15].brightness")+1
737  char uniformName[uniformSize];
738 
739  int pointLightCount = VuoListGetCount_VuoSceneObject(sceneRenderer->pointLights);
740  GLint pointLightCountUniform = VuoGlProgram_getUniformLocation(program, "pointLightCount");
741  if (pointLightCountUniform != -1)
742  {
743  glUniform1i(pointLightCountUniform, pointLightCount);
744 
745  strlcpy(uniformName, "pointLights[", uniformSize);
746  const int prefixLength = 12; // strlen("pointLights[")
747 
748  for (int i=1; i<=pointLightCount; ++i)
749  {
750  VuoSceneObject pointLight = VuoListGetValue_VuoSceneObject(sceneRenderer->pointLights, i);
751 
752  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].color");
753  GLint colorUniform = VuoGlProgram_getUniformLocation(program, uniformName);
754  // Unlike typical VuoColors, convert these from sRGB-gamma2.2 into sRGB-linear,
755  // since that's what lighting.glsl expects.
756  VuoColor c = VuoSceneObject_getLightColor(pointLight);
757  glUniform4f(colorUniform,
758  pow(c.r, 2.2) * c.a,
759  pow(c.g, 2.2) * c.a,
760  pow(c.b, 2.2) * c.a,
761  c.a);
762 
763  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].brightness");
764  GLint brightnessUniform = VuoGlProgram_getUniformLocation(program, uniformName);
765  glUniform1f(brightnessUniform, pow(VuoSceneObject_getLightBrightness(pointLight), 2.2));
766 
767  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].position");
768  GLint positionUniform = VuoGlProgram_getUniformLocation(program, uniformName);
769  VuoPoint3d p = VuoSceneObject_getTranslation(pointLight);
770  glUniform3f(positionUniform, p.x, p.y, p.z);
771 
772  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].range");
773  GLint rangeUniform = VuoGlProgram_getUniformLocation(program, uniformName);
774  glUniform1f(rangeUniform, VuoSceneObject_getLightRange(pointLight));
775 
776  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].sharpness");
777  GLint sharpnessUniform = VuoGlProgram_getUniformLocation(program, uniformName);
778  glUniform1f(sharpnessUniform, VuoSceneObject_getLightSharpness(pointLight));
779  }
780  }
781 
782  int spotLightCount = VuoListGetCount_VuoSceneObject(sceneRenderer->spotLights);
783  GLint spotLightCountUniform = VuoGlProgram_getUniformLocation(program, "spotLightCount");
784  if (spotLightCountUniform != -1)
785  {
786  glUniform1i(spotLightCountUniform, spotLightCount);
787 
788  strlcpy(uniformName, "spotLights[", uniformSize);
789  const int prefixLength = 11; // strlen("spotLights[")
790 
791  for (int i=1; i<=spotLightCount; ++i)
792  {
793  VuoSceneObject spotLight = VuoListGetValue_VuoSceneObject(sceneRenderer->spotLights, i);
794 
795  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].color");
796  GLint colorUniform = VuoGlProgram_getUniformLocation(program, uniformName);
797  // Unlike typical VuoColors, convert these from sRGB-gamma2.2 into sRGB-linear,
798  // since that's what lighting.glsl expects.
799  VuoColor c = VuoSceneObject_getLightColor(spotLight);
800  glUniform4f(colorUniform,
801  pow(c.r, 2.2) * c.a,
802  pow(c.g, 2.2) * c.a,
803  pow(c.b, 2.2) * c.a,
804  c.a);
805 
806  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].brightness");
807  GLint brightnessUniform = VuoGlProgram_getUniformLocation(program, uniformName);
808  glUniform1f(brightnessUniform, pow(VuoSceneObject_getLightBrightness(spotLight), 2.2));
809 
810  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].position");
811  GLint positionUniform = VuoGlProgram_getUniformLocation(program, uniformName);
812  VuoPoint3d p = VuoSceneObject_getTranslation(spotLight);
813  glUniform3f(positionUniform, p.x, p.y, p.z);
814 
815  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].direction");
816  GLint directionUniform = VuoGlProgram_getUniformLocation(program, uniformName);
817  VuoPoint3d direction = VuoTransform_getDirection(VuoSceneObject_getTransform(spotLight));
818  glUniform3f(directionUniform, direction.x, direction.y, direction.z);
819 
820  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].cone");
821  GLint coneUniform = VuoGlProgram_getUniformLocation(program, uniformName);
822  glUniform1f(coneUniform, VuoSceneObject_getLightCone(spotLight));
823 
824  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].range");
825  GLint rangeUniform = VuoGlProgram_getUniformLocation(program, uniformName);
826  glUniform1f(rangeUniform, VuoSceneObject_getLightRange(spotLight));
827 
828  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].sharpness");
829  GLint sharpnessUniform = VuoGlProgram_getUniformLocation(program, uniformName);
830  glUniform1f(sharpnessUniform, VuoSceneObject_getLightSharpness(spotLight));
831  }
832  }
833 
834  VuoSceneRenderer_setGLDepthMask(!shader->isTransparent);
835 
836  VuoSceneRenderer_setGL(GL_SAMPLE_ALPHA_TO_COVERAGE, shader->useAlphaAsCoverage);
837  VuoSceneRenderer_setGL(GL_SAMPLE_ALPHA_TO_ONE, shader->useAlphaAsCoverage);
838 
840  if (blendMode == VuoBlendMode_Normal)
841  {
842  VuoSceneRenderer_setGL(GL_DEPTH_TEST, true);
843  VuoSceneRenderer_setGLBlendFunction(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
844  VuoSceneRenderer_setGLBlendEquation(GL_FUNC_ADD, GL_FUNC_ADD);
845  }
846  else
847  {
848  VuoSceneRenderer_setGL(GL_DEPTH_TEST, false);
849 
850  if (blendMode == VuoBlendMode_Multiply)
851  {
852  VuoSceneRenderer_setGLBlendFunction(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
853  VuoSceneRenderer_setGLBlendEquation(GL_FUNC_ADD, GL_FUNC_ADD);
854  }
855  else if (blendMode == VuoBlendMode_DarkerComponents)
856  {
858  VuoSceneRenderer_setGLBlendFunction(GL_ONE, GL_ONE, GL_ONE, GL_ONE);
859  VuoSceneRenderer_setGLBlendEquation(GL_MIN, GL_FUNC_ADD);
860  }
861  else if (blendMode == VuoBlendMode_LighterComponents)
862  {
863  VuoSceneRenderer_setGLBlendFunction(GL_ZERO, GL_ZERO, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
864  VuoSceneRenderer_setGLBlendEquation(GL_MAX, GL_FUNC_ADD);
865  }
866  else if (blendMode == VuoBlendMode_LinearDodge)
867  {
868  VuoSceneRenderer_setGLBlendFunction(GL_ONE, GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
869  VuoSceneRenderer_setGLBlendEquation(GL_FUNC_ADD, GL_FUNC_ADD);
870  }
871  else if (blendMode == VuoBlendMode_Subtract)
872  {
873  VuoSceneRenderer_setGLBlendFunction(GL_ONE, GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
874  VuoSceneRenderer_setGLBlendEquation(GL_FUNC_REVERSE_SUBTRACT, GL_FUNC_ADD);
875  }
876  }
877 
878  glBindVertexArray(soi->vao);
879 
880  GLenum mode = VuoMesh_getGlMode(mesh);
881 
882  unsigned int vertexCount, elementCount;
883  void *textureCoordinateOffset, *colorOffset;
884  VuoMesh_getGPUBuffers(mesh, &vertexCount, nullptr, nullptr, &textureCoordinateOffset, &colorOffset, &elementCount, nullptr);
885 
886  GLint hasTextureCoordinatesUniform = VuoGlProgram_getUniformLocation(program, "hasTextureCoordinates");
887  if (hasTextureCoordinatesUniform != -1)
888  glUniform1i(hasTextureCoordinatesUniform, textureCoordinateOffset != NULL);
889 
890  GLint hasVertexColorsUniform = VuoGlProgram_getUniformLocation(program, "hasVertexColors");
891  if (hasVertexColorsUniform != -1)
892  glUniform1i(hasVertexColorsUniform, colorOffset != nullptr);
893 
894  GLint primitiveHalfSizeUniform = VuoGlProgram_getUniformLocation(program, "primitiveHalfSize");
895  if (primitiveHalfSizeUniform != -1)
896  glUniform1f(primitiveHalfSizeUniform, VuoMesh_getPrimitiveSize(mesh) / 2);
897 
898  unsigned int faceCullingGL = VuoMesh_getFaceCullingGL(mesh);
899  bool enableCulling = faceCullingGL != GL_NONE && !shader->isTransparent;
900  VuoSceneRenderer_setGL(GL_CULL_FACE, enableCulling);
901  if (enableCulling)
902  VuoSceneRenderer_setGLFaceCulling(faceCullingGL);
903 
904  if (VuoIsDebugEnabled() && positionAttribute)
905  {
906  VGL();
907  VuoSceneRenderer_checkDataBounds(cgl_ctx, mesh, positionAttribute);
908  }
909 
910  unsigned long completeInputElementCount = VuoMesh_getCompleteElementCount(mesh);
911  if (elementCount)
912  glDrawElements(mode, completeInputElementCount, GL_UNSIGNED_INT, (void*)0);
913  else if (vertexCount)
914  glDrawArrays(mode, 0, completeInputElementCount);
915 
917  }
919 
920 #ifdef VUO_PROFILE
921  double seconds;
922  glEndQuery(GL_TIME_ELAPSED_EXT);
923  GLuint nanoseconds;
924  glGetQueryObjectuiv(timeElapsedQuery, GL_QUERY_RESULT, &nanoseconds);
925  seconds = ((double)nanoseconds) / NSEC_PER_SEC;
926  glDeleteQueries(1, &timeElapsedQuery);
927 
928  std::string description = so.name ? std::string(so.name) : "no name";
929  description += std::string(" (") + shader->name + ")";
930  sceneRenderer->profileTimes.push_back(make_pair(description, seconds));
931 #endif
932 }
933 
934 //static void VuoSceneRenderer_drawElement(VuoSceneObject so, float projectionMatrix[16], float compositeModelviewMatrix[16], VuoGlContext glContext, int element, float length);
935 
943 static void VuoSceneRenderer_drawSceneObjects(VuoSceneRendererInternal *sceneRenderer, VuoGlContext glContext, list<VuoSceneRenderer_TreeRenderState> &renderList)
944 {
945  for (auto currentState : renderList)
946  {
947  VuoSceneRenderer_drawSceneObject(currentState.so, currentState.soi, sceneRenderer->projectionMatrix, currentState.modelviewMatrix, sceneRenderer, glContext);
948 
949 // if (objectTypesToRender & VuoSceneRenderer_OpaqueObjects)
950 // {
951 // VuoSceneRenderer_drawElement(currentState.so, sceneRenderer->projectionMatrix, currentState.modelviewMatrix, glContext, 0, .08f); // Normals
952 // VuoSceneRenderer_drawElement(currentState.so, sceneRenderer->projectionMatrix, currentState.modelviewMatrix, glContext, 1, .08f); // Tangents
953 // VuoSceneRenderer_drawElement(currentState.so, sceneRenderer->projectionMatrix, currentState.modelviewMatrix, glContext, 2, .08f); // Bitangents
954 // }
955  }
956 }
957 
958 //static void VuoSceneRenderer_drawLights(VuoSceneRendererInternal *sceneRenderer);
961 static void VuoSceneRenderer_uploadSceneObjects(VuoSceneRendererInternal *sceneRenderer, VuoGlContext glContext, VuoPoint3d cameraTranslation);
962 
968 extern "C" void VuoSceneRenderer_draw(VuoSceneRenderer sr, CGLContextObj cgl_ctx)
969 {
970  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
971 
972  if (!sceneRenderer->rootSceneObjectPendingValid && !sceneRenderer->rootSceneObjectValid)
973  return;
974 
975  if (sceneRenderer->rootSceneObjectPendingUpdated)
976  {
977  // Release the old scenegraph.
978  if (sceneRenderer->rootSceneObjectValid)
979  {
981  VuoSceneRenderer_cleanupMeshShaderItems(sceneRenderer, cgl_ctx);
982  VuoSceneObject_release(sceneRenderer->rootSceneObject);
983  VuoRelease(sceneRenderer->pointLights);
984  VuoRelease(sceneRenderer->spotLights);
985  }
986 
987  // Upload the new scenegraph.
988  sceneRenderer->rootSceneObject = sceneRenderer->rootSceneObjectPending;
989  VuoSceneObject_retain(sceneRenderer->rootSceneObject);
990 
991  sceneRenderer->rootSceneObjectValid = true;
993 
994  VuoSceneRenderer_uploadSceneObjects(sceneRenderer, cgl_ctx, VuoSceneObject_getTranslation(sceneRenderer->camera));
995 
996  VuoSceneObject_findLights(sceneRenderer->rootSceneObject, &sceneRenderer->ambientColor, &sceneRenderer->ambientBrightness, &sceneRenderer->pointLights, &sceneRenderer->spotLights);
997  VuoRetain(sceneRenderer->pointLights);
998  VuoRetain(sceneRenderer->spotLights);
999 
1000  sceneRenderer->rootSceneObjectPendingUpdated = false;
1001 
1002 #if 0
1003  VLog("Render lists:");
1004  VLog(" Opaque:");
1005  for (auto currentState : sceneRenderer->opaqueObjects)
1006  {
1007  VLog(" '%s' @ %g,%g,%g with '%s'", currentState.so->name,
1008  currentState.modelviewMatrix[12],
1009  currentState.modelviewMatrix[13],
1010  currentState.modelviewMatrix[14],
1011  currentState.so->shader ? currentState.so->shader->name : NULL
1012  );
1013  }
1014  VLog(" Potentially Transparent:");
1015  for (auto currentState : sceneRenderer->potentiallyTransparentObjects)
1016  {
1017  VLog(" '%s' @ %g,%g,%g with '%s'", currentState.so->name,
1018  currentState.modelviewMatrix[12],
1019  currentState.modelviewMatrix[13],
1020  currentState.modelviewMatrix[14],
1021  currentState.so->shader->name
1022  );
1023  }
1024 #endif
1025  }
1026 
1027 
1028  // Start out with VuoGlContextPool::createContext()'s initial state.
1029  bzero(&sceneRenderer->glState, sizeof(sceneRenderer->glState));
1030  sceneRenderer->glState.vGL_DEPTH_MASK = true;
1031  sceneRenderer->glState.vGL_DEPTH_TEST = false;
1032  sceneRenderer->glState.vGL_CULL_FACE = true;
1033  sceneRenderer->glState.vGL_CULL_FACE_MODE = GL_BACK;
1034  sceneRenderer->glState.vGL_BLEND_SRC_RGB = GL_ONE;
1035  sceneRenderer->glState.vGL_BLEND_DST_RGB = GL_ONE_MINUS_SRC_ALPHA;
1036  sceneRenderer->glState.vGL_BLEND_SRC_ALPHA = GL_ONE;
1037  sceneRenderer->glState.vGL_BLEND_DST_ALPHA = GL_ONE_MINUS_SRC_ALPHA;
1038  sceneRenderer->glState.vGL_BLEND_EQUATION_RGB = GL_FUNC_ADD;
1039  sceneRenderer->glState.vGL_BLEND_EQUATION_ALPHA = GL_FUNC_ADD;
1040  sceneRenderer->glState.vGL_SAMPLE_ALPHA_TO_COVERAGE = false;
1041  sceneRenderer->glState.vGL_SAMPLE_ALPHA_TO_ONE = false;
1042 
1044 
1045  VuoSceneRenderer_drawSceneObjects(sceneRenderer, cgl_ctx, sceneRenderer->opaqueObjects);
1046 
1047  if (!sceneRenderer->potentiallyTransparentObjects.empty())
1048  VuoSceneRenderer_drawSceneObjects(sceneRenderer, cgl_ctx, sceneRenderer->potentiallyTransparentObjects);
1049 
1050  // Restore the context back to VuoGlContextPool::createContext()'s initial state.
1052  VuoSceneRenderer_setGL(GL_DEPTH_TEST, false);
1053  VuoSceneRenderer_setGL(GL_CULL_FACE, true );
1055  VuoSceneRenderer_setGLBlendFunction(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1056  VuoSceneRenderer_setGLBlendEquation(GL_FUNC_ADD, GL_FUNC_ADD);
1057  VuoSceneRenderer_setGL(GL_SAMPLE_ALPHA_TO_COVERAGE, false);
1058  VuoSceneRenderer_setGL(GL_SAMPLE_ALPHA_TO_ONE, false);
1059 
1060 
1061 // VuoSceneRenderer_drawLights(sceneRenderer);
1062 
1063 
1064 #if VUO_PRO
1065  if (VuoSceneObject_getType(sceneRenderer->camera) == VuoSceneObjectSubType_FisheyeCamera)
1066  VuoSceneRendererPro_drawVignette(sceneRenderer, cgl_ctx);
1067 #endif
1068 
1069 #ifdef VUO_PROFILE
1070  VL();
1071  VLog("Object render time (percent of a 60 Hz frame)");
1072  double totalPercent = 0;
1073  sceneRenderer->profileTimes.sort(VuoProfileSort);
1074  for (std::list<VuoProfileEntry>::iterator i = sceneRenderer->profileTimes.begin(); i != sceneRenderer->profileTimes.end(); ++i)
1075  {
1076  double objectPercent = i->second / (1./60.) * 100.;
1077  VLog(" %6.2f %s", objectPercent, i->first.c_str());
1078  totalPercent += objectPercent;
1079  }
1080  VLog(" ------ -----");
1081  VLog(" %6.2f total", totalPercent);
1082  sceneRenderer->profileTimes.clear();
1083 #endif
1084 
1085  glFlushRenderAPPLE();
1086 
1087  if (VuoIsDebugEnabled())
1088  {
1089  GLint rendererID;
1090 #pragma clang diagnostic push
1091 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1092  CGLGetParameter(cgl_ctx, kCGLCPCurrentRendererID, &rendererID);
1093 #pragma clang diagnostic pop
1094  if (rendererID != sceneRenderer->glContextRendererID)
1095  {
1096  VUserLog("OpenGL context %p's renderer changed to %s", cgl_ctx, VuoCglRenderer_getText(rendererID));
1097  sceneRenderer->glContextRendererID = rendererID;
1098  }
1099  }
1100 }
1101 
1109 /*static void VuoSceneRenderer_drawElement(VuoSceneObject so, float projectionMatrix[16], float compositeModelviewMatrix[16], VuoGlContext glContext, int element, float length) // TODO Enum type for element to debug
1110 {
1111  if (!so.mesh)
1112  return;
1113 
1114  CGLContextObj cgl_ctx = (CGLContextObj)glContext;
1115 
1116  glLoadIdentity();
1117 
1118  glMatrixMode(GL_PROJECTION);
1119  glLoadMatrixf(projectionMatrix);
1120 
1121  glMatrixMode(GL_MODELVIEW);
1122  glLoadMatrixf(compositeModelviewMatrix);
1123 
1124  glBegin(GL_LINES);
1125  bool first=true;
1126  for (unsigned int i = 0; i < so.mesh->submeshCount; i++)
1127  {
1128  VuoSubmesh submesh = so.mesh->submeshes[i];
1129  VuoSubmesh_download(&submesh);
1130 
1131  for(unsigned int m = 0; m < submesh.vertexCount; m++)
1132  {
1133  VuoPoint4d v = submesh.positions[m];
1134  VuoPoint4d n = (VuoPoint4d){0,0,1,1};
1135 
1136  switch(element)
1137  {
1138  case 0: // normals
1139  n = submesh.normals[m];
1140  glColor3f(1,.5,.5);
1141  break;
1142  case 1: // tangents
1143  n = submesh.tangents[m];
1144  glColor3f(.5,1,.5);
1145  break;
1146  case 2: // bitangents
1147  n = submesh.bitangents[m];
1148  glColor3f(.5,.5,1);
1149  break;
1150  }
1151 
1152  if (first)
1153  {
1154  VuoPoint3d n3 = VuoPoint3d_make(n.x,n.y,n.z);
1155  VUserLog("%p [%d]: %s, length %g",&so,element,VuoPoint4d_getSummary(n),VuoPoint3d_magnitude(n3));
1156 
1157  // Check orthogonality
1158  {
1159  VuoPoint3d n3 = VuoPoint3d_make(submesh.normals[m].x,submesh.normals[m].y,submesh.normals[m].z);
1160  VuoPoint3d t3 = VuoPoint3d_make(submesh.tangents[m].x,submesh.tangents[m].y,submesh.tangents[m].z);
1161  VuoPoint3d b3 = VuoPoint3d_make(submesh.bitangents[m].x,submesh.bitangents[m].y,submesh.bitangents[m].z);
1162 
1163  if (VuoPoint3d_dotProduct(n3,t3) > 0.0001)
1164  VUserLog(" n•t = %g; should be 0",VuoPoint3d_dotProduct(n3,t3));
1165  if (VuoPoint3d_dotProduct(n3,b3) > 0.0001)
1166  VUserLog(" n•b = %g; should be 0",VuoPoint3d_dotProduct(n3,b3));
1167  }
1168  }
1169  first=false;
1170 
1171  n = VuoPoint4d_add(v, VuoPoint4d_multiply(n, length));
1172 
1173  glVertex3d(v.x, v.y, v.z);
1174  glVertex3d(n.x, n.y, n.z);
1175  }
1176  }
1177  glEnd();
1178 
1179  glLoadIdentity();
1180 }*/
1181 
1185 /*static void drawCircle(CGLContextObj cgl_ctx, VuoPoint3d center, float radius, VuoPoint3d normal)
1186 {
1187  glPushMatrix();
1188  VuoTransform t = VuoTransform_makeQuaternion(
1189  center,
1190  VuoTransform_quaternionFromVectors(VuoPoint3d_make(1,0,0), normal),
1191  VuoPoint3d_make(1,1,1));
1192  float m[16];
1193  VuoTransform_getMatrix(t, m);
1194  glLoadMatrixf(m);
1195 
1196  glBegin(GL_LINE_LOOP);
1197  const int segments = 32;
1198  for (int i=0; i<segments; ++i)
1199  glVertex3f(
1200  0,
1201  cos(2.*M_PI*i/segments)*radius,
1202  sin(2.*M_PI*i/segments)*radius);
1203  glEnd();
1204 
1205  glPopMatrix();
1206 }*/
1207 
1211 /*static void drawCone(CGLContextObj cgl_ctx, VuoPoint3d center, float radius, VuoPoint3d normal, float height)
1212 {
1213  glPushMatrix();
1214  VuoTransform t = VuoTransform_makeQuaternion(
1215  center,
1216  VuoTransform_quaternionFromVectors(VuoPoint3d_make(1,0,0), normal),
1217  VuoPoint3d_make(1,1,1));
1218  float m[16];
1219  VuoTransform_getMatrix(t, m);
1220  glLoadMatrixf(m);
1221 
1222  glBegin(GL_LINE_LOOP);
1223  const int segments = 32;
1224  for (int i=0; i<segments; ++i)
1225  glVertex3f(
1226  0,
1227  cos(2.*M_PI*i/segments)*radius,
1228  sin(2.*M_PI*i/segments)*radius);
1229  glEnd();
1230  glBegin(GL_LINES);
1231  glVertex3f(-height, 0, 0); glVertex3f(0, radius, 0);
1232  glVertex3f(-height, 0, 0); glVertex3f(0, 0, radius);
1233  glVertex3f(-height, 0, 0); glVertex3f(0, -radius, 0);
1234  glVertex3f(-height, 0, 0); glVertex3f(0, 0, -radius);
1235  glEnd();
1236 
1237  glPopMatrix();
1238 }*/
1239 
1243 /*static void VuoSceneRenderer_drawLights(VuoSceneRendererInternal *sceneRenderer)
1244 {
1245  CGLContextObj cgl_ctx = (CGLContextObj)sceneRenderer->glContext;
1246 
1247  glPointSize(20);
1248 
1249  glMatrixMode(GL_PROJECTION);
1250  glLoadMatrixf(sceneRenderer->projectionMatrix);
1251 
1252  glMatrixMode(GL_MODELVIEW);
1253  glLoadIdentity();
1254 
1255  int pointLightCount = VuoListGetCount_VuoSceneObject(sceneRenderer->pointLights);
1256  for (int i=1; i<=pointLightCount; ++i)
1257  {
1258  VuoSceneObject pointLight = VuoListGetValue_VuoSceneObject(sceneRenderer->pointLights, i);
1259  VuoPoint3d position = pointLight.transform.translation;
1260  glBegin(GL_POINTS);
1261  glVertex3f(position.x, position.y, position.z);
1262  glEnd();
1263 
1264  // Draw a pair of concentric sphere outlines illustrating the light's range.
1265  {
1266  float innerRange = pointLight.lightRange*pointLight.lightSharpness;
1267  float outerRange = pointLight.lightRange*(2-pointLight.lightSharpness);
1268 
1269  // XY plane
1270  drawCircle(cgl_ctx, position, innerRange, VuoPoint3d_make(0,0,1));
1271  drawCircle(cgl_ctx, position, outerRange, VuoPoint3d_make(0,0,1));
1272  // XZ plane
1273  drawCircle(cgl_ctx, position, innerRange, VuoPoint3d_make(0,1,0));
1274  drawCircle(cgl_ctx, position, outerRange, VuoPoint3d_make(0,1,0));
1275  // YZ plane
1276  drawCircle(cgl_ctx, position, innerRange, VuoPoint3d_make(1,0,0));
1277  drawCircle(cgl_ctx, position, outerRange, VuoPoint3d_make(1,0,0));
1278  }
1279  }
1280 
1281  int spotLightCount = VuoListGetCount_VuoSceneObject(sceneRenderer->spotLights);
1282  for (int i=1; i<=spotLightCount; ++i)
1283  {
1284  VuoSceneObject spotLight = VuoListGetValue_VuoSceneObject(sceneRenderer->spotLights, i);
1285  VuoPoint3d position = spotLight.transform.translation;
1286  glBegin(GL_POINTS);
1287  glVertex3f(position.x, position.y, position.z);
1288  glEnd();
1289 
1290  VuoPoint3d direction = VuoTransform_getDirection(spotLight.transform);
1291  float innerRange = spotLight.lightRange*spotLight.lightSharpness;
1292  float outerRange = spotLight.lightRange*(2-spotLight.lightSharpness);
1293 
1294  // Draw a pair of cones (whose bases are the intersection of a plane and a spherical shell) to illustrate the light's range.
1295  {
1296  float innerConeAngle = spotLight.lightCone*spotLight.lightSharpness;
1297  float outerConeAngle = spotLight.lightCone*(2-spotLight.lightSharpness);
1298 
1299  float innerIntersectionDistance = innerRange*cos(innerConeAngle/2.);
1300  float outerIntersectionDistance = outerRange*cos(outerConeAngle/2.);
1301 
1302  float innerIntersectionRadius = innerRange*sin(innerConeAngle/2.);
1303  float outerIntersectionRadius = outerRange*sin(outerConeAngle/2.);
1304 
1305  VuoPoint3d innerEndpoint = VuoPoint3d_add(position, VuoPoint3d_multiply(direction, innerIntersectionDistance));
1306  VuoPoint3d outerEndpoint = VuoPoint3d_add(position, VuoPoint3d_multiply(direction, outerIntersectionDistance));
1307 
1308  drawCone(cgl_ctx, innerEndpoint, innerIntersectionRadius, direction, innerIntersectionDistance);
1309  drawCone(cgl_ctx, outerEndpoint, outerIntersectionRadius, direction, outerIntersectionDistance);
1310  }
1311  }
1312 }*/
1313 
1327 {
1328  soi->overrideIsRealSize = VuoSceneRendererInternal_object::RealSize_Inherit;
1329  soi->overrideShader = NULL;
1330 
1331  if (VuoSceneObject_getType(so) == VuoSceneObjectSubType_Text && !VuoText_isEmpty(VuoSceneObject_getText(so)))
1332  {
1333  soi->overrideIsRealSize = VuoSceneRendererInternal_object::RealSize_True;
1334  if (!sceneRenderer->sharedTextOverrideShader)
1335  {
1336  sceneRenderer->sharedTextOverrideShader = VuoShader_makeUnlitImageShader(NULL, 1);
1337  VuoRetain(sceneRenderer->sharedTextOverrideShader);
1338  }
1339  soi->overrideShader = sceneRenderer->sharedTextOverrideShader;
1340  VuoRetain(soi->overrideShader);
1341  }
1342 
1343  // Apply the overrides just within this function's scope.
1344  VuoShader shader = VuoSceneObject_getShader(so);
1345  if (soi->overrideShader)
1346  shader = soi->overrideShader;
1347  if (!shader)
1348  return false;
1349 
1350  VuoMesh mesh = VuoSceneObject_getMesh(so);
1351  if (!mesh)
1352  return false;
1353 
1354 
1355  // If we already have a VAO for this mesh/shader combination, use it.
1356  VuoSceneRendererMeshShader meshShader(mesh, shader);
1357  VuoSceneRendererMeshShaderVAOs::iterator i = sceneRenderer->meshShaderItems.find(meshShader);
1358  if (cache && i != sceneRenderer->meshShaderItems.end())
1359  {
1360  soi->vao = i->second;
1361  return true;
1362  }
1363 
1364 
1365  // …otherwise, create one.
1366 
1367  CGLContextObj cgl_ctx = (CGLContextObj)glContext;
1368 
1369  // Fetch the shader's attribute locations.
1370  // All VuoSubmeshes are assumed to have the same elementAssemblyMethod.
1371  GLint positionAttribute, normalAttribute, textureCoordinateAttribute, colorAttribute;
1372  if (!VuoShader_getAttributeLocations(shader, VuoMesh_getElementAssemblyMethod(mesh), glContext, &positionAttribute, &normalAttribute, &textureCoordinateAttribute, &colorAttribute))
1373  VUserLog("Error: Couldn't fetch the shader's attribute locations.");
1374 
1375  unsigned int combinedBuffer, elementCount, elementBuffer;
1376  void *normalOffset, *textureCoordinateOffset, *colorOffset;
1377  VuoMesh_getGPUBuffers(mesh, nullptr, &combinedBuffer, &normalOffset, &textureCoordinateOffset, &colorOffset, &elementCount, &elementBuffer);
1378 
1379  GLuint vertexArray;
1380 
1381 
1382  // Create a Vertex Array Object, to store this sceneobject's vertex array bindings.
1383  glGenVertexArrays(1, &vertexArray);
1384  glBindVertexArray(vertexArray);
1385 
1386 
1387  // Bind the combined buffer to the Vertex Array Object.
1388  glBindBuffer(GL_ARRAY_BUFFER, combinedBuffer);
1389 
1390 
1391  // Populate the Vertex Array Object with the various vertex attributes.
1392 
1393  int stride = sizeof(float) * 3;
1394  glEnableVertexAttribArray((GLuint)positionAttribute);
1395  glVertexAttribPointer((GLuint)positionAttribute, 3 /* XYZ */, GL_FLOAT, GL_FALSE, stride, (void*)0);
1396 
1397  if (normalOffset && normalAttribute>=0)
1398  {
1399  glEnableVertexAttribArray((GLuint)normalAttribute);
1400  glVertexAttribPointer((GLuint)normalAttribute, 3 /* XYZ */, GL_FLOAT, GL_FALSE, stride, normalOffset);
1401  }
1402 
1403  if (textureCoordinateOffset && textureCoordinateAttribute>=0)
1404  {
1405  glEnableVertexAttribArray((GLuint)textureCoordinateAttribute);
1406  glVertexAttribPointer((GLuint)textureCoordinateAttribute, 2 /* XY */, GL_FLOAT, GL_FALSE, sizeof(float) * 2, textureCoordinateOffset);
1407  }
1408 
1409  if (colorOffset && colorAttribute >= 0)
1410  {
1411  glEnableVertexAttribArray((GLuint)colorAttribute);
1412  glVertexAttribPointer((GLuint)colorAttribute, 4 /* RGBA */, GL_FLOAT, GL_FALSE, sizeof(float) * 4, colorOffset);
1413  }
1414 
1415  // Bind the Element Buffer to the Vertex Array Object
1416  if (elementCount)
1417  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
1418 
1419 
1420  glBindVertexArray(0);
1421 
1422  soi->vao = vertexArray;
1423 
1424  glBindBuffer(GL_ARRAY_BUFFER, 0);
1425 
1426  if (cache)
1427  sceneRenderer->meshShaderItems[meshShader] = soi->vao;
1428 
1429  return true;
1430 }
1431 
1439 static void VuoSceneRenderer_uploadSceneObjects(VuoSceneRendererInternal *sceneRenderer, VuoGlContext glContext, VuoPoint3d cameraTranslation)
1440 {
1442  rootState.so = sceneRenderer->rootSceneObject;
1443  rootState.soi = new VuoSceneRendererInternal_object;
1444  VuoTransform_getMatrix(VuoTransform_makeIdentity(), rootState.modelviewMatrix);
1445 
1446  list<VuoSceneRenderer_TreeRenderState> objects(1, rootState);
1447  while (!objects.empty())
1448  {
1449  VuoSceneRenderer_TreeRenderState currentState = objects.front();
1450  objects.pop_front();
1451 
1452  float localModelviewMatrix[16];
1453  VuoTransform_getMatrix(VuoSceneObject_getTransform(currentState.so), localModelviewMatrix);
1454  float compositeModelviewMatrix[16];
1455  VuoTransform_multiplyMatrices4x4(localModelviewMatrix, currentState.modelviewMatrix, compositeModelviewMatrix);
1456  VuoTransform_copyMatrix4x4(compositeModelviewMatrix, currentState.modelviewMatrix);
1457 
1458  bool isRenderable = VuoSceneRenderer_uploadSceneObject(sceneRenderer, currentState.so, currentState.soi, glContext, true);
1459  if (isRenderable)
1460  {
1461  if (sceneRenderer->shouldSortByDepth)
1462  {
1463  if (VuoShader_isOpaque(VuoSceneObject_getShader(currentState.so)))
1464  sceneRenderer->opaqueObjects.push_back(currentState);
1465  else
1466  sceneRenderer->potentiallyTransparentObjects.push_back(currentState);
1467  }
1468  else
1469  sceneRenderer->opaqueObjects.push_back(currentState);
1470  }
1471 
1472  // Prepend childObjects to the objects queue.
1473  // (Do it in reverse order so the objects end up in forward order after calling ::push_front repeatedly.) @@@
1475  for (long i = VuoListGetCount_VuoSceneObject(VuoSceneObject_getChildObjects(currentState.so)) - 1; i >= 0; --i)
1476  {
1478  childState.so = childObjects[i];
1479  childState.soi = new VuoSceneRendererInternal_object;
1480  memcpy(childState.modelviewMatrix, compositeModelviewMatrix, sizeof(float[16]));
1481  objects.push_front(childState);
1482  }
1483 
1484  if (!isRenderable)
1485  delete currentState.soi;
1486  }
1487 
1488  if (sceneRenderer->shouldSortByDepth)
1489  {
1490  // Sort opaque objects to render front to back (to reduce overdraw).
1491  // For better performance, this takes into account the object's transform but not the object's vertices.
1492  sceneRenderer->opaqueObjects.sort([=](const VuoSceneRenderer_TreeRenderState &a, const VuoSceneRenderer_TreeRenderState &b){
1493  return VuoPoint3d_distance(VuoTransform_getMatrix4x4Translation(a.modelviewMatrix), cameraTranslation)
1494  < VuoPoint3d_distance(VuoTransform_getMatrix4x4Translation(b.modelviewMatrix), cameraTranslation);
1495  });
1496 
1497  // Sort potentially-transparent objects to render back to front (for proper blending).
1498  sceneRenderer->potentiallyTransparentObjects.sort([=](const VuoSceneRenderer_TreeRenderState &a, const VuoSceneRenderer_TreeRenderState &b){
1499  return VuoPoint3d_distance(VuoTransform_getMatrix4x4Translation(a.modelviewMatrix), cameraTranslation)
1500  > VuoPoint3d_distance(VuoTransform_getMatrix4x4Translation(b.modelviewMatrix), cameraTranslation);
1501  });
1502  }
1503 }
1504 
1511 {
1512  for (auto object : sceneRenderer->opaqueObjects)
1513  {
1514  if (object.soi->overrideShader)
1515  VuoRelease(object.soi->overrideShader);
1516  delete object.soi;
1517  }
1518  sceneRenderer->opaqueObjects.clear();
1519 
1520  for (auto object : sceneRenderer->potentiallyTransparentObjects)
1521  {
1522  if (object.soi->overrideShader)
1523  VuoRelease(object.soi->overrideShader);
1524  delete object.soi;
1525  }
1526  sceneRenderer->potentiallyTransparentObjects.clear();
1527 }
1528 
1533 {
1534  CGLContextObj cgl_ctx = (CGLContextObj)glContext;
1535 
1536  for (VuoSceneRendererMeshShaderVAOs::iterator msi = sceneRenderer->meshShaderItems.begin(); msi != sceneRenderer->meshShaderItems.end(); ++msi)
1537  glDeleteVertexArrays(1, &(msi->second));
1538 
1539  sceneRenderer->meshShaderItems.clear();
1540 }
1541 
1550 {
1551  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1552 
1553  dispatch_semaphore_wait(sceneRenderer->scenegraphSemaphore, DISPATCH_TIME_FOREVER);
1554  {
1555  VuoSceneObject_retain(rootSceneObject);
1556 
1557  if (sceneRenderer->rootSceneObjectPendingValid)
1559 
1560  sceneRenderer->rootSceneObjectPending = rootSceneObject;
1561 
1562  sceneRenderer->rootSceneObjectPendingValid = true;
1563  sceneRenderer->rootSceneObjectPendingUpdated = true;
1564  }
1565  dispatch_semaphore_signal(sceneRenderer->scenegraphSemaphore);
1566 }
1567 
1576 {
1577  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1578  *isValid = sceneRenderer->rootSceneObjectValid;
1579  return sceneRenderer->rootSceneObject;
1580 }
1581 
1592 {
1593  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1594 
1595  dispatch_semaphore_wait(sceneRenderer->scenegraphSemaphore, DISPATCH_TIME_FOREVER);
1596  {
1597  if (sceneRenderer->cameraName)
1598  VuoRelease(sceneRenderer->cameraName);
1599 
1600  sceneRenderer->cameraName = cameraName;
1601  VuoRetain(sceneRenderer->cameraName);
1602 
1603  sceneRenderer->useLeftCamera = useLeftCamera;
1604  }
1605  dispatch_semaphore_signal(sceneRenderer->scenegraphSemaphore);
1606 }
1607 
1614 {
1615  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1616 
1617  dispatch_semaphore_wait(sceneRenderer->scenegraphSemaphore, DISPATCH_TIME_FOREVER);
1618 
1619  if (sceneRenderer->rootSceneObjectPendingValid)
1621 
1622  if (sceneRenderer->rootSceneObjectValid)
1623  {
1624  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1625  VuoSceneRenderer_cleanupRenderLists(sceneRenderer);
1626  VuoSceneRenderer_cleanupMeshShaderItems(sceneRenderer, cgl_ctx);
1627  });
1628  VuoSceneObject_release(sceneRenderer->rootSceneObject);
1629 
1630  VuoRelease(sceneRenderer->pointLights);
1631  VuoRelease(sceneRenderer->spotLights);
1632  }
1633 
1634  if (sceneRenderer->sharedTextOverrideShader)
1635  VuoRelease(sceneRenderer->sharedTextOverrideShader);
1636 
1637  if (sceneRenderer->cameraName)
1638  VuoRelease(sceneRenderer->cameraName);
1639  VuoSceneObject_release(sceneRenderer->camera);
1640 
1641 #if VUO_PRO
1642  VuoRelease(sceneRenderer->vignetteShader);
1643  VuoRelease(sceneRenderer->vignetteQuad);
1644 #endif
1645 
1646  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1647  glDeleteRenderbuffersEXT(1, &sceneRenderer->renderBuffer);
1648  glDeleteRenderbuffersEXT(1, &sceneRenderer->renderDepthBuffer);
1649  glDeleteFramebuffers(1, &sceneRenderer->outputFramebuffer);
1650  glDeleteFramebuffers(1, &sceneRenderer->outputFramebuffer2);
1651  });
1652 
1653  dispatch_semaphore_signal(sceneRenderer->scenegraphSemaphore);
1654  dispatch_release(sceneRenderer->scenegraphSemaphore);
1655 
1656  delete sceneRenderer;
1657 }
1658 
1662 extern "C" bool VuoSceneRenderer_renderInternal(VuoSceneRenderer sr, VuoGlContext glContext, GLuint *outputTexture, GLenum target, GLuint imageGlInternalFormat, VuoMultisample multisample, GLuint *outputDepthTexture, bool invertDepthImage)
1663 {
1664  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1665 
1666  CGLContextObj cgl_ctx = (CGLContextObj)glContext;
1667 
1668  static bool force2DDepth = true;
1669  static dispatch_once_t once = 0;
1670  dispatch_once(&once, ^{
1671  // If the user set the `force2DDepth` preference, use it.
1672  Boolean overridden = false;
1673  force2DDepth = (int)CFPreferencesGetAppIntegerValue(CFSTR("force2DDepth"), CFSTR("org.vuo.Editor"), &overridden);
1674 
1675  if (!overridden)
1676  {
1677  // https://b33p.net/kosada/node/13485
1678  // https://b33p.net/kosada/node/13896
1679  // On some NVIDIA GPUs, the texture targets for the color and depth buffers need to match
1680  // (otherwise it sporadically crashes, or if a window is added via livecoding, it renders the scene with the wrong colors).
1681  // On AMD 7970 and AMD M370X, the depth texture target needs to always be GL_TEXTURE_2D
1682  // (otherwise it throws GL_INVALID_OPERATION).
1683  const char *renderer = (const char *)glGetString(GL_RENDERER);
1684  if (strcmp(renderer, "NVIDIA GeForce GT 650M OpenGL Engine") == 0
1685  || strcmp(renderer, "NVIDIA GeForce 9400M OpenGL Engine") == 0)
1686  force2DDepth = false;
1687  else
1688  force2DDepth = true;
1689  }
1690 
1691  VDebugLog("force2DDepth = %d", force2DDepth);
1692  });
1693 
1694  unsigned char colorBytesPerPixel = VuoGlTexture_getBytesPerPixel(imageGlInternalFormat, GL_BGRA);
1695  unsigned long requiredBytes = sceneRenderer->viewportWidth * sceneRenderer->viewportHeight * colorBytesPerPixel;
1696  if (outputDepthTexture)
1697  requiredBytes += sceneRenderer->viewportWidth * sceneRenderer->viewportHeight * VuoGlTexture_getBytesPerPixel(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT);
1698 
1699  int supportedSamples = VuoGlContext_getMaximumSupportedMultisampling(glContext);
1700  multisample = (VuoMultisample)MIN(multisample, supportedSamples);
1701  bool actuallyMultisampling = multisample > 1;
1702 
1703  float fudge = 1;
1704  const char *renderer = (const char *)glGetString(GL_RENDERER);
1705  if (strcmp(renderer, "Intel HD Graphics 3000") == 0)
1706  // See https://b33p.net/kosada/node/12030
1707  fudge = 2.5;
1708 
1709  if (multisample)
1710  requiredBytes += requiredBytes * multisample * fudge;
1711 
1712  unsigned long maximumTextureBytes = VuoGlTexture_getMaximumTextureBytes(glContext);
1713  if (maximumTextureBytes > 0 && requiredBytes > maximumTextureBytes)
1714  {
1715  VUserLog("Not enough graphics memory for a %dx%d (%d bytes/pixel * %d sample) render%s. Requires %lu MB, have %lu MB.",
1716  sceneRenderer->viewportWidth, sceneRenderer->viewportHeight,
1717  colorBytesPerPixel, multisample==0?1:multisample,
1718  outputDepthTexture ? " with depth buffer" : "",
1719  requiredBytes/1024/1024, maximumTextureBytes/1024/1024);
1720  return false;
1721  }
1722 
1723 
1724  // Create a new GL Texture Object and Framebuffer Object.
1725  if (!*outputTexture)
1726  *outputTexture = VuoGlTexturePool_use(glContext, VuoGlTexturePool_Allocate, GL_TEXTURE_2D, imageGlInternalFormat, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight, GL_BGRA, NULL);
1727 
1728  if (outputDepthTexture)
1729  *outputDepthTexture = VuoGlTexturePool_use(glContext, VuoGlTexturePool_Allocate, GL_TEXTURE_2D, GL_DEPTH_COMPONENT, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight, GL_DEPTH_COMPONENT, NULL);
1730 
1731  if (!*outputTexture || (outputDepthTexture && !*outputDepthTexture))
1732  return false;
1733 
1734  sceneRenderer->shouldSortByDepth = outputDepthTexture && *outputDepthTexture;
1735 
1736  glBindFramebuffer(GL_FRAMEBUFFER, sceneRenderer->outputFramebuffer);
1737 
1738  // If multisampling, create high-res intermediate renderbuffers.
1739  // Otherwise, directly bind the textures to the framebuffer.
1740  if (actuallyMultisampling)
1741  {
1742  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, sceneRenderer->renderBuffer);
1743 // VLog("glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, %d, %s, %d, %d);", multisample, VuoGl_stringForConstant(imageGlInternalFormat), sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1744  glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, multisample, imageGlInternalFormat, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1745  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER_EXT, sceneRenderer->renderBuffer);
1746 
1747  if (outputDepthTexture)
1748  {
1749  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, sceneRenderer->renderDepthBuffer);
1750  glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, multisample, GL_DEPTH_COMPONENT, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1751  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER_EXT, sceneRenderer->renderDepthBuffer);
1752  }
1753  }
1754  else
1755  {
1756  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, *outputTexture, 0);
1757  if (outputDepthTexture)
1758  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, force2DDepth ? GL_TEXTURE_2D : target, *outputDepthTexture, 0);
1759  }
1760 
1761 
1762  // Render.
1763  dispatch_semaphore_wait(sceneRenderer->scenegraphSemaphore, DISPATCH_TIME_FOREVER);
1764  {
1765  if (invertDepthImage)
1766  {
1767  glClearDepth(0);
1768  glDepthRange(1, 0);
1769  glDepthFunc(GL_GREATER);
1770  }
1771  else
1772  {
1773  glClearDepth(1);
1774  glDepthRange(0, 1);
1775  glDepthFunc(GL_LESS);
1776  }
1777 
1778  glViewport(0, 0, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1779  glClearColor(0,0,0,0);
1780  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1781 
1782  VuoSceneRenderer_draw(sceneRenderer, cgl_ctx);
1783  }
1784 
1785 
1786  // If multisampling, resolve the renderbuffers into standard textures by blitting them.
1787  if (actuallyMultisampling)
1788  {
1789  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, sceneRenderer->outputFramebuffer2);
1790 
1791  {
1792  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, *outputTexture, 0);
1793  if (outputDepthTexture)
1794  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, force2DDepth ? GL_TEXTURE_2D : target, *outputDepthTexture, 0);
1795 
1796  glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, sceneRenderer->outputFramebuffer);
1797  glReadBuffer(GL_COLOR_ATTACHMENT0);
1798  glDrawBuffer(GL_COLOR_ATTACHMENT0);
1799  glBlitFramebufferEXT(0, 0, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight,
1800  0, 0, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight,
1801  GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
1802  GL_NEAREST);
1803 
1804  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, 0, 0);
1805  if (outputDepthTexture)
1806  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, force2DDepth ? GL_TEXTURE_2D : target, 0, 0);
1807  }
1808 
1809  glBindFramebuffer(GL_FRAMEBUFFER, 0);
1810  }
1811  else
1812  {
1813  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, 0, 0);
1814  if (outputDepthTexture)
1815  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, force2DDepth ? GL_TEXTURE_2D : target, 0, 0);
1816  }
1817 
1818 
1819  glBindFramebuffer(GL_FRAMEBUFFER, 0);
1820 
1821  // Make sure the render commands are actually submitted before we release the semaphore,
1822  // since the textures we're using might immediately be recycled (if the rootSceneObject is released).
1823  glFlushRenderAPPLE();
1824 
1825  dispatch_semaphore_signal(sceneRenderer->scenegraphSemaphore);
1826 
1827  return true;
1828 }
1829 
1839 void VuoSceneRenderer_renderToImage(VuoSceneRenderer sr, VuoImage *image, VuoImageColorDepth imageColorDepth, VuoMultisample multisample, VuoImage *depthImage, bool invertDepthImage)
1840 {
1841  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1842  GLuint imageGlInternalFormat = VuoImageColorDepth_getGlInternalFormat(GL_BGRA, imageColorDepth);
1843  __block GLuint texture = 0;
1844  __block GLuint depthTexture = 0;
1845  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1846  if (!VuoSceneRenderer_renderInternal(sr, cgl_ctx, &texture, GL_TEXTURE_2D, imageGlInternalFormat, multisample, depthImage ? &depthTexture : NULL, invertDepthImage))
1847  {
1848  *image = NULL;
1849  if (depthImage)
1850  *depthImage = NULL;
1851  }
1852  });
1853 
1854  *image = VuoImage_make(texture, imageGlInternalFormat, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1855  if (depthImage)
1856  *depthImage = VuoImage_make(depthTexture, GL_DEPTH_COMPONENT, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1857 }
1858 
1869 {
1870  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1871  GLuint imageGlInternalFormat = VuoImageColorDepth_getGlInternalFormat(GL_BGRA, imageColorDepth);
1872  __block GLuint texture = 0;
1873  __block GLuint depthTexture = 0;
1874  __block bool renderSucceeded;
1875  __block VuoIoSurface vis;
1876  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1877  vis = VuoIoSurfacePool_use(cgl_ctx, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight, &texture);
1878  renderSucceeded = VuoSceneRenderer_renderInternal(sr, cgl_ctx, &texture, GL_TEXTURE_RECTANGLE_ARB, imageGlInternalFormat, multisample, includeDepthBuffer ? &depthTexture : NULL, false);
1879  });
1880  if (!renderSucceeded)
1881  {
1882  VuoIoSurfacePool_disuse(vis, false); // It was never used, so no need to quarantine it.
1883  return NULL;
1884  }
1885 
1886  if (includeDepthBuffer)
1887  {
1888  VuoImage depthImage = VuoImage_make(depthTexture, GL_DEPTH_COMPONENT, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1889  VuoRetain(depthImage);
1890  VuoRelease(depthImage);
1891  }
1892 
1893  return vis;
1894 }