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