Vuo  2.0.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
27 
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<void *, void *> 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 #if VUO_PRO
279  VuoSceneRendererPro_init(sceneRenderer, cgl_ctx);
280 #endif
281 
282  glGenRenderbuffersEXT(1, &sceneRenderer->renderBuffer);
283  glGenRenderbuffersEXT(1, &sceneRenderer->renderDepthBuffer);
284  glGenFramebuffers(1, &sceneRenderer->outputFramebuffer);
285  glGenFramebuffers(1, &sceneRenderer->outputFramebuffer2);
286 
287  CGLGetParameter(cgl_ctx, kCGLCPCurrentRendererID, &sceneRenderer->glContextRendererID);
288  });
289 
290  return (VuoSceneRenderer)sceneRenderer;
291 }
292 
294 
300 {
301  VuoSceneObject camera;
302  if (sceneRenderer->rootSceneObjectValid)
303  {
304  if (VuoSceneObject_findCamera(sceneRenderer->rootSceneObject, sceneRenderer->cameraName, &camera))
305  {
306  VuoSceneObject_retain(camera);
307  VuoSceneObject_release(sceneRenderer->camera);
308  sceneRenderer->camera = camera;
309  return;
310  }
311 
312  if (sceneRenderer->cameraName && strlen(sceneRenderer->cameraName) != 0)
313  // Search again, and this time just pick the first camera we find.
314  if (VuoSceneObject_findCamera(sceneRenderer->rootSceneObject, "", &camera))
315  {
316  VuoSceneObject_retain(camera);
317  VuoSceneObject_release(sceneRenderer->camera);
318  sceneRenderer->camera = camera;
319  return;
320  }
321  }
322 
324  VuoSceneObject_retain(camera);
325  VuoSceneObject_release(sceneRenderer->camera);
326  sceneRenderer->camera = camera;
327 }
328 
335 void VuoSceneRenderer_regenerateProjectionMatrix(VuoSceneRenderer sr, unsigned int width, unsigned int height)
336 {
337 // VDebugLog("%dx%d", width, height);
338  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
339 
340  dispatch_semaphore_wait(sceneRenderer->scenegraphSemaphore, DISPATCH_TIME_FOREVER);
341 
342  // Store the viewport size (in case we need to regenerate the projection matrix later)
343  sceneRenderer->viewportWidth = width;
344  sceneRenderer->viewportHeight = height;
345 
348 
349  dispatch_semaphore_signal(sceneRenderer->scenegraphSemaphore);
350 }
351 
358 {
359  float cameraDistanceMin = sceneRenderer->camera.cameraDistanceMin;
360  float cameraDistanceMax = sceneRenderer->camera.cameraDistanceMax;
361 
362  // Build a projection matrix for a camera located at the origin, facing along the -z axis.
363  float aspectRatio = (float)sceneRenderer->viewportWidth/(float)sceneRenderer->viewportHeight;
364  if (sceneRenderer->camera.type == VuoSceneObjectSubType_PerspectiveCamera)
365  {
366  float halfFieldOfView = (sceneRenderer->camera.cameraFieldOfView * (float)M_PI / 180.f) / 2.f;
367 
368  // left matrix column
369  sceneRenderer->projectionMatrix[ 0] = 1.f/tanf(halfFieldOfView);
370  sceneRenderer->projectionMatrix[ 1] = 0;
371  sceneRenderer->projectionMatrix[ 2] = 0;
372  sceneRenderer->projectionMatrix[ 3] = 0;
373 
374  sceneRenderer->projectionMatrix[ 4] = 0;
375  sceneRenderer->projectionMatrix[ 5] = aspectRatio/tanf(halfFieldOfView);
376  sceneRenderer->projectionMatrix[ 6] = 0;
377  sceneRenderer->projectionMatrix[ 7] = 0;
378 
379  sceneRenderer->projectionMatrix[ 8] = 0;
380  sceneRenderer->projectionMatrix[ 9] = 0;
381  sceneRenderer->projectionMatrix[10] = (cameraDistanceMax + cameraDistanceMin) / (cameraDistanceMin - cameraDistanceMax);
382  sceneRenderer->projectionMatrix[11] = -1.f;
383 
384  // right matrix column
385  sceneRenderer->projectionMatrix[12] = 0;
386  sceneRenderer->projectionMatrix[13] = 0;
387  sceneRenderer->projectionMatrix[14] = 2.f * cameraDistanceMax * cameraDistanceMin / (cameraDistanceMin - cameraDistanceMax);
388  sceneRenderer->projectionMatrix[15] = 0;
389  }
390  else if (sceneRenderer->camera.type == VuoSceneObjectSubType_StereoCamera)
391  {
392  float halfFieldOfView = (sceneRenderer->camera.cameraFieldOfView * (float)M_PI / 180.f) / 2.f;
393  float top = cameraDistanceMin * tanf(halfFieldOfView);
394  float right = aspectRatio * top;
395  float frustumshift = (sceneRenderer->camera.cameraIntraocularDistance / 2.f) * cameraDistanceMin / sceneRenderer->camera.cameraConfocalDistance;
396  if (!sceneRenderer->useLeftCamera)
397  frustumshift *= -1.f;
398 
399  // column 0
400  sceneRenderer->projectionMatrix[ 0] = 1.f/tanf(halfFieldOfView);
401  sceneRenderer->projectionMatrix[ 1] = 0;
402  sceneRenderer->projectionMatrix[ 2] = 0;
403  sceneRenderer->projectionMatrix[ 3] = 0;
404 
405  // column 1
406  sceneRenderer->projectionMatrix[ 4] = 0;
407  sceneRenderer->projectionMatrix[ 5] = aspectRatio/tanf(halfFieldOfView);
408  sceneRenderer->projectionMatrix[ 6] = 0;
409  sceneRenderer->projectionMatrix[ 7] = 0;
410 
411  // column 2
412  sceneRenderer->projectionMatrix[ 8] = 2.f * frustumshift / right;
413  sceneRenderer->projectionMatrix[ 9] = 0;
414  sceneRenderer->projectionMatrix[10] = (cameraDistanceMax + cameraDistanceMin) / (cameraDistanceMin - cameraDistanceMax);
415  sceneRenderer->projectionMatrix[11] = -1.f;
416 
417  // column 3
418  sceneRenderer->projectionMatrix[12] = (sceneRenderer->useLeftCamera ? 1.f : -1.f) * sceneRenderer->camera.cameraIntraocularDistance / 2.f;
419  sceneRenderer->projectionMatrix[13] = 0;
420  sceneRenderer->projectionMatrix[14] = 2.f * cameraDistanceMax * cameraDistanceMin / (cameraDistanceMin - cameraDistanceMax);
421  sceneRenderer->projectionMatrix[15] = 0;
422  }
423  else if (sceneRenderer->camera.type == VuoSceneObjectSubType_OrthographicCamera)
424  {
425  float halfWidth = sceneRenderer->camera.cameraWidth / 2.f;
426 
427  // left matrix column
428  sceneRenderer->projectionMatrix[ 0] = 1.f/halfWidth;
429  sceneRenderer->projectionMatrix[ 1] = 0;
430  sceneRenderer->projectionMatrix[ 2] = 0;
431  sceneRenderer->projectionMatrix[ 3] = 0;
432 
433  sceneRenderer->projectionMatrix[ 4] = 0;
434  sceneRenderer->projectionMatrix[ 5] = aspectRatio/halfWidth;
435  sceneRenderer->projectionMatrix[ 6] = 0;
436  sceneRenderer->projectionMatrix[ 7] = 0;
437 
438  sceneRenderer->projectionMatrix[ 8] = 0;
439  sceneRenderer->projectionMatrix[ 9] = 0;
440  sceneRenderer->projectionMatrix[10] = -2.f / (cameraDistanceMax - cameraDistanceMin);
441  sceneRenderer->projectionMatrix[11] = 0;
442 
443  // right matrix column
444  sceneRenderer->projectionMatrix[12] = 0;
445  sceneRenderer->projectionMatrix[13] = 0;
446  sceneRenderer->projectionMatrix[14] = -(cameraDistanceMax + cameraDistanceMin) / (cameraDistanceMax - cameraDistanceMin);
447  sceneRenderer->projectionMatrix[15] = 1;
448  }
449  else if (sceneRenderer->camera.type == VuoSceneObjectSubType_FisheyeCamera)
450  {
451  bzero(sceneRenderer->projectionMatrix, sizeof(float)*16);
452 #if VUO_PRO
453  VuoSceneRendererPro_generateFisheyeProjectionMatrix(sceneRenderer, sceneRenderer->camera, aspectRatio);
454 #endif
455  }
456  else
457  VUserLog("Unknown type %d", sceneRenderer->camera.type);
458 
459 
460  // Transform the scene by the inverse of the camera's transform.
461  // (Don't move the ship around the universe: move the universe around the ship.)
462  {
463  float cameraMatrix[16];
464  VuoTransform_getMatrix(sceneRenderer->camera.transform, cameraMatrix);
465 
466  float invertedCameraMatrix[16];
467  VuoTransform_invertMatrix4x4(cameraMatrix, invertedCameraMatrix);
468 
469  VuoTransform_copyMatrix4x4(invertedCameraMatrix, sceneRenderer->cameraMatrixInverse);
470  }
471 }
472 
478 static void VuoSceneRenderer_addUniformSuffix(char *address, int i, const char *suffix)
479 {
480  if (i < 10)
481  *(address++) = '0' + i;
482  else
483  {
484  *(address++) = '1';
485  *(address++) = '0' + (i - 10);
486  }
487 
488  strcpy(address, suffix);
489 }
490 
496 void VuoSceneRenderer_checkDataBounds(CGLContextObj cgl_ctx, VuoSubmesh submesh, GLuint positionAttribute)
497 {
498  GLint enabled = -1;
499  glGetVertexAttribiv(positionAttribute, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled);
500  if (enabled != 1)
501  {
502  VUserLog("Error: Vertex attrib array isn't enabled.");
503  return;
504  }
505 
506  GLint vaovbo = -1;
507  glGetVertexAttribiv(positionAttribute, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &vaovbo);
508  if (vaovbo != (GLint)submesh.glUpload.combinedBuffer)
509  VUserLog("Error: GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING (%d) doesn't match submesh's VBO (%d).", vaovbo, submesh.glUpload.combinedBuffer);
510 
511  GLint vertexBufferBound = -1;
512  glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vertexBufferBound);
513  if (vertexBufferBound)
514  VUserLog("Error: GL_ARRAY_BUFFER_BINDING is %d (it should probably be 0, since we're using a VAO).", vertexBufferBound);
515 
516  GLint elementBufferBound = -1;
517  glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &elementBufferBound);
518  if (submesh.elementCount && !elementBufferBound)
519  VUserLog("Error: GL_ELEMENT_ARRAY_BUFFER_BINDING is 0 (it should be nonzero, since we're trying to draw elements).");
520 
521  GLint combinedBufferSize = -1;
522  glBindBuffer(GL_ARRAY_BUFFER, submesh.glUpload.combinedBuffer);
523  glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &combinedBufferSize);
524  glBindBuffer(GL_ARRAY_BUFFER, 0);
525  if (combinedBufferSize < 4)
526  VUserLog("Error: combinedBuffer is unrealistically small (%d bytes).", combinedBufferSize);
527 
528  GLint attribStride = -1;
529  glGetVertexAttribiv(positionAttribute, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &attribStride);
530 
531  GLint attribComponents = -1;
532  glGetVertexAttribiv(positionAttribute, GL_VERTEX_ATTRIB_ARRAY_SIZE, &attribComponents);
533 
534  GLint attribType = -1;
535  glGetVertexAttribiv(positionAttribute, GL_VERTEX_ATTRIB_ARRAY_TYPE, &attribType);
536 
537  void *attribBase = (void *)0;
538  glGetVertexAttribPointerv(positionAttribute, GL_VERTEX_ATTRIB_ARRAY_POINTER, &attribBase);
539 
540  unsigned int bytesPerComponent;
541  if (attribType == GL_UNSIGNED_INT)
542  bytesPerComponent = 1;
543  else if (attribType == GL_FLOAT)
544  bytesPerComponent = 4;
545  else
546  {
547  VUserLog("Error: Unknown combinedBuffer type %s.", VuoGl_stringForConstant((GLenum)attribType));
548  return;
549  }
550 
551  unsigned int minIndex = 0;
552  unsigned int maxIndex = 0;
553  if (submesh.elementCount)
554  {
555  if (submesh.glUpload.elementBufferSize != submesh.elementCount * sizeof(unsigned int))
556  VUserLog("Error: elementBufferSize doesn't match elementCount.");
557 
558  unsigned int *b = (unsigned int *)malloc(submesh.glUpload.elementBufferSize);
559  glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, submesh.glUpload.elementBufferSize, b);
560 
561  for (unsigned int i = 0; i < submesh.elementCount; ++i)
562  {
563  if (b[i] > maxIndex)
564  maxIndex = b[i];
565  if (b[i] < minIndex)
566  minIndex = b[i];
567  }
568 
569  free(b);
570  }
571  else
572  maxIndex = submesh.vertexCount - 1;
573 
574  long startWithinVBO = (long)attribBase + (unsigned int)attribStride * minIndex;
575  long endWithinVBO = (long)attribBase +(unsigned int) attribStride * maxIndex + (unsigned int)attribComponents * bytesPerComponent;
576 
577  if (startWithinVBO < 0)
578  VUserLog("Error: start < 0");
579  if (endWithinVBO < 0)
580  VUserLog("Error: end < 0");
581  if (startWithinVBO > endWithinVBO)
582  VUserLog("Error: start > end");
583  if (endWithinVBO > combinedBufferSize)
584  VUserLog("Error: end > combinedBufferSize");
585 
586  VGL();
587 }
588 
597 static void VuoSceneRenderer_drawSceneObject(VuoSceneObject so, VuoSceneRendererInternal_object *soi, float projectionMatrix[16], float modelviewMatrix[16], VuoSceneRendererInternal *sceneRenderer, VuoGlContext glContext)
598 {
599  // Apply the overrides just within this function's scope.
600  if (soi->overrideIsRealSize != VuoSceneRendererInternal_object::RealSize_Inherit)
601  so.isRealSize = soi->overrideIsRealSize;
602  if (soi->overrideShader)
603  so.shader = soi->overrideShader;
604 
605 
606  if (so.type == VuoSceneObjectSubType_Text && so.text && so.text[0] != 0)
607  {
608  // Text has to be rasterized while rendering (instead of while uploading)
609  // since resizing the window (which doesn't re-upload) could cause the text to change size
610  // (and we want to ensure the text is crisp, so we can't just scale the pre-rasterized bitmap).
611 
612  float verticalScale = 1.;
613  float rotationZ = 0.;
614  float wrapWidth = so.wrapWidth;
615  if (so.scaleWithScene)
616  {
617  so.font.pointSize *= (double)sceneRenderer->viewportWidth / (VuoGraphicsWindowDefaultWidth * sceneRenderer->backingScaleFactor);
618  wrapWidth *= (double)sceneRenderer->viewportWidth / (VuoGraphicsWindowDefaultWidth * sceneRenderer->backingScaleFactor);
619 
620  VuoTransform transform = VuoTransform_makeFromMatrix4x4(modelviewMatrix);
621  so.font.pointSize *= transform.scale.x;
622  wrapWidth *= transform.scale.x;
623  verticalScale = transform.scale.y / transform.scale.x;
624  rotationZ = VuoTransform_getEuler(transform).z;
625  }
626 
627  VuoPoint2d corners[4];
628  VuoImage textImage = VuoImage_makeText(so.text, so.font, sceneRenderer->backingScaleFactor, verticalScale, rotationZ, wrapWidth, corners);
629 
630  VuoPoint2d anchorOffset = VuoSceneText_getAnchorOffset(so, verticalScale, rotationZ, wrapWidth, sceneRenderer->viewportWidth, sceneRenderer->backingScaleFactor);
631  modelviewMatrix[12] += anchorOffset.x;
632  modelviewMatrix[13] += anchorOffset.y;
633 
634  VuoShader_setUniform_VuoImage(so.shader, "texture", textImage);
635  }
636 
637 
638  if (!so.mesh || !so.mesh->submeshCount || !so.shader)
639  return;
640 
641  VuoImage image = VuoShader_getUniform_VuoImage(so.shader, "texture");
642  if (so.isRealSize && !image)
643  return;
644 
645  CGLContextObj cgl_ctx = (CGLContextObj)glContext;
646 
647 #ifdef VUO_PROFILE
648  GLuint timeElapsedQuery;
649  glGenQueries(1, &timeElapsedQuery);
650  glBeginQuery(GL_TIME_ELAPSED_EXT, timeElapsedQuery);
651 #endif
652 
653  GLint positionAttribute = -1;
654  if (VuoIsDebugEnabled())
655  if (!VuoShader_getAttributeLocations(so.shader, so.mesh->submeshes[0].elementAssemblyMethod, cgl_ctx, &positionAttribute, NULL, NULL, NULL, NULL))
656  VDebugLog("Error: Couldn't fetch the 'position' attribute, needed to check data bounds.");
657 
658 
659  // All VuoSubmeshes are assumed to have the same elementAssemblyMethod.
660  VuoGlProgram program;
661  if (!VuoShader_activate(so.shader, so.mesh->submeshes[0].elementAssemblyMethod, cgl_ctx, &program))
662  {
663  VUserLog("Shader activation failed.");
664  return;
665  }
666 
667  {
668  GLint projectionMatrixUniform = VuoGlProgram_getUniformLocation(program, "projectionMatrix");
669  glUniformMatrix4fv(projectionMatrixUniform, 1, GL_FALSE, projectionMatrix);
670 
671  GLint useFisheyeProjectionUniform = VuoGlProgram_getUniformLocation(program, "useFisheyeProjection");
672  if (useFisheyeProjectionUniform != -1)
673  glUniform1i(useFisheyeProjectionUniform, sceneRenderer->camera.type == VuoSceneObjectSubType_FisheyeCamera);
674 
675  GLint modelviewMatrixUniform = VuoGlProgram_getUniformLocation(program, "modelviewMatrix");
676 
677  if (so.isRealSize)
678  {
679  float billboardMatrix[16];
680  VuoPoint2d mesh0 = (VuoPoint2d){so.mesh->submeshes[0].positions[0].x, so.mesh->submeshes[0].positions[0].y};
681  VuoTransform_getBillboardMatrix(image->pixelsWide, image->pixelsHigh, image->scaleFactor, so.preservePhysicalSize, modelviewMatrix[12], modelviewMatrix[13], sceneRenderer->viewportWidth, sceneRenderer->viewportHeight, sceneRenderer->backingScaleFactor, mesh0, billboardMatrix);
682  glUniformMatrix4fv(modelviewMatrixUniform, 1, GL_FALSE, billboardMatrix);
683  }
684  else
685  glUniformMatrix4fv(modelviewMatrixUniform, 1, GL_FALSE, modelviewMatrix);
686 
687  GLint cameraMatrixInverseUniform = VuoGlProgram_getUniformLocation(program, "cameraMatrixInverse");
688  if (cameraMatrixInverseUniform != -1)
689  glUniformMatrix4fv(cameraMatrixInverseUniform, 1, GL_FALSE, sceneRenderer->cameraMatrixInverse);
690 
691  GLint cameraPositionUniform = VuoGlProgram_getUniformLocation(program, "cameraPosition");
692  if (cameraPositionUniform != -1)
693  glUniform3f(cameraPositionUniform, sceneRenderer->camera.transform.translation.x, sceneRenderer->camera.transform.translation.y, sceneRenderer->camera.transform.translation.z);
694 
695  GLint aspectRatioUniform = VuoGlProgram_getUniformLocation(program, "aspectRatio");
696  if (aspectRatioUniform != -1)
697  glUniform1f(aspectRatioUniform, (float)sceneRenderer->viewportWidth/(float)sceneRenderer->viewportHeight);
698 
699  GLint viewportSizeUniform = VuoGlProgram_getUniformLocation(program, "viewportSize");
700  if (viewportSizeUniform != -1)
701  glUniform2f(viewportSizeUniform, (float)sceneRenderer->viewportWidth, (float)sceneRenderer->viewportHeight);
702 
703  GLint ambientColorUniform = VuoGlProgram_getUniformLocation(program, "ambientColor");
704  if (ambientColorUniform != -1)
705  // Unlike typical VuoColors, convert these from sRGB-gamma2.2 into sRGB-linear,
706  // since that's what lighting.glsl expects.
707  glUniform4f(ambientColorUniform,
708  pow(sceneRenderer->ambientColor.r, 2.2) * sceneRenderer->ambientColor.a,
709  pow(sceneRenderer->ambientColor.g, 2.2) * sceneRenderer->ambientColor.a,
710  pow(sceneRenderer->ambientColor.b, 2.2) * sceneRenderer->ambientColor.a,
711  sceneRenderer->ambientColor.a);
712  GLint ambientBrightnessUniform = VuoGlProgram_getUniformLocation(program, "ambientBrightness");
713  if (ambientBrightnessUniform != -1)
714  glUniform1f(ambientBrightnessUniform, pow(sceneRenderer->ambientBrightness, 2.2));
715 
716  size_t uniformSize = 27; // strlen("pointLights[15].brightness")+1
717  char uniformName[uniformSize];
718 
719  int pointLightCount = VuoListGetCount_VuoSceneObject(sceneRenderer->pointLights);
720  GLint pointLightCountUniform = VuoGlProgram_getUniformLocation(program, "pointLightCount");
721  if (pointLightCountUniform != -1)
722  {
723  glUniform1i(pointLightCountUniform, pointLightCount);
724 
725  strlcpy(uniformName, "pointLights[", uniformSize);
726  const int prefixLength = 12; // strlen("pointLights[")
727 
728  for (int i=1; i<=pointLightCount; ++i)
729  {
730  VuoSceneObject pointLight = VuoListGetValue_VuoSceneObject(sceneRenderer->pointLights, i);
731 
732  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].color");
733  GLint colorUniform = VuoGlProgram_getUniformLocation(program, uniformName);
734  // Unlike typical VuoColors, convert these from sRGB-gamma2.2 into sRGB-linear,
735  // since that's what lighting.glsl expects.
736  glUniform4f(colorUniform,
737  pow(pointLight.lightColor.r, 2.2) * pointLight.lightColor.a,
738  pow(pointLight.lightColor.g, 2.2) * pointLight.lightColor.a,
739  pow(pointLight.lightColor.b, 2.2) * pointLight.lightColor.a,
740  pointLight.lightColor.a);
741 
742  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].brightness");
743  GLint brightnessUniform = VuoGlProgram_getUniformLocation(program, uniformName);
744  glUniform1f(brightnessUniform, pow(pointLight.lightBrightness, 2.2));
745 
746  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].position");
747  GLint positionUniform = VuoGlProgram_getUniformLocation(program, uniformName);
748  glUniform3f(positionUniform, pointLight.transform.translation.x, pointLight.transform.translation.y, pointLight.transform.translation.z);
749 
750  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].range");
751  GLint rangeUniform = VuoGlProgram_getUniformLocation(program, uniformName);
752  glUniform1f(rangeUniform, pointLight.lightRange);
753 
754  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].sharpness");
755  GLint sharpnessUniform = VuoGlProgram_getUniformLocation(program, uniformName);
756  glUniform1f(sharpnessUniform, pointLight.lightSharpness);
757  }
758  }
759 
760  int spotLightCount = VuoListGetCount_VuoSceneObject(sceneRenderer->spotLights);
761  GLint spotLightCountUniform = VuoGlProgram_getUniformLocation(program, "spotLightCount");
762  if (spotLightCountUniform != -1)
763  {
764  glUniform1i(spotLightCountUniform, spotLightCount);
765 
766  strlcpy(uniformName, "spotLights[", uniformSize);
767  const int prefixLength = 11; // strlen("spotLights[")
768 
769  for (int i=1; i<=spotLightCount; ++i)
770  {
771  VuoSceneObject spotLight = VuoListGetValue_VuoSceneObject(sceneRenderer->spotLights, i);
772 
773  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].color");
774  GLint colorUniform = VuoGlProgram_getUniformLocation(program, uniformName);
775  // Unlike typical VuoColors, convert these from sRGB-gamma2.2 into sRGB-linear,
776  // since that's what lighting.glsl expects.
777  glUniform4f(colorUniform,
778  pow(spotLight.lightColor.r, 2.2) * spotLight.lightColor.a,
779  pow(spotLight.lightColor.g, 2.2) * spotLight.lightColor.a,
780  pow(spotLight.lightColor.b, 2.2) * spotLight.lightColor.a,
781  spotLight.lightColor.a);
782 
783  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].brightness");
784  GLint brightnessUniform = VuoGlProgram_getUniformLocation(program, uniformName);
785  glUniform1f(brightnessUniform, pow(spotLight.lightBrightness, 2.2));
786 
787  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].position");
788  GLint positionUniform = VuoGlProgram_getUniformLocation(program, uniformName);
789  glUniform3f(positionUniform, spotLight.transform.translation.x, spotLight.transform.translation.y, spotLight.transform.translation.z);
790 
791  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].direction");
792  GLint directionUniform = VuoGlProgram_getUniformLocation(program, uniformName);
793  VuoPoint3d direction = VuoTransform_getDirection(spotLight.transform);
794  glUniform3f(directionUniform, direction.x, direction.y, direction.z);
795 
796  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].cone");
797  GLint coneUniform = VuoGlProgram_getUniformLocation(program, uniformName);
798  glUniform1f(coneUniform, spotLight.lightCone);
799 
800  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].range");
801  GLint rangeUniform = VuoGlProgram_getUniformLocation(program, uniformName);
802  glUniform1f(rangeUniform, spotLight.lightRange);
803 
804  VuoSceneRenderer_addUniformSuffix(uniformName+prefixLength, i-1, "].sharpness");
805  GLint sharpnessUniform = VuoGlProgram_getUniformLocation(program, uniformName);
806  glUniform1f(sharpnessUniform, spotLight.lightSharpness);
807  }
808  }
809 
811 
812  VuoSceneRenderer_setGL(GL_SAMPLE_ALPHA_TO_COVERAGE, so.shader->useAlphaAsCoverage);
813  VuoSceneRenderer_setGL(GL_SAMPLE_ALPHA_TO_ONE, so.shader->useAlphaAsCoverage);
814 
815  if (so.blendMode == VuoBlendMode_Normal)
816  {
817  VuoSceneRenderer_setGL(GL_DEPTH_TEST, true);
818  VuoSceneRenderer_setGLBlendFunction(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
819  VuoSceneRenderer_setGLBlendEquation(GL_FUNC_ADD, GL_FUNC_ADD);
820  }
821  else
822  {
823  VuoSceneRenderer_setGL(GL_DEPTH_TEST, false);
824 
825  if (so.blendMode == VuoBlendMode_Multiply)
826  {
827  VuoSceneRenderer_setGLBlendFunction(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
828  VuoSceneRenderer_setGLBlendEquation(GL_FUNC_ADD, GL_FUNC_ADD);
829  }
830  else if (so.blendMode == VuoBlendMode_DarkerComponents)
831  {
833  VuoSceneRenderer_setGLBlendFunction(GL_ONE, GL_ONE, GL_ONE, GL_ONE);
834  VuoSceneRenderer_setGLBlendEquation(GL_MIN, GL_FUNC_ADD);
835  }
836  else if (so.blendMode == VuoBlendMode_LighterComponents)
837  {
838  VuoSceneRenderer_setGLBlendFunction(GL_ZERO, GL_ZERO, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
839  VuoSceneRenderer_setGLBlendEquation(GL_MAX, GL_FUNC_ADD);
840  }
841  else if (so.blendMode == VuoBlendMode_LinearDodge)
842  {
843  VuoSceneRenderer_setGLBlendFunction(GL_ONE, GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
844  VuoSceneRenderer_setGLBlendEquation(GL_FUNC_ADD, GL_FUNC_ADD);
845  }
846  else if (so.blendMode == VuoBlendMode_Subtract)
847  {
848  VuoSceneRenderer_setGLBlendFunction(GL_ONE, GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
849  VuoSceneRenderer_setGLBlendEquation(GL_FUNC_REVERSE_SUBTRACT, GL_FUNC_ADD);
850  }
851  }
852 
853  glBindVertexArray(soi->vao);
854 
855  VuoSubmesh submesh = so.mesh->submeshes[0];
856  GLenum mode = VuoSubmesh_getGlMode(submesh);
857 
858  GLint hasTextureCoordinatesUniform = VuoGlProgram_getUniformLocation(program, "hasTextureCoordinates");
859  if (hasTextureCoordinatesUniform != -1)
860  glUniform1i(hasTextureCoordinatesUniform, submesh.glUpload.textureCoordinateOffset != NULL);
861 
862  GLint primitiveHalfSizeUniform = VuoGlProgram_getUniformLocation(program, "primitiveHalfSize");
863  if (primitiveHalfSizeUniform != -1)
864  glUniform1f(primitiveHalfSizeUniform, submesh.primitiveSize/2);
865 
866  bool enableCulling = submesh.faceCullingMode != GL_NONE && !so.shader->isTransparent;
867  VuoSceneRenderer_setGL(GL_CULL_FACE, enableCulling);
868  if (enableCulling)
870 
871  if (VuoIsDebugEnabled() && positionAttribute)
872  {
873  VGL();
874  VuoSceneRenderer_checkDataBounds(cgl_ctx, submesh, positionAttribute);
875  }
876 
877  unsigned long completeInputElementCount = VuoSubmesh_getCompleteElementCount(submesh);
878  if (submesh.elementCount)
879  glDrawElements(mode, completeInputElementCount, GL_UNSIGNED_INT, (void*)0);
880  else if (submesh.vertexCount)
881  glDrawArrays(mode, 0, completeInputElementCount);
882 
884  }
885  VuoShader_deactivate(so.shader, so.mesh->submeshes[0].elementAssemblyMethod, cgl_ctx);
886 
887 #ifdef VUO_PROFILE
888  double seconds;
889  glEndQuery(GL_TIME_ELAPSED_EXT);
890  GLuint nanoseconds;
891  glGetQueryObjectuiv(timeElapsedQuery, GL_QUERY_RESULT, &nanoseconds);
892  seconds = ((double)nanoseconds) / NSEC_PER_SEC;
893  glDeleteQueries(1, &timeElapsedQuery);
894 
895  std::string description = so.name ? std::string(so.name) : "no name";
896  description += std::string(" (") + so.shader->name + ")";
897  sceneRenderer->profileTimes.push_back(make_pair(description, seconds));
898 #endif
899 }
900 
901 //static void VuoSceneRenderer_drawElement(VuoSceneObject so, float projectionMatrix[16], float compositeModelviewMatrix[16], VuoGlContext glContext, int element, float length);
902 
910 static void VuoSceneRenderer_drawSceneObjects(VuoSceneRendererInternal *sceneRenderer, VuoGlContext glContext, list<VuoSceneRenderer_TreeRenderState> &renderList)
911 {
912  for (auto currentState : renderList)
913  {
914  VuoSceneRenderer_drawSceneObject(*currentState.so, currentState.soi, sceneRenderer->projectionMatrix, currentState.modelviewMatrix, sceneRenderer, glContext);
915 
916 // if (objectTypesToRender & VuoSceneRenderer_OpaqueObjects)
917 // {
918 // VuoSceneRenderer_drawElement(currentState.so, sceneRenderer->projectionMatrix, currentState.modelviewMatrix, glContext, 0, .08f); // Normals
919 // VuoSceneRenderer_drawElement(currentState.so, sceneRenderer->projectionMatrix, currentState.modelviewMatrix, glContext, 1, .08f); // Tangents
920 // VuoSceneRenderer_drawElement(currentState.so, sceneRenderer->projectionMatrix, currentState.modelviewMatrix, glContext, 2, .08f); // Bitangents
921 // }
922  }
923 }
924 
925 //static void VuoSceneRenderer_drawLights(VuoSceneRendererInternal *sceneRenderer);
928 static void VuoSceneRenderer_uploadSceneObjects(VuoSceneRendererInternal *sceneRenderer, VuoGlContext glContext, VuoPoint3d cameraTranslation);
929 
936 {
937  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
938 
939  if (!sceneRenderer->rootSceneObjectPendingValid && !sceneRenderer->rootSceneObjectValid)
940  return;
941 
942  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
943 
944  if (sceneRenderer->rootSceneObjectPendingUpdated)
945  {
946  // Release the old scenegraph.
947  if (sceneRenderer->rootSceneObjectValid)
948  {
949  VuoSceneRenderer_cleanupRenderLists(sceneRenderer);
950  VuoSceneRenderer_cleanupMeshShaderItems(sceneRenderer, cgl_ctx);
951  VuoSceneObject_release(sceneRenderer->rootSceneObject);
952  VuoRelease(sceneRenderer->pointLights);
953  VuoRelease(sceneRenderer->spotLights);
954  }
955 
956  // Upload the new scenegraph.
957  sceneRenderer->rootSceneObject = sceneRenderer->rootSceneObjectPending;
958  VuoSceneObject_retain(sceneRenderer->rootSceneObject);
959 
960  sceneRenderer->rootSceneObjectValid = true;
962 
963  VuoSceneRenderer_uploadSceneObjects(sceneRenderer, cgl_ctx, sceneRenderer->camera.transform.translation);
964 
965  VuoSceneObject_findLights(sceneRenderer->rootSceneObject, &sceneRenderer->ambientColor, &sceneRenderer->ambientBrightness, &sceneRenderer->pointLights, &sceneRenderer->spotLights);
966  VuoRetain(sceneRenderer->pointLights);
967  VuoRetain(sceneRenderer->spotLights);
968 
969  sceneRenderer->rootSceneObjectPendingUpdated = false;
970 
971 #if 0
972  VLog("Render lists:");
973  VLog(" Opaque:");
974  for (auto currentState : sceneRenderer->opaqueObjects)
975  {
976  VLog(" '%s' @ %g,%g,%g with '%s'", currentState.so->name,
977  currentState.modelviewMatrix[12],
978  currentState.modelviewMatrix[13],
979  currentState.modelviewMatrix[14],
980  currentState.so->shader ? currentState.so->shader->name : NULL
981  );
982  }
983  VLog(" Potentially Transparent:");
984  for (auto currentState : sceneRenderer->potentiallyTransparentObjects)
985  {
986  VLog(" '%s' @ %g,%g,%g with '%s'", currentState.so->name,
987  currentState.modelviewMatrix[12],
988  currentState.modelviewMatrix[13],
989  currentState.modelviewMatrix[14],
990  currentState.so->shader->name
991  );
992  }
993 #endif
994  }
995 
996 
997  // Start out with VuoGlContextPool::createContext()'s initial state.
998  bzero(&sceneRenderer->glState, sizeof(sceneRenderer->glState));
999  sceneRenderer->glState.vGL_DEPTH_MASK = true;
1000  sceneRenderer->glState.vGL_DEPTH_TEST = false;
1001  sceneRenderer->glState.vGL_CULL_FACE = true;
1002  sceneRenderer->glState.vGL_CULL_FACE_MODE = GL_BACK;
1003  sceneRenderer->glState.vGL_BLEND_SRC_RGB = GL_ONE;
1004  sceneRenderer->glState.vGL_BLEND_DST_RGB = GL_ONE_MINUS_SRC_ALPHA;
1005  sceneRenderer->glState.vGL_BLEND_SRC_ALPHA = GL_ONE;
1006  sceneRenderer->glState.vGL_BLEND_DST_ALPHA = GL_ONE_MINUS_SRC_ALPHA;
1007  sceneRenderer->glState.vGL_BLEND_EQUATION_RGB = GL_FUNC_ADD;
1008  sceneRenderer->glState.vGL_BLEND_EQUATION_ALPHA = GL_FUNC_ADD;
1009  sceneRenderer->glState.vGL_SAMPLE_ALPHA_TO_COVERAGE = false;
1010  sceneRenderer->glState.vGL_SAMPLE_ALPHA_TO_ONE = false;
1011 
1013 
1014  VuoSceneRenderer_drawSceneObjects(sceneRenderer, cgl_ctx, sceneRenderer->opaqueObjects);
1015 
1016  if (!sceneRenderer->potentiallyTransparentObjects.empty())
1017  VuoSceneRenderer_drawSceneObjects(sceneRenderer, cgl_ctx, sceneRenderer->potentiallyTransparentObjects);
1018 
1019  // Restore the context back to VuoGlContextPool::createContext()'s initial state.
1021  VuoSceneRenderer_setGL(GL_DEPTH_TEST, false);
1022  VuoSceneRenderer_setGL(GL_CULL_FACE, true );
1024  VuoSceneRenderer_setGLBlendFunction(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1025  VuoSceneRenderer_setGLBlendEquation(GL_FUNC_ADD, GL_FUNC_ADD);
1026  VuoSceneRenderer_setGL(GL_SAMPLE_ALPHA_TO_COVERAGE, false);
1027  VuoSceneRenderer_setGL(GL_SAMPLE_ALPHA_TO_ONE, false);
1028 
1029 
1030 // VuoSceneRenderer_drawLights(sceneRenderer);
1031 
1032 
1033 #if VUO_PRO
1034  if (sceneRenderer->camera.type == VuoSceneObjectSubType_FisheyeCamera)
1035  VuoSceneRendererPro_drawVignette(sceneRenderer, cgl_ctx);
1036 #endif
1037 
1038 #ifdef VUO_PROFILE
1039  VL();
1040  VLog("Object render time (percent of a 60 Hz frame)");
1041  double totalPercent = 0;
1042  sceneRenderer->profileTimes.sort(VuoProfileSort);
1043  for (std::list<VuoProfileEntry>::iterator i = sceneRenderer->profileTimes.begin(); i != sceneRenderer->profileTimes.end(); ++i)
1044  {
1045  double objectPercent = i->second / (1./60.) * 100.;
1046  VLog(" %6.2f %s", objectPercent, i->first.c_str());
1047  totalPercent += objectPercent;
1048  }
1049  VLog(" ------ -----");
1050  VLog(" %6.2f total", totalPercent);
1051  sceneRenderer->profileTimes.clear();
1052 #endif
1053 
1054  glFlushRenderAPPLE();
1055 
1056  if (VuoIsDebugEnabled())
1057  {
1058  GLint rendererID;
1059  CGLGetParameter(cgl_ctx, kCGLCPCurrentRendererID, &rendererID);
1060  if (rendererID != sceneRenderer->glContextRendererID)
1061  {
1062  VUserLog("OpenGL context %p's renderer changed to %s", cgl_ctx, VuoCglRenderer_getText(rendererID));
1063  sceneRenderer->glContextRendererID = rendererID;
1064  }
1065  }
1066 
1067  });
1068 }
1069 
1077 /*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
1078 {
1079  if (!so.mesh)
1080  return;
1081 
1082  CGLContextObj cgl_ctx = (CGLContextObj)glContext;
1083 
1084  glLoadIdentity();
1085 
1086  glMatrixMode(GL_PROJECTION);
1087  glLoadMatrixf(projectionMatrix);
1088 
1089  glMatrixMode(GL_MODELVIEW);
1090  glLoadMatrixf(compositeModelviewMatrix);
1091 
1092  glBegin(GL_LINES);
1093  bool first=true;
1094  for (unsigned int i = 0; i < so.mesh->submeshCount; i++)
1095  {
1096  VuoSubmesh submesh = so.mesh->submeshes[i];
1097  VuoSubmesh_download(&submesh);
1098 
1099  for(unsigned int m = 0; m < submesh.vertexCount; m++)
1100  {
1101  VuoPoint4d v = submesh.positions[m];
1102  VuoPoint4d n = (VuoPoint4d){0,0,1,1};
1103 
1104  switch(element)
1105  {
1106  case 0: // normals
1107  n = submesh.normals[m];
1108  glColor3f(1,.5,.5);
1109  break;
1110  case 1: // tangents
1111  n = submesh.tangents[m];
1112  glColor3f(.5,1,.5);
1113  break;
1114  case 2: // bitangents
1115  n = submesh.bitangents[m];
1116  glColor3f(.5,.5,1);
1117  break;
1118  }
1119 
1120  if (first)
1121  {
1122  VuoPoint3d n3 = VuoPoint3d_make(n.x,n.y,n.z);
1123  VUserLog("%p [%d]: %s, length %g",&so,element,VuoPoint4d_getSummary(n),VuoPoint3d_magnitude(n3));
1124 
1125  // Check orthogonality
1126  {
1127  VuoPoint3d n3 = VuoPoint3d_make(submesh.normals[m].x,submesh.normals[m].y,submesh.normals[m].z);
1128  VuoPoint3d t3 = VuoPoint3d_make(submesh.tangents[m].x,submesh.tangents[m].y,submesh.tangents[m].z);
1129  VuoPoint3d b3 = VuoPoint3d_make(submesh.bitangents[m].x,submesh.bitangents[m].y,submesh.bitangents[m].z);
1130 
1131  if (VuoPoint3d_dotProduct(n3,t3) > 0.0001)
1132  VUserLog(" n•t = %g; should be 0",VuoPoint3d_dotProduct(n3,t3));
1133  if (VuoPoint3d_dotProduct(n3,b3) > 0.0001)
1134  VUserLog(" n•b = %g; should be 0",VuoPoint3d_dotProduct(n3,b3));
1135  }
1136  }
1137  first=false;
1138 
1139  n = VuoPoint4d_add(v, VuoPoint4d_multiply(n, length));
1140 
1141  glVertex3d(v.x, v.y, v.z);
1142  glVertex3d(n.x, n.y, n.z);
1143  }
1144  }
1145  glEnd();
1146 
1147  glLoadIdentity();
1148 }*/
1149 
1153 /*static void drawCircle(CGLContextObj cgl_ctx, VuoPoint3d center, float radius, VuoPoint3d normal)
1154 {
1155  glPushMatrix();
1156  VuoTransform t = VuoTransform_makeQuaternion(
1157  center,
1158  VuoTransform_quaternionFromVectors(VuoPoint3d_make(1,0,0), normal),
1159  VuoPoint3d_make(1,1,1));
1160  float m[16];
1161  VuoTransform_getMatrix(t, m);
1162  glLoadMatrixf(m);
1163 
1164  glBegin(GL_LINE_LOOP);
1165  const int segments = 32;
1166  for (int i=0; i<segments; ++i)
1167  glVertex3f(
1168  0,
1169  cos(2.*M_PI*i/segments)*radius,
1170  sin(2.*M_PI*i/segments)*radius);
1171  glEnd();
1172 
1173  glPopMatrix();
1174 }*/
1175 
1179 /*static void drawCone(CGLContextObj cgl_ctx, VuoPoint3d center, float radius, VuoPoint3d normal, float height)
1180 {
1181  glPushMatrix();
1182  VuoTransform t = VuoTransform_makeQuaternion(
1183  center,
1184  VuoTransform_quaternionFromVectors(VuoPoint3d_make(1,0,0), normal),
1185  VuoPoint3d_make(1,1,1));
1186  float m[16];
1187  VuoTransform_getMatrix(t, m);
1188  glLoadMatrixf(m);
1189 
1190  glBegin(GL_LINE_LOOP);
1191  const int segments = 32;
1192  for (int i=0; i<segments; ++i)
1193  glVertex3f(
1194  0,
1195  cos(2.*M_PI*i/segments)*radius,
1196  sin(2.*M_PI*i/segments)*radius);
1197  glEnd();
1198  glBegin(GL_LINES);
1199  glVertex3f(-height, 0, 0); glVertex3f(0, radius, 0);
1200  glVertex3f(-height, 0, 0); glVertex3f(0, 0, radius);
1201  glVertex3f(-height, 0, 0); glVertex3f(0, -radius, 0);
1202  glVertex3f(-height, 0, 0); glVertex3f(0, 0, -radius);
1203  glEnd();
1204 
1205  glPopMatrix();
1206 }*/
1207 
1211 /*static void VuoSceneRenderer_drawLights(VuoSceneRendererInternal *sceneRenderer)
1212 {
1213  CGLContextObj cgl_ctx = (CGLContextObj)sceneRenderer->glContext;
1214 
1215  glPointSize(20);
1216 
1217  glMatrixMode(GL_PROJECTION);
1218  glLoadMatrixf(sceneRenderer->projectionMatrix);
1219 
1220  glMatrixMode(GL_MODELVIEW);
1221  glLoadIdentity();
1222 
1223  int pointLightCount = VuoListGetCount_VuoSceneObject(sceneRenderer->pointLights);
1224  for (int i=1; i<=pointLightCount; ++i)
1225  {
1226  VuoSceneObject pointLight = VuoListGetValue_VuoSceneObject(sceneRenderer->pointLights, i);
1227  VuoPoint3d position = pointLight.transform.translation;
1228  glBegin(GL_POINTS);
1229  glVertex3f(position.x, position.y, position.z);
1230  glEnd();
1231 
1232  // Draw a pair of concentric sphere outlines illustrating the light's range.
1233  {
1234  float innerRange = pointLight.lightRange*pointLight.lightSharpness;
1235  float outerRange = pointLight.lightRange*(2-pointLight.lightSharpness);
1236 
1237  // XY plane
1238  drawCircle(cgl_ctx, position, innerRange, VuoPoint3d_make(0,0,1));
1239  drawCircle(cgl_ctx, position, outerRange, VuoPoint3d_make(0,0,1));
1240  // XZ plane
1241  drawCircle(cgl_ctx, position, innerRange, VuoPoint3d_make(0,1,0));
1242  drawCircle(cgl_ctx, position, outerRange, VuoPoint3d_make(0,1,0));
1243  // YZ plane
1244  drawCircle(cgl_ctx, position, innerRange, VuoPoint3d_make(1,0,0));
1245  drawCircle(cgl_ctx, position, outerRange, VuoPoint3d_make(1,0,0));
1246  }
1247  }
1248 
1249  int spotLightCount = VuoListGetCount_VuoSceneObject(sceneRenderer->spotLights);
1250  for (int i=1; i<=spotLightCount; ++i)
1251  {
1252  VuoSceneObject spotLight = VuoListGetValue_VuoSceneObject(sceneRenderer->spotLights, i);
1253  VuoPoint3d position = spotLight.transform.translation;
1254  glBegin(GL_POINTS);
1255  glVertex3f(position.x, position.y, position.z);
1256  glEnd();
1257 
1258  VuoPoint3d direction = VuoTransform_getDirection(spotLight.transform);
1259  float innerRange = spotLight.lightRange*spotLight.lightSharpness;
1260  float outerRange = spotLight.lightRange*(2-spotLight.lightSharpness);
1261 
1262  // Draw a pair of cones (whose bases are the intersection of a plane and a spherical shell) to illustrate the light's range.
1263  {
1264  float innerConeAngle = spotLight.lightCone*spotLight.lightSharpness;
1265  float outerConeAngle = spotLight.lightCone*(2-spotLight.lightSharpness);
1266 
1267  float innerIntersectionDistance = innerRange*cos(innerConeAngle/2.);
1268  float outerIntersectionDistance = outerRange*cos(outerConeAngle/2.);
1269 
1270  float innerIntersectionRadius = innerRange*sin(innerConeAngle/2.);
1271  float outerIntersectionRadius = outerRange*sin(outerConeAngle/2.);
1272 
1273  VuoPoint3d innerEndpoint = VuoPoint3d_add(position, VuoPoint3d_multiply(direction, innerIntersectionDistance));
1274  VuoPoint3d outerEndpoint = VuoPoint3d_add(position, VuoPoint3d_multiply(direction, outerIntersectionDistance));
1275 
1276  drawCone(cgl_ctx, innerEndpoint, innerIntersectionRadius, direction, innerIntersectionDistance);
1277  drawCone(cgl_ctx, outerEndpoint, outerIntersectionRadius, direction, outerIntersectionDistance);
1278  }
1279  }
1280 }*/
1281 
1295 {
1296  soi->overrideIsRealSize = VuoSceneRendererInternal_object::RealSize_Inherit;
1297  soi->overrideShader = NULL;
1298 
1299  if (so->type == VuoSceneObjectSubType_Text && so->text && so->text[0] != 0)
1300  {
1301  soi->overrideIsRealSize = VuoSceneRendererInternal_object::RealSize_True;
1302  if (!sceneRenderer->sharedTextOverrideShader)
1303  {
1304  sceneRenderer->sharedTextOverrideShader = VuoShader_makeUnlitImageShader(NULL, 1);
1305  VuoRetain(sceneRenderer->sharedTextOverrideShader);
1306  }
1307  soi->overrideShader = sceneRenderer->sharedTextOverrideShader;
1308  VuoRetain(soi->overrideShader);
1309  }
1310 
1311  // Apply the overrides just within this function's scope.
1312  VuoShader shader = so->shader;
1313  if (soi->overrideShader)
1314  shader = soi->overrideShader;
1315 
1316 
1317  if (!so->mesh || !so->mesh->submeshCount || !shader)
1318  return false;
1319 
1320 
1321  // If we already have a VAO for this mesh/shader combination, use it.
1322  VuoSceneRendererMeshShader meshShader(so->mesh, shader);
1323  VuoSceneRendererMeshShaderVAOs::iterator i = sceneRenderer->meshShaderItems.find(meshShader);
1324  if (cache && i != sceneRenderer->meshShaderItems.end())
1325  {
1326  soi->vao = i->second;
1327  return true;
1328  }
1329 
1330 
1331  // …otherwise, create one.
1332 
1333  CGLContextObj cgl_ctx = (CGLContextObj)glContext;
1334 
1335  // Fetch the shader's attribute locations.
1336  // All VuoSubmeshes are assumed to have the same elementAssemblyMethod.
1337  GLint positionAttribute, normalAttribute, tangentAttribute, bitangentAttribute, textureCoordinateAttribute;
1338  if (!VuoShader_getAttributeLocations(shader, so->mesh->submeshes[0].elementAssemblyMethod, glContext, &positionAttribute, &normalAttribute, &tangentAttribute, &bitangentAttribute, &textureCoordinateAttribute))
1339  VUserLog("Error: Couldn't fetch the shader's attribute locations.");
1340 
1341 
1342  VuoSubmesh meshItem = so->mesh->submeshes[0];
1343  GLuint vertexArray;
1344 
1345 
1346  // Create a Vertex Array Object, to store this sceneobject's vertex array bindings.
1347  glGenVertexArrays(1, &vertexArray);
1348  glBindVertexArray(vertexArray);
1349 
1350 
1351  // Bind the combined buffer to the Vertex Array Object.
1352  glBindBuffer(GL_ARRAY_BUFFER, so->mesh->submeshes[0].glUpload.combinedBuffer);
1353 
1354 
1355  // Populate the Vertex Array Object with the various vertex attributes.
1356 
1357  int stride = VuoSubmesh_getStride(so->mesh->submeshes[0]);
1358  glEnableVertexAttribArray((GLuint)positionAttribute);
1359  glVertexAttribPointer((GLuint)positionAttribute, 4 /* XYZW */, GL_FLOAT, GL_FALSE, stride, (void*)0);
1360 
1361  if (meshItem.glUpload.normalOffset && normalAttribute>=0)
1362  {
1363  glEnableVertexAttribArray((GLuint)normalAttribute);
1364  glVertexAttribPointer((GLuint)normalAttribute, 4 /* XYZW */, GL_FLOAT, GL_FALSE, stride, so->mesh->submeshes[0].glUpload.normalOffset);
1365  }
1366 
1367  if (meshItem.glUpload.tangentOffset && tangentAttribute>=0)
1368  {
1369  glEnableVertexAttribArray((GLuint)tangentAttribute);
1370  glVertexAttribPointer((GLuint)tangentAttribute, 4 /* XYZW */, GL_FLOAT, GL_FALSE, stride, so->mesh->submeshes[0].glUpload.tangentOffset);
1371  }
1372 
1373  if (meshItem.glUpload.bitangentOffset && bitangentAttribute>=0)
1374  {
1375  glEnableVertexAttribArray((GLuint)bitangentAttribute);
1376  glVertexAttribPointer((GLuint)bitangentAttribute, 4 /* XYZW */, GL_FLOAT, GL_FALSE, stride, so->mesh->submeshes[0].glUpload.bitangentOffset);
1377  }
1378 
1379  if (meshItem.glUpload.textureCoordinateOffset && textureCoordinateAttribute>=0)
1380  {
1381  glEnableVertexAttribArray((GLuint)textureCoordinateAttribute);
1382  glVertexAttribPointer((GLuint)textureCoordinateAttribute, 4 /* XYZW */, GL_FLOAT, GL_FALSE, stride, so->mesh->submeshes[0].glUpload.textureCoordinateOffset);
1383  }
1384 
1385 
1386  // Bind the Element Buffer to the Vertex Array Object
1387  if (so->mesh->submeshes[0].elementCount)
1388  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, so->mesh->submeshes[0].glUpload.elementBuffer);
1389 
1390 
1391  glBindVertexArray(0);
1392 
1393  soi->vao = vertexArray;
1394 
1395  glBindBuffer(GL_ARRAY_BUFFER, 0);
1396 
1397  if (cache)
1398  sceneRenderer->meshShaderItems[meshShader] = soi->vao;
1399 
1400  return true;
1401 }
1402 
1410 static void VuoSceneRenderer_uploadSceneObjects(VuoSceneRendererInternal *sceneRenderer, VuoGlContext glContext, VuoPoint3d cameraTranslation)
1411 {
1413  rootState.so = &sceneRenderer->rootSceneObject;
1414  rootState.soi = new VuoSceneRendererInternal_object;
1415  VuoTransform_getMatrix(VuoTransform_makeIdentity(), rootState.modelviewMatrix);
1416 
1417  list<VuoSceneRenderer_TreeRenderState> objects(1, rootState);
1418  while (!objects.empty())
1419  {
1420  VuoSceneRenderer_TreeRenderState currentState = objects.front();
1421  objects.pop_front();
1422 
1423  float localModelviewMatrix[16];
1424  VuoTransform_getMatrix(currentState.so->transform, localModelviewMatrix);
1425  float compositeModelviewMatrix[16];
1426  VuoTransform_multiplyMatrices4x4(localModelviewMatrix, currentState.modelviewMatrix, compositeModelviewMatrix);
1427  VuoTransform_copyMatrix4x4(compositeModelviewMatrix, currentState.modelviewMatrix);
1428 
1429  bool isRenderable = VuoSceneRenderer_uploadSceneObject(sceneRenderer, currentState.so, currentState.soi, glContext, true);
1430  if (isRenderable)
1431  {
1432  if (sceneRenderer->shouldSortByDepth)
1433  {
1434  if (VuoShader_isOpaque(currentState.so->shader))
1435  sceneRenderer->opaqueObjects.push_back(currentState);
1436  else
1437  sceneRenderer->potentiallyTransparentObjects.push_back(currentState);
1438  }
1439  else
1440  sceneRenderer->opaqueObjects.push_back(currentState);
1441  }
1442 
1443  // Prepend childObjects to the objects queue.
1444  // (Do it in reverse order so the objects end up in forward order after calling ::push_front repeatedly.) @@@
1445  VuoSceneObject *childObjects = VuoListGetData_VuoSceneObject(currentState.so->childObjects);
1446  for (long i = VuoListGetCount_VuoSceneObject(currentState.so->childObjects) - 1; i >= 0; --i)
1447  {
1449  childState.so = &(childObjects[i]);
1450  childState.soi = new VuoSceneRendererInternal_object;
1451  memcpy(childState.modelviewMatrix, compositeModelviewMatrix, sizeof(float[16]));
1452  objects.push_front(childState);
1453  }
1454 
1455  if (!isRenderable)
1456  delete currentState.soi;
1457  }
1458 
1459  if (sceneRenderer->shouldSortByDepth)
1460  {
1461  // Sort opaque objects to render front to back (to reduce overdraw).
1462  // For better performance, this takes into account the object's transform but not the object's vertices.
1463  sceneRenderer->opaqueObjects.sort([=](const VuoSceneRenderer_TreeRenderState &a, const VuoSceneRenderer_TreeRenderState &b){
1464  return VuoPoint3d_distance(VuoTransform_getMatrix4x4Translation(a.modelviewMatrix), cameraTranslation)
1465  < VuoPoint3d_distance(VuoTransform_getMatrix4x4Translation(b.modelviewMatrix), cameraTranslation);
1466  });
1467 
1468  // Sort potentially-transparent objects to render back to front (for proper blending).
1469  sceneRenderer->potentiallyTransparentObjects.sort([=](const VuoSceneRenderer_TreeRenderState &a, const VuoSceneRenderer_TreeRenderState &b){
1470  return VuoPoint3d_distance(VuoTransform_getMatrix4x4Translation(a.modelviewMatrix), cameraTranslation)
1471  > VuoPoint3d_distance(VuoTransform_getMatrix4x4Translation(b.modelviewMatrix), cameraTranslation);
1472  });
1473  }
1474 }
1475 
1482 {
1483  for (auto object : sceneRenderer->opaqueObjects)
1484  {
1485  if (object.soi->overrideShader)
1486  VuoRelease(object.soi->overrideShader);
1487  delete object.soi;
1488  }
1489  sceneRenderer->opaqueObjects.clear();
1490 
1491  for (auto object : sceneRenderer->potentiallyTransparentObjects)
1492  {
1493  if (object.soi->overrideShader)
1494  VuoRelease(object.soi->overrideShader);
1495  delete object.soi;
1496  }
1497  sceneRenderer->potentiallyTransparentObjects.clear();
1498 }
1499 
1504 {
1505  CGLContextObj cgl_ctx = (CGLContextObj)glContext;
1506 
1507  for (VuoSceneRendererMeshShaderVAOs::iterator msi = sceneRenderer->meshShaderItems.begin(); msi != sceneRenderer->meshShaderItems.end(); ++msi)
1508  glDeleteVertexArrays(1, &(msi->second));
1509 
1510  sceneRenderer->meshShaderItems.clear();
1511 }
1512 
1521 {
1522  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1523 
1524  dispatch_semaphore_wait(sceneRenderer->scenegraphSemaphore, DISPATCH_TIME_FOREVER);
1525  {
1526  VuoSceneObject_retain(rootSceneObject);
1527 
1528  if (sceneRenderer->rootSceneObjectPendingValid)
1530 
1531  sceneRenderer->rootSceneObjectPending = rootSceneObject;
1532 
1533  sceneRenderer->rootSceneObjectPendingValid = true;
1534  sceneRenderer->rootSceneObjectPendingUpdated = true;
1535  }
1536  dispatch_semaphore_signal(sceneRenderer->scenegraphSemaphore);
1537 }
1538 
1547 {
1548  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1549  *isValid = sceneRenderer->rootSceneObjectValid;
1550  return sceneRenderer->rootSceneObject;
1551 }
1552 
1563 {
1564  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1565 
1566  dispatch_semaphore_wait(sceneRenderer->scenegraphSemaphore, DISPATCH_TIME_FOREVER);
1567  {
1568  if (sceneRenderer->cameraName)
1569  VuoRelease(sceneRenderer->cameraName);
1570 
1571  sceneRenderer->cameraName = cameraName;
1572  VuoRetain(sceneRenderer->cameraName);
1573 
1574  sceneRenderer->useLeftCamera = useLeftCamera;
1575  }
1576  dispatch_semaphore_signal(sceneRenderer->scenegraphSemaphore);
1577 }
1578 
1585 {
1586  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1587 
1588  dispatch_semaphore_wait(sceneRenderer->scenegraphSemaphore, DISPATCH_TIME_FOREVER);
1589 
1590  if (sceneRenderer->rootSceneObjectPendingValid)
1592 
1593  if (sceneRenderer->rootSceneObjectValid)
1594  {
1595  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1596  VuoSceneRenderer_cleanupRenderLists(sceneRenderer);
1597  VuoSceneRenderer_cleanupMeshShaderItems(sceneRenderer, cgl_ctx);
1598  });
1599  VuoSceneObject_release(sceneRenderer->rootSceneObject);
1600 
1601  VuoRelease(sceneRenderer->pointLights);
1602  VuoRelease(sceneRenderer->spotLights);
1603  }
1604 
1605  if (sceneRenderer->sharedTextOverrideShader)
1606  VuoRelease(sceneRenderer->sharedTextOverrideShader);
1607 
1608  if (sceneRenderer->cameraName)
1609  VuoRelease(sceneRenderer->cameraName);
1610  VuoSceneObject_release(sceneRenderer->camera);
1611 
1612 #if VUO_PRO
1613  VuoRelease(sceneRenderer->vignetteShader);
1614 #endif
1615 
1616  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1617  glDeleteRenderbuffersEXT(1, &sceneRenderer->renderBuffer);
1618  glDeleteRenderbuffersEXT(1, &sceneRenderer->renderDepthBuffer);
1619  glDeleteFramebuffers(1, &sceneRenderer->outputFramebuffer);
1620  glDeleteFramebuffers(1, &sceneRenderer->outputFramebuffer2);
1621  });
1622 
1623  dispatch_semaphore_signal(sceneRenderer->scenegraphSemaphore);
1624  dispatch_release(sceneRenderer->scenegraphSemaphore);
1625 
1626  delete sceneRenderer;
1627 }
1628 
1632 extern "C" bool VuoSceneRenderer_renderInternal(VuoSceneRenderer sr, VuoGlContext glContext, GLuint *outputTexture, GLenum target, GLuint imageGlInternalFormat, VuoMultisample multisample, GLuint *outputDepthTexture, bool invertDepthImage)
1633 {
1634  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1635 
1636  CGLContextObj cgl_ctx = (CGLContextObj)glContext;
1637 
1638  static bool force2DDepth = true;
1639  static dispatch_once_t once = 0;
1640  dispatch_once(&once, ^{
1641  // If the user set the `force2DDepth` preference, use it.
1642  Boolean overridden = false;
1643  force2DDepth = (int)CFPreferencesGetAppIntegerValue(CFSTR("force2DDepth"), CFSTR("org.vuo.Editor"), &overridden);
1644 
1645  if (!overridden)
1646  {
1647  // https://b33p.net/kosada/node/13485
1648  // https://b33p.net/kosada/node/13896
1649  // On some NVIDIA GPUs, the texture targets for the color and depth buffers need to match
1650  // (otherwise it sporadically crashes, or if a window is added via livecoding, it renders the scene with the wrong colors).
1651  // On AMD 7970 and AMD M370X, the depth texture target needs to always be GL_TEXTURE_2D
1652  // (otherwise it throws GL_INVALID_OPERATION).
1653  const char *renderer = (const char *)glGetString(GL_RENDERER);
1654  if (strcmp(renderer, "NVIDIA GeForce GT 650M OpenGL Engine") == 0
1655  || strcmp(renderer, "NVIDIA GeForce 9400M OpenGL Engine") == 0)
1656  force2DDepth = false;
1657  else
1658  force2DDepth = true;
1659  }
1660 
1661  VDebugLog("force2DDepth = %d", force2DDepth);
1662  });
1663 
1664  unsigned char colorBytesPerPixel = VuoGlTexture_getBytesPerPixel(imageGlInternalFormat, GL_BGRA);
1665  unsigned long requiredBytes = sceneRenderer->viewportWidth * sceneRenderer->viewportHeight * colorBytesPerPixel;
1666  if (outputDepthTexture)
1667  requiredBytes += sceneRenderer->viewportWidth * sceneRenderer->viewportHeight * VuoGlTexture_getBytesPerPixel(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT);
1668 
1669  int supportedSamples = VuoGlContext_getMaximumSupportedMultisampling(glContext);
1670  multisample = (VuoMultisample)MIN(multisample, supportedSamples);
1671  bool actuallyMultisampling = multisample > 1;
1672 
1673  float fudge = 1;
1674  const char *renderer = (const char *)glGetString(GL_RENDERER);
1675  if (strcmp(renderer, "Intel HD Graphics 3000") == 0)
1676  // See https://b33p.net/kosada/node/12030
1677  fudge = 2.5;
1678 
1679  if (multisample)
1680  requiredBytes += requiredBytes * multisample * fudge;
1681 
1682  unsigned long maximumTextureBytes = VuoGlTexture_getMaximumTextureBytes(glContext);
1683  if (maximumTextureBytes > 0 && requiredBytes > maximumTextureBytes)
1684  {
1685  VUserLog("Not enough graphics memory for a %dx%d (%d bytes/pixel * %d sample) render%s. Requires %lu MB, have %lu MB.",
1686  sceneRenderer->viewportWidth, sceneRenderer->viewportHeight,
1687  colorBytesPerPixel, multisample==0?1:multisample,
1688  outputDepthTexture ? " with depth buffer" : "",
1689  requiredBytes/1024/1024, maximumTextureBytes/1024/1024);
1690  return false;
1691  }
1692 
1693 
1694  // Create a new GL Texture Object and Framebuffer Object.
1695  if (!*outputTexture)
1696  *outputTexture = VuoGlTexturePool_use(glContext, VuoGlTexturePool_Allocate, GL_TEXTURE_2D, imageGlInternalFormat, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight, GL_BGRA, NULL);
1697 
1698  if (outputDepthTexture)
1699  *outputDepthTexture = VuoGlTexturePool_use(glContext, VuoGlTexturePool_Allocate, GL_TEXTURE_2D, GL_DEPTH_COMPONENT, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight, GL_DEPTH_COMPONENT, NULL);
1700 
1701  if (!*outputTexture || (outputDepthTexture && !*outputDepthTexture))
1702  return false;
1703 
1704  sceneRenderer->shouldSortByDepth = *outputDepthTexture;
1705 
1706  glBindFramebuffer(GL_FRAMEBUFFER, sceneRenderer->outputFramebuffer);
1707 
1708  // If multisampling, create high-res intermediate renderbuffers.
1709  // Otherwise, directly bind the textures to the framebuffer.
1710  if (actuallyMultisampling)
1711  {
1712  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, sceneRenderer->renderBuffer);
1713 // VLog("glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, %d, %s, %d, %d);", multisample, VuoGl_stringForConstant(imageGlInternalFormat), sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1714  glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, multisample, imageGlInternalFormat, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1715  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER_EXT, sceneRenderer->renderBuffer);
1716 
1717  if (outputDepthTexture)
1718  {
1719  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, sceneRenderer->renderDepthBuffer);
1720  glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, multisample, GL_DEPTH_COMPONENT, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1721  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER_EXT, sceneRenderer->renderDepthBuffer);
1722  }
1723  }
1724  else
1725  {
1726  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, *outputTexture, 0);
1727  if (outputDepthTexture)
1728  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, force2DDepth ? GL_TEXTURE_2D : target, *outputDepthTexture, 0);
1729  }
1730 
1731 
1732  // Render.
1733  dispatch_semaphore_wait(sceneRenderer->scenegraphSemaphore, DISPATCH_TIME_FOREVER);
1734  {
1735  if (invertDepthImage)
1736  {
1737  glClearDepth(0);
1738  glDepthRange(1, 0);
1739  glDepthFunc(GL_GREATER);
1740  }
1741  else
1742  {
1743  glClearDepth(1);
1744  glDepthRange(0, 1);
1745  glDepthFunc(GL_LESS);
1746  }
1747 
1748  glViewport(0, 0, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1749  glClearColor(0,0,0,0);
1750  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1751 
1752  VuoSceneRenderer_draw(sceneRenderer);
1753  }
1754 
1755 
1756  // If multisampling, resolve the renderbuffers into standard textures by blitting them.
1757  if (actuallyMultisampling)
1758  {
1759  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, sceneRenderer->outputFramebuffer2);
1760 
1761  {
1762  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, *outputTexture, 0);
1763  if (outputDepthTexture)
1764  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, force2DDepth ? GL_TEXTURE_2D : target, *outputDepthTexture, 0);
1765 
1766  glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, sceneRenderer->outputFramebuffer);
1767  glReadBuffer(GL_COLOR_ATTACHMENT0);
1768  glDrawBuffer(GL_COLOR_ATTACHMENT0);
1769  glBlitFramebufferEXT(0, 0, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight,
1770  0, 0, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight,
1771  GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
1772  GL_NEAREST);
1773 
1774  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, 0, 0);
1775  if (outputDepthTexture)
1776  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, force2DDepth ? GL_TEXTURE_2D : target, 0, 0);
1777  }
1778 
1779  glBindFramebuffer(GL_FRAMEBUFFER, 0);
1780  }
1781  else
1782  {
1783  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, 0, 0);
1784  if (outputDepthTexture)
1785  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, force2DDepth ? GL_TEXTURE_2D : target, 0, 0);
1786  }
1787 
1788 
1789  glBindFramebuffer(GL_FRAMEBUFFER, 0);
1790 
1791  // Make sure the render commands are actually submitted before we release the semaphore,
1792  // since the textures we're using might immediately be recycled (if the rootSceneObject is released).
1793  glFlushRenderAPPLE();
1794 
1795  dispatch_semaphore_signal(sceneRenderer->scenegraphSemaphore);
1796 
1797  return true;
1798 }
1799 
1809 void VuoSceneRenderer_renderToImage(VuoSceneRenderer sr, VuoImage *image, VuoImageColorDepth imageColorDepth, VuoMultisample multisample, VuoImage *depthImage, bool invertDepthImage)
1810 {
1811  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1812  GLuint imageGlInternalFormat = VuoImageColorDepth_getGlInternalFormat(GL_BGRA, imageColorDepth);
1813  __block GLuint texture = 0;
1814  __block GLuint depthTexture = 0;
1815  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1816  if (!VuoSceneRenderer_renderInternal(sr, cgl_ctx, &texture, GL_TEXTURE_2D, imageGlInternalFormat, multisample, depthImage ? &depthTexture : NULL, invertDepthImage))
1817  {
1818  *image = NULL;
1819  if (depthImage)
1820  *depthImage = NULL;
1821  }
1822  });
1823 
1824  *image = VuoImage_make(texture, imageGlInternalFormat, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1825  if (depthImage)
1826  *depthImage = VuoImage_make(depthTexture, GL_DEPTH_COMPONENT, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1827 }
1828 
1839 {
1840  VuoSceneRendererInternal *sceneRenderer = (VuoSceneRendererInternal *)sr;
1841  GLuint imageGlInternalFormat = VuoImageColorDepth_getGlInternalFormat(GL_BGRA, imageColorDepth);
1842  __block GLuint texture = 0;
1843  __block GLuint depthTexture = 0;
1844  __block bool renderSucceeded;
1845  __block VuoIoSurface vis;
1846  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
1847  vis = VuoIoSurfacePool_use(cgl_ctx, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight, &texture);
1848  renderSucceeded = VuoSceneRenderer_renderInternal(sr, cgl_ctx, &texture, GL_TEXTURE_RECTANGLE_ARB, imageGlInternalFormat, multisample, includeDepthBuffer ? &depthTexture : NULL, false);
1849  });
1850  if (!renderSucceeded)
1851  {
1852  VuoIoSurfacePool_disuse(vis, false); // It was never used, so no need to quarantine it.
1853  return NULL;
1854  }
1855 
1856  if (includeDepthBuffer)
1857  {
1858  VuoImage depthImage = VuoImage_make(depthTexture, GL_DEPTH_COMPONENT, sceneRenderer->viewportWidth, sceneRenderer->viewportHeight);
1859  VuoRetain(depthImage);
1860  VuoRelease(depthImage);
1861  }
1862 
1863  return vis;
1864 }