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