/* ============= RB_ExportCubemaps ============= */ const void *RB_ExportCubemaps(const void *data) { const exportCubemapsCommand_t *cmd = data; // finish any 2D drawing if needed if (tess.numIndexes) RB_EndSurface(); if (!glRefConfig.framebufferObject || !tr.world || tr.numCubemaps == 0) { // do nothing ri.Printf(PRINT_ALL, "Nothing to export!\n"); return (const void *)(cmd + 1); } if (cmd) { FBO_t *oldFbo = glState.currentFBO; int sideSize = r_cubemapSize->integer * r_cubemapSize->integer * 4; byte *cubemapPixels = ri.Malloc(sideSize * 6); int i, j; FBO_Bind(tr.renderCubeFbo); for (i = 0; i < tr.numCubemaps; i++) { char filename[MAX_QPATH]; cubemap_t *cubemap = &tr.cubemaps[i]; byte *p = cubemapPixels; for (j = 0; j < 6; j++) { FBO_AttachImage(tr.renderCubeFbo, cubemap->image, GL_COLOR_ATTACHMENT0_EXT, j); qglReadPixels(0, 0, r_cubemapSize->integer, r_cubemapSize->integer, GL_RGBA, GL_UNSIGNED_BYTE, p); p += sideSize; } if (cubemap->name[0]) { COM_StripExtension(cubemap->name, filename, MAX_QPATH); Q_strcat(filename, MAX_QPATH, ".dds"); } else { Com_sprintf(filename, MAX_QPATH, "cubemaps/%s/%03d.dds", tr.world->baseName, i); } R_SaveDDS(filename, cubemapPixels, r_cubemapSize->integer, r_cubemapSize->integer, 6); ri.Printf(PRINT_ALL, "Saved cubemap %d as %s\n", i, filename); } FBO_Bind(oldFbo); ri.Free(cubemapPixels); } return (const void *)(cmd + 1); }
/* ============ FBO_Init ============ */ void FBO_Init(void) { int i; int hdrFormat, multisample = 0; ri.Printf(PRINT_ALL, "------- FBO_Init -------\n"); if(!glRefConfig.framebufferObject) return; tr.numFBOs = 0; GL_CheckErrors(); R_IssuePendingRenderCommands(); hdrFormat = GL_RGBA8; if (r_hdr->integer && glRefConfig.textureFloat) hdrFormat = GL_RGBA16F_ARB; if (glRefConfig.framebufferMultisample) qglGetIntegerv(GL_MAX_SAMPLES, &multisample); if (r_ext_framebuffer_multisample->integer < multisample) multisample = r_ext_framebuffer_multisample->integer; if (multisample < 2 || !glRefConfig.framebufferBlit) multisample = 0; if (multisample != r_ext_framebuffer_multisample->integer) ri.Cvar_SetValue("r_ext_framebuffer_multisample", (float)multisample); // only create a render FBO if we need to resolve MSAA or do HDR // otherwise just render straight to the screen (tr.renderFbo = NULL) if (multisample && glRefConfig.framebufferMultisample) { tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height); FBO_CreateBuffer(tr.renderFbo, hdrFormat, 0, multisample); FBO_CreateBuffer(tr.renderFbo, GL_DEPTH_COMPONENT24, 0, multisample); R_CheckFBO(tr.renderFbo); tr.msaaResolveFbo = FBO_Create("_msaaResolve", tr.renderDepthImage->width, tr.renderDepthImage->height); FBO_AttachImage(tr.msaaResolveFbo, tr.renderImage, GL_COLOR_ATTACHMENT0, 0); FBO_AttachImage(tr.msaaResolveFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT, 0); R_CheckFBO(tr.msaaResolveFbo); } else if (r_hdr->integer) { tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height); FBO_AttachImage(tr.renderFbo, tr.renderImage, GL_COLOR_ATTACHMENT0, 0); FBO_AttachImage(tr.renderFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT, 0); R_CheckFBO(tr.renderFbo); } // clear render buffer // this fixes the corrupt screen bug with r_hdr 1 on older hardware if (tr.renderFbo) { GL_BindFramebuffer(GL_FRAMEBUFFER, tr.renderFbo->frameBuffer); qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); } if (tr.screenScratchImage) { tr.screenScratchFbo = FBO_Create("screenScratch", tr.screenScratchImage->width, tr.screenScratchImage->height); FBO_AttachImage(tr.screenScratchFbo, tr.screenScratchImage, GL_COLOR_ATTACHMENT0, 0); FBO_AttachImage(tr.screenScratchFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT, 0); R_CheckFBO(tr.screenScratchFbo); } if (tr.sunRaysImage) { tr.sunRaysFbo = FBO_Create("_sunRays", tr.renderDepthImage->width, tr.renderDepthImage->height); FBO_AttachImage(tr.sunRaysFbo, tr.sunRaysImage, GL_COLOR_ATTACHMENT0, 0); FBO_AttachImage(tr.sunRaysFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT, 0); R_CheckFBO(tr.sunRaysFbo); } if (MAX_DRAWN_PSHADOWS && tr.pshadowMaps[0]) { for( i = 0; i < MAX_DRAWN_PSHADOWS; i++) { tr.pshadowFbos[i] = FBO_Create(va("_shadowmap%d", i), tr.pshadowMaps[i]->width, tr.pshadowMaps[i]->height); // FIXME: this next line wastes 16mb with 16x512x512 sun shadow maps, skip if OpenGL 4.3+ or ARB_framebuffer_no_attachments FBO_CreateBuffer(tr.pshadowFbos[i], GL_RGBA8, 0, 0); FBO_AttachImage(tr.pshadowFbos[i], tr.pshadowMaps[i], GL_DEPTH_ATTACHMENT, 0); R_CheckFBO(tr.pshadowFbos[i]); } } if (tr.sunShadowDepthImage[0]) { for (i = 0; i < 4; i++) { tr.sunShadowFbo[i] = FBO_Create("_sunshadowmap", tr.sunShadowDepthImage[i]->width, tr.sunShadowDepthImage[i]->height); // FIXME: this next line wastes 16mb with 4x1024x1024 sun shadow maps, skip if OpenGL 4.3+ or ARB_framebuffer_no_attachments // This at least gets sun shadows working on older GPUs (Intel) FBO_CreateBuffer(tr.sunShadowFbo[i], GL_RGBA8, 0, 0); FBO_AttachImage(tr.sunShadowFbo[i], tr.sunShadowDepthImage[i], GL_DEPTH_ATTACHMENT, 0); R_CheckFBO(tr.sunShadowFbo[i]); } } if (tr.screenShadowImage) { tr.screenShadowFbo = FBO_Create("_screenshadow", tr.screenShadowImage->width, tr.screenShadowImage->height); FBO_AttachImage(tr.screenShadowFbo, tr.screenShadowImage, GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.screenShadowFbo); } if (tr.textureScratchImage[0]) { for (i = 0; i < 2; i++) { tr.textureScratchFbo[i] = FBO_Create(va("_texturescratch%d", i), tr.textureScratchImage[i]->width, tr.textureScratchImage[i]->height); FBO_AttachImage(tr.textureScratchFbo[i], tr.textureScratchImage[i], GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.textureScratchFbo[i]); } } if (tr.calcLevelsImage) { tr.calcLevelsFbo = FBO_Create("_calclevels", tr.calcLevelsImage->width, tr.calcLevelsImage->height); FBO_AttachImage(tr.calcLevelsFbo, tr.calcLevelsImage, GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.calcLevelsFbo); } if (tr.targetLevelsImage) { tr.targetLevelsFbo = FBO_Create("_targetlevels", tr.targetLevelsImage->width, tr.targetLevelsImage->height); FBO_AttachImage(tr.targetLevelsFbo, tr.targetLevelsImage, GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.targetLevelsFbo); } if (tr.quarterImage[0]) { for (i = 0; i < 2; i++) { tr.quarterFbo[i] = FBO_Create(va("_quarter%d", i), tr.quarterImage[i]->width, tr.quarterImage[i]->height); FBO_AttachImage(tr.quarterFbo[i], tr.quarterImage[i], GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.quarterFbo[i]); } } if (tr.hdrDepthImage) { tr.hdrDepthFbo = FBO_Create("_hdrDepth", tr.hdrDepthImage->width, tr.hdrDepthImage->height); FBO_AttachImage(tr.hdrDepthFbo, tr.hdrDepthImage, GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.hdrDepthFbo); } if (tr.screenSsaoImage) { tr.screenSsaoFbo = FBO_Create("_screenssao", tr.screenSsaoImage->width, tr.screenSsaoImage->height); FBO_AttachImage(tr.screenSsaoFbo, tr.screenSsaoImage, GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.screenSsaoFbo); } if (tr.renderCubeImage) { tr.renderCubeFbo = FBO_Create("_renderCubeFbo", tr.renderCubeImage->width, tr.renderCubeImage->height); FBO_AttachImage(tr.renderCubeFbo, tr.renderCubeImage, GL_COLOR_ATTACHMENT0, 0); FBO_CreateBuffer(tr.renderCubeFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); R_CheckFBO(tr.renderCubeFbo); } GL_CheckErrors(); GL_BindFramebuffer(GL_FRAMEBUFFER, 0); glState.currentFBO = NULL; }
/* ================= RB_BeginDrawingView Any mirrored or portaled views have already been drawn, so prepare to actually render the visible surfaces for this view ================= */ void RB_BeginDrawingView (void) { int clearBits = 0; // sync with gl if needed if ( r_finish->integer == 1 && !glState.finishCalled ) { qglFinish (); glState.finishCalled = qtrue; } if ( r_finish->integer == 0 ) { glState.finishCalled = qtrue; } // we will need to change the projection matrix before drawing // 2D images again backEnd.projection2D = qfalse; if (glRefConfig.framebufferObject) { FBO_t *fbo = backEnd.viewParms.targetFbo; // FIXME: HUGE HACK: render to the screen fbo if we've already postprocessed the frame and aren't drawing more world // drawing more world check is in case of double renders, such as skyportals if (fbo == NULL && !(backEnd.framePostProcessed && (backEnd.refdef.rdflags & RDF_NOWORLDMODEL))) fbo = tr.renderFbo; if (tr.renderCubeFbo && fbo == tr.renderCubeFbo) { cubemap_t *cubemap = &tr.cubemaps[backEnd.viewParms.targetFboCubemapIndex]; FBO_AttachImage(fbo, cubemap->image, GL_COLOR_ATTACHMENT0_EXT, backEnd.viewParms.targetFboLayer); } FBO_Bind(fbo); } // // set the modelview matrix for the viewer // SetViewportAndScissor(); // ensures that depth writes are enabled for the depth clear GL_State( GLS_DEFAULT ); // clear relevant buffers clearBits = GL_DEPTH_BUFFER_BIT; if ( r_measureOverdraw->integer || r_shadows->integer == 2 ) { clearBits |= GL_STENCIL_BUFFER_BIT; } if ( r_fastsky->integer && !( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) { clearBits |= GL_COLOR_BUFFER_BIT; // FIXME: only if sky shaders have been used } // clear to black for cube maps if (tr.renderCubeFbo && backEnd.viewParms.targetFbo == tr.renderCubeFbo) { clearBits |= GL_COLOR_BUFFER_BIT; } qglClear( clearBits ); if ( ( backEnd.refdef.rdflags & RDF_HYPERSPACE ) ) { RB_Hyperspace(); return; } else { backEnd.isHyperspace = qfalse; } // we will only draw a sun if there was sky rendered in this view backEnd.skyRenderedThisView = qfalse; // clip to the plane of the portal if ( backEnd.viewParms.isPortal ) { #if 0 float plane[4]; GLdouble plane2[4]; plane[0] = backEnd.viewParms.portalPlane.normal[0]; plane[1] = backEnd.viewParms.portalPlane.normal[1]; plane[2] = backEnd.viewParms.portalPlane.normal[2]; plane[3] = backEnd.viewParms.portalPlane.dist; plane2[0] = DotProduct (backEnd.viewParms.or.axis[0], plane); plane2[1] = DotProduct (backEnd.viewParms.or.axis[1], plane); plane2[2] = DotProduct (backEnd.viewParms.or.axis[2], plane); plane2[3] = DotProduct (plane, backEnd.viewParms.or.origin) - plane[3]; #endif GL_SetModelviewMatrix( s_flipMatrix ); } }