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