/* =============== R_BeginFrame =============== */ void R_BeginFrame( qboolean clearScene ) { glConfig.softwareGammaUpdate = false; // in case of possible fails if(( gl_clear->integer || gl_overview->integer ) && clearScene && cls.state != ca_cinematic ) { pglClear( GL_COLOR_BUFFER_BIT ); } // update gamma if( vid_gamma->modified ) { glConfig.softwareGammaUpdate = true; BuildGammaTable( vid_gamma->value, vid_texgamma->value ); GL_RebuildLightmaps(); glConfig.softwareGammaUpdate = false; vid_gamma->modified = false; } R_Set2DMode( true ); // draw buffer stuff pglDrawBuffer( GL_BACK ); // texturemode stuff // update texture parameters if( gl_texturemode->modified || gl_texture_anisotropy->modified || gl_texture_lodbias ->modified ) R_SetTextureParameters(); // swapinterval stuff GL_UpdateSwapInterval(); CL_ExtraUpdate (); }
/* ================== V_PostRender ================== */ void V_PostRender( void ) { R_Set2DMode( true ); if( cls.state == ca_active ) { CL_DrawHUD( CL_ACTIVE ); #ifndef NO_VGUI VGui_Paint(); #endif } if( cls.scrshot_action == scrshot_inactive || cls.scrshot_action == scrshot_normal ) { SCR_RSpeeds(); SCR_NetSpeeds(); SCR_DrawFPS(); CL_DrawDemoRecording(); R_ShowTextures(); CL_DrawHUD( CL_CHANGELEVEL ); Con_DrawConsole(); UI_UpdateMenu( host.realtime ); Con_DrawDebug(); // must be last S_ExtraUpdate(); } SCR_MakeScreenShot(); R_EndFrame(); }
/* =============== R_EndFrame =============== */ void R_EndFrame( void ) { // flush any remaining 2D bits R_Set2DMode( false ); if( !pwglSwapBuffers( glw_state.hDC )) Sys_Error( "wglSwapBuffers() failed!\n" ); }
static void _processFrame( void ) { qboolean firstInverted; static qboolean phase = false; if ( phase != _isPhaseInverted( ) && ( strobe.strobeInterval == 1 || strobe.strobeInterval == -1 ) && !strobe.cdTriggered ) firstInverted = true; else firstInverted = false; if ( strobe.cdTriggered ) { strobe.frameState = FRAME_RENDER | ( strobe.frameState & PHASE_POSITIVE ); } if ( _isNormal( ) ) // Show normal { if ( _isPositive( ) ) ++strobe.pNCounter; else ++strobe.nNCounter; if ( !firstInverted ) R_Set2DMode( false ); } else // Show black { if ( _isPositive( ) ) ++strobe.pBCounter; else ++strobe.nBCounter; if ( !firstInverted ) _makeFrameBlack( 1.0f ); } if ( firstInverted ) { R_Set2DMode( false ); _makeFrameBlack( 0.25f ); } phase = _isPhaseInverted( ); ++strobe.fCounter; }
/* =============== R_EndFrame =============== */ void R_EndFrame( void ) { // flush any remaining 2D bits R_Set2DMode( false ); #ifdef XASH_SDL SDL_GL_SwapWindow(host.hWnd); #elif defined __ANDROID__ // For direct android backend Android_SwapBuffers(); #endif }
/* =============== R_EndFrame =============== */ void R_EndFrame( void ) { if( r_strobe->integer ) R_Strobe_Tick(); else R_Set2DMode( false ); #ifdef XASH_SDL SDL_GL_SwapWindow( host.hWnd ); #elif defined __ANDROID__ // For direct android backend Android_SwapBuffers(); #endif }
/* ================== V_PostRender ================== */ void V_PostRender( void ) { qboolean draw_2d = false; R_Set2DMode( true ); if( cls.state == ca_active ) { SCR_TileClear(); CL_DrawHUD( CL_ACTIVE ); #ifdef XASH_VGUI VGui_Paint(); #endif } switch( cls.scrshot_action ) { case scrshot_inactive: case scrshot_normal: case scrshot_snapshot: draw_2d = true; break; } if( draw_2d ) { SCR_RSpeeds(); SCR_NetSpeeds(); SCR_DrawFPS(); SV_DrawOrthoTriangles(); CL_DrawDemoRecording(); R_ShowTextures(); CL_DrawHUD( CL_CHANGELEVEL ); Con_DrawConsole(); UI_UpdateMenu( host.realtime ); Con_DrawVersion(); Con_DrawDebug(); // must be last S_ExtraUpdate(); } SCR_MakeScreenShot(); R_EndFrame(); }
/* =============== V_CalcRefDef sets cl.refdef view values =============== */ void V_CalcRefDef( void ) { R_Set2DMode( false ); tr.framecount++; // g-cont. keep actual frame for all viewpasses do { clgame.dllFuncs.pfnCalcRefdef( &cl.refdef ); V_MergeOverviewRefdef( &cl.refdef ); R_RenderFrame( &cl.refdef, true ); cl.refdef.onlyClientDraw = false; } while( cl.refdef.nextView ); // Xash3D extension. draw debug triangles on a server SV_DrawDebugTriangles (); SCR_AddDirtyPoint( cl.refdef.viewport[0], cl.refdef.viewport[1] ); SCR_AddDirtyPoint( cl.refdef.viewport[0] + cl.refdef.viewport[2] - 1, cl.refdef.viewport[1] + cl.refdef.viewport[3] - 1 ); }
/* ============== GL_BackendEndFrame ============== */ void GL_BackendEndFrame( void ) { // go into 2D mode (in case we draw PlayerSetup between two 2d calls) if( !RI.drawWorld ) R_Set2DMode( true ); if( r_speeds->integer <= 0 || !RI.drawWorld ) return; switch( r_speeds->integer ) { case 1: Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i wpoly, %3i bpoly\n%3i epoly, %3i spoly", r_stats.c_world_polys, r_stats.c_brush_polys, r_stats.c_studio_polys, r_stats.c_sprite_polys ); break; case 2: Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "visible leafs:\n%3i leafs\ncurrent leaf %3li", r_stats.c_world_leafs, r_viewleaf - cl.worldmodel->leafs ); break; case 3: Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i studio models drawn\n%3i sprites drawn", r_stats.c_studio_models_drawn, r_stats.c_sprite_models_drawn ); break; case 4: Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i static entities\n%3i normal entities", r_numStatics, r_numEntities - r_numStatics ); break; case 5: Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i tempents\n%3i viewbeams\n%3i particles", r_stats.c_active_tents_count, r_stats.c_view_beams_count, r_stats.c_particle_count ); break; case 6: Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i mirrors\n", r_stats.c_mirror_passes ); break; } Q_memset( &r_stats, 0, sizeof( r_stats )); }
void R_Strobe_Tick( void ) { double delta, delta2; double currentTime = Sys_DoubleTime( ); static int frameSnapshot = 0; static double recentTime2 = 0.0; static double recentTime = 0.0; static double deltaArray[DEVIATION_SIZE] = {0.0}; if ( CL_IsInMenu( ) || ( !vid_fullscreen->integer && CL_IsInConsole( ) ) ) // Disable when not in fullscreen due to viewport problems { R_Set2DMode( false ); return; } delta2 = currentTime - recentTime2; recentTime2 = currentTime; strobe.elapsedTime = currentTime - strobe.initialTime; /* if (deltaArray[0] == -1.0) { for ( int k = 0; k < ARRAYSIZE( deltaArray ); ++k ) { deltaArray[k] = _currentFPS( ); } } */ if ( strobe.cdTimer >= 0.0 && delta2 > 0.0 ) strobe.cdTimer += delta2; if ( strobe.fCounter - frameSnapshot == 1 ) { deltaArray[strobe.fCounter % ARRAYSIZE( deltaArray )] = _currentFPS( ); strobe.deviation = _standardDeviation( deltaArray, ARRAYSIZE( deltaArray ) ); } frameSnapshot = strobe.fCounter; if ( r_strobe_cooldown->integer > 0 ) { if ( ( strobe.cdTimer > (double)abs( r_strobe_cooldown->integer ) ) && strobe.cdTriggered == true ) { strobe.cdTriggered = false; strobe.cdTimer = -1.0; } if ( strobe.fCounter > ARRAYSIZE( deltaArray ) ) { if ( strobe.deviation > DEVIATION_LIMIT ) { strobe.cdTriggered = true; strobe.cdTimer = 0.0; } } } else { strobe.cdTriggered = false; } if ( ( ( strobe.strobeInterval != r_strobe->integer ) && ( strobe.strobeInterval ) ) || /*((swapInterval != r_strobe_swapinterval->integer) && (swapInterval != 0)) || */ strobe.fCounter == INT_MAX ) { _reset( ); R_Strobe_Tick( ); } strobe.strobeInterval = r_strobe->integer; strobe.swapInterval = r_strobe_swapinterval->integer; if ( ( strobe.strobeInterval == 0 ) || ( ( gl_swapInterval->integer == 0 ) && ( strobe.strobeInterval ) ) || ( strobe.strobeInterval > 25 || strobe.strobeInterval < -25 ) ) { if ( !gl_swapInterval->integer ) MsgDev( D_WARN, "Strobing requires V-SYNC enabled! (gl_swapInterval != 0) \n" ); if ( strobe.strobeInterval ) { Cvar_Set( "r_strobe", "0" ); } R_Set2DMode( false ); return; } if ( ( strobe.fCounter % 2 ) == 0 ) { ++strobe.pCounter; strobe.frameState |= PHASE_POSITIVE; } else { ++strobe.nCounter; strobe.frameState &= ~PHASE_POSITIVE; } if ( strobe.swapInterval < 0 ) strobe.swapInterval = abs( strobe.swapInterval ); if ( ( strobe.swapInterval ) && ( strobe.strobeInterval % 2 ) ) // Swapping not enabled for even intervals as it is neither necessary nor works as intended { delta = currentTime - recentTime; if ( ( delta >= (double)( strobe.swapInterval ) ) && ( delta < (double)( 2 * strobe.swapInterval ) ) ) // Basic timer { strobe.frameState |= PHASE_INVERTED; } else if ( delta < (double)( strobe.swapInterval ) ) { strobe.frameState &= ~PHASE_INVERTED; } else //if (delta >= (double)(2 * swapInterval)) { recentTime = currentTime; } } switch ( strobe.frameState & ( PHASE_POSITIVE | PHASE_INVERTED ) ) { case ( PHASE_POSITIVE | PHASE_INVERTED ): if ( ( abs( strobe.strobeInterval ) % 2 ) == 0 ) strobe.frameState = ( ( ( strobe.pCounter - 1 ) % ( abs( strobe.strobeInterval ) + 1 ) ) == ( abs( strobe.strobeInterval ) / 2 ) ) ? strobe.frameState | FRAME_RENDER : strobe.frameState & ~FRAME_RENDER; //even else strobe.frameState &= ~FRAME_RENDER; break; case ( PHASE_POSITIVE & ~PHASE_INVERTED ): if ( abs( strobe.strobeInterval ) % 2 == 0 ) strobe.frameState = ( ( ( strobe.pCounter - 1 ) % ( abs( strobe.strobeInterval ) + 1 ) ) == 0 ) ? strobe.frameState | FRAME_RENDER : strobe.frameState & ~FRAME_RENDER; //even else { if ( abs( strobe.strobeInterval ) == 1 ) strobe.frameState |= FRAME_RENDER; else strobe.frameState = ( ( ( strobe.pCounter - 1 ) % ( ( abs( strobe.strobeInterval ) + 1 ) / 2 ) ) == 0 ) ? strobe.frameState | FRAME_RENDER : strobe.frameState & ~FRAME_RENDER; //odd } break; case ( ~PHASE_POSITIVE & PHASE_INVERTED ): if ( abs( strobe.strobeInterval ) % 2 == 0 ) strobe.frameState = ( ( ( strobe.nCounter - 1 ) % ( abs( strobe.strobeInterval ) + 1 ) ) == 0 ) ? strobe.frameState | FRAME_RENDER : strobe.frameState & ~FRAME_RENDER; //even else { if ( abs( strobe.strobeInterval ) == 1 ) strobe.frameState |= FRAME_RENDER; else strobe.frameState = ( ( ( strobe.nCounter - 1 ) % ( ( abs( strobe.strobeInterval ) + 1 ) / 2 ) ) == 0 ) ? strobe.frameState | FRAME_RENDER : strobe.frameState & ~FRAME_RENDER; //odd } break; case 0: if ( ( abs( strobe.strobeInterval ) % 2 ) == 0 ) strobe.frameState = ( ( ( strobe.nCounter - 1 ) % ( abs( strobe.strobeInterval ) + 1 ) ) == ( abs( strobe.strobeInterval ) / 2 ) ) ? strobe.frameState | FRAME_RENDER : strobe.frameState & ~FRAME_RENDER; //even else strobe.frameState &= ~FRAME_RENDER; break; default: strobe.frameState = ( PHASE_POSITIVE | FRAME_RENDER ); } if ( strobe.strobeInterval < 0 ) strobe.frameState ^= FRAME_RENDER; _processFrame( ); }
/* ================= VID_CubemapShot ================= */ qboolean VID_CubemapShot( const char *base, uint size, const float *vieworg, qboolean skyshot ) { rgbdata_t *r_shot, *r_side; byte *temp = NULL; byte *buffer = NULL; string basename; int i = 1, flags, result; if( !RI.drawWorld || !cl.worldmodel ) return false; // make sure the specified size is valid while( i < size ) i<<=1; if( i != size ) return false; if( size > glState.width || size > glState.height ) return false; // setup refdef RI.params |= RP_ENVVIEW; // do not render non-bmodel entities // alloc space temp = Mem_Alloc( r_temppool, size * size * 3 ); buffer = Mem_Alloc( r_temppool, size * size * 3 * 6 ); r_shot = Mem_Alloc( r_temppool, sizeof( rgbdata_t )); r_side = Mem_Alloc( r_temppool, sizeof( rgbdata_t )); // use client vieworg if( !vieworg ) vieworg = cl.refdef.vieworg; for( i = 0; i < 6; i++ ) { // go into 3d mode R_Set2DMode( false ); if( skyshot ) { R_DrawCubemapView( vieworg, r_skyBoxInfo[i].angles, size ); flags = r_skyBoxInfo[i].flags; } else { R_DrawCubemapView( vieworg, r_envMapInfo[i].angles, size ); flags = r_envMapInfo[i].flags; } pglReadPixels( 0, 0, size, size, GL_RGB, GL_UNSIGNED_BYTE, temp ); r_side->flags = IMAGE_HAS_COLOR; r_side->width = r_side->height = size; r_side->type = PF_RGB_24; r_side->size = r_side->width * r_side->height * 3; r_side->buffer = temp; if( flags ) Image_Process( &r_side, 0, 0, 0.0f, flags, NULL ); Q_memcpy( buffer + (size * size * 3 * i), r_side->buffer, size * size * 3 ); } RI.params &= ~RP_ENVVIEW; r_shot->flags = IMAGE_HAS_COLOR; r_shot->flags |= (skyshot) ? IMAGE_SKYBOX : IMAGE_CUBEMAP; r_shot->width = size; r_shot->height = size; r_shot->type = PF_RGB_24; r_shot->size = r_shot->width * r_shot->height * 3 * 6; r_shot->palette = NULL; r_shot->buffer = buffer; // make sure what we have right extension Q_strncpy( basename, base, MAX_STRING ); FS_StripExtension( basename ); FS_DefaultExtension( basename, ".tga" ); // write image as 6 sides result = FS_SaveImage( basename, r_shot ); FS_FreeImage( r_shot ); FS_FreeImage( r_side ); return result; }
/* ============== GL_BackendStartFrame ============== */ void GL_BackendStartFrame( void ) { r_speeds_msg[0] = '\0'; if( !RI.drawWorld ) R_Set2DMode( false ); }
/* * R_RenderScene */ void R_RenderScene( const refdef_t *fd ) { int fbFlags = 0; int ppFrontBuffer = 0; image_t *ppSource; if( r_norefresh->integer ) return; R_Set2DMode( false ); RB_SetTime( fd->time ); if( !( fd->rdflags & RDF_NOWORLDMODEL ) ) rsc.refdef = *fd; rn.refdef = *fd; if( !rn.refdef.minLight ) { rn.refdef.minLight = 0.1f; } fd = &rn.refdef; rn.renderFlags = RF_NONE; rn.farClip = R_DefaultFarClip(); rn.clipFlags = 15; if( rsh.worldModel && !( fd->rdflags & RDF_NOWORLDMODEL ) && rsh.worldBrushModel->globalfog ) rn.clipFlags |= 16; rn.meshlist = &r_worldlist; rn.portalmasklist = &r_portalmasklist; rn.shadowBits = 0; rn.dlightBits = 0; rn.shadowGroup = NULL; fbFlags = 0; rn.fbColorAttachment = rn.fbDepthAttachment = NULL; if( !( fd->rdflags & RDF_NOWORLDMODEL ) ) { if( r_soft_particles->integer && ( rsh.screenTexture != NULL ) ) { rn.fbColorAttachment = rsh.screenTexture; rn.fbDepthAttachment = rsh.screenDepthTexture; rn.renderFlags |= RF_SOFT_PARTICLES; fbFlags |= 1; } if( rsh.screenPPCopies[0] && rsh.screenPPCopies[1] ) { int oldFlags = fbFlags; shader_t *cc = rn.refdef.colorCorrection; if( r_fxaa->integer ) { fbFlags |= 2; } if( cc && cc->numpasses > 0 && cc->passes[0].images[0] && cc->passes[0].images[0] != rsh.noTexture ) { fbFlags |= 4; } if( fbFlags != oldFlags ) { if( !rn.fbColorAttachment ) { rn.fbColorAttachment = rsh.screenPPCopies[0]; ppFrontBuffer = 1; } } } } ppSource = rn.fbColorAttachment; // clip new scissor region to the one currently set Vector4Set( rn.scissor, fd->scissor_x, fd->scissor_y, fd->scissor_width, fd->scissor_height ); Vector4Set( rn.viewport, fd->x, fd->y, fd->width, fd->height ); VectorCopy( fd->vieworg, rn.pvsOrigin ); VectorCopy( fd->vieworg, rn.lodOrigin ); R_BindFrameBufferObject( 0 ); R_BuildShadowGroups(); R_RenderView( fd ); R_RenderDebugSurface( fd ); R_RenderDebugBounds(); R_BindFrameBufferObject( 0 ); R_Set2DMode( true ); if( !( fd->rdflags & RDF_NOWORLDMODEL ) ) { ri.Mutex_Lock( rf.speedsMsgLock ); R_WriteSpeedsMessage( rf.speedsMsg, sizeof( rf.speedsMsg ) ); ri.Mutex_Unlock( rf.speedsMsgLock ); } // blit and blend framebuffers in proper order if( fbFlags == 1 ) { // only blit soft particles directly when we don't have any other post processing // otherwise use the soft particles FBO as the base texture on the next layer // to avoid wasting time on resolves and the fragment shader to blit to a temp texture R_BlitTextureToScrFbo( fd, ppSource, 0, GLSL_PROGRAM_TYPE_NONE, colorWhite, 0, 0, NULL ); } fbFlags &= ~1; // apply FXAA if( fbFlags & 2 ) { image_t *dest; fbFlags &= ~2; dest = fbFlags ? rsh.screenPPCopies[ppFrontBuffer] : NULL; R_BlitTextureToScrFbo( fd, ppSource, dest ? dest->fbo : 0, GLSL_PROGRAM_TYPE_FXAA, colorWhite, 0, 0, NULL ); ppFrontBuffer ^= 1; ppSource = dest; } // apply color correction if( fbFlags & 4 ) { image_t *dest; fbFlags &= ~4; dest = fbFlags ? rsh.screenPPCopies[ppFrontBuffer] : NULL; R_BlitTextureToScrFbo( fd, ppSource, dest ? dest->fbo : 0, GLSL_PROGRAM_TYPE_COLORCORRECTION, colorWhite, 0, 1, &( rn.refdef.colorCorrection->passes[0].images[0] ) ); } }
/* * R_ResampleCinematicFrame */ static image_t *R_ResampleCinematicFrame( r_cinhandle_t *handle ) { const int samples = 4; if( !handle->pic ) { // we haven't yet read a new frame, return whatever image we got // this will return NULL until at least one frame has been read return handle->image; } if( handle->yuv ) { int i; if( !handle->yuv_images[0] ) { qbyte *fake_data[1] = { NULL }; const char *letters[3] = { "y", "u", "v" }; for( i = 0; i < 3; i++ ) { handle->yuv_images[i] = R_LoadImage( va( "%s_%s", handle->name, letters[i] ), fake_data, 1, 1, IT_CINEMATIC|IT_LUMINANCE, 1 ); } handle->new_frame = qtrue; } if( handle->new_frame ) { int fbo; qboolean in2D; // render/convert three 8-bit YUV images into RGB framebuffer in2D = rf.in2D; fbo = RFB_BoundObject(); if( !in2D ) { R_PushRefInst(); } R_InitViewportTexture( &handle->image, handle->name, 0, handle->cyuv->image_width, handle->cyuv->image_height, 0, IT_CINEMATIC|IT_FRAMEBUFFER, samples ); R_BindFrameBufferObject( handle->image->fbo ); R_Set2DMode( qtrue ); RB_Scissor( 0, 0, handle->image->upload_width, handle->image->upload_height ); RB_Viewport( 0, 0, handle->image->upload_width, handle->image->upload_height ); // flip the image vertically because we're rendering to a FBO R_DrawStretchRawYUVBuiltin( 0, 0, handle->image->upload_width, handle->image->upload_height, (float)handle->cyuv->x_offset / handle->cyuv->image_width, (float)handle->cyuv->y_offset / handle->cyuv->image_height, (float)(handle->cyuv->x_offset + handle->cyuv->width) / handle->cyuv->image_width, (float)(handle->cyuv->y_offset + handle->cyuv->height) / handle->cyuv->image_height, handle->cyuv->yuv, handle->yuv_images, 2 ); if( !in2D ) { R_PopRefInst( 0 ); } R_BindFrameBufferObject( fbo ); R_Set2DMode( in2D ); handle->new_frame = qfalse; } } else { if( !handle->image ) { handle->image = R_LoadImage( handle->name, &handle->pic, handle->width, handle->height, IT_CINEMATIC, samples ); handle->new_frame = qfalse; } else if( handle->new_frame ) { R_ReplaceImage( handle->image, &handle->pic, handle->width, handle->height, handle->image->flags, samples ); handle->new_frame = qfalse; } } return handle->image; }
/* * R_RenderScene */ void R_RenderScene( const refdef_t *fd ) { int fbFlags = 0; if( r_norefresh->integer ) return; R_Set2DMode( qfalse ); RB_SetTime( fd->time ); if( !( fd->rdflags & RDF_NOWORLDMODEL ) ) rsc.refdef = *fd; rn.refdef = *fd; if( !rn.refdef.minLight ) { rn.refdef.minLight = 0.1f; } if( !rsh.screenWeaponTexture || rn.refdef.weaponAlpha == 1 ) { rn.refdef.rdflags &= ~RDF_WEAPONALPHA; } fd = &rn.refdef; rn.renderFlags = RF_NONE; rn.farClip = R_DefaultFarClip(); rn.clipFlags = 15; if( rsh.worldModel && !( fd->rdflags & RDF_NOWORLDMODEL ) && rsh.worldBrushModel->globalfog ) rn.clipFlags |= 16; rn.meshlist = &r_worldlist; rn.shadowBits = 0; rn.dlightBits = 0; rn.shadowGroup = NULL; fbFlags = 0; rn.fbColorAttachment = rn.fbDepthAttachment = NULL; if( !( fd->rdflags & RDF_NOWORLDMODEL ) ) { // soft particles require GL_EXT_framebuffer_blit as we need to copy the depth buffer // attachment into a texture we're going to read from in GLSL shader if( r_soft_particles->integer && glConfig.ext.framebuffer_blit && ( rsh.screenTexture != NULL ) ) { rn.fbColorAttachment = rsh.screenTexture; rn.fbDepthAttachment = rsh.screenDepthTexture; rn.renderFlags |= RF_SOFT_PARTICLES; fbFlags |= 1; } if( ( fd->rdflags & RDF_WEAPONALPHA ) && ( rsh.screenWeaponTexture != NULL ) ) { fbFlags |= 2; } if( r_fxaa->integer && ( rsh.screenFxaaCopy != NULL ) ) { if( !rn.fbColorAttachment ) { rn.fbColorAttachment = rsh.screenFxaaCopy; } fbFlags |= 4; } } // adjust field of view for widescreen if( glConfig.wideScreen && !( fd->rdflags & RDF_NOFOVADJUSTMENT ) ) AdjustFov( &rn.refdef.fov_x, &rn.refdef.fov_y, glConfig.width, glConfig.height, qfalse ); // clip new scissor region to the one currently set Vector4Set( rn.scissor, fd->scissor_x, fd->scissor_y, fd->scissor_width, fd->scissor_height ); Vector4Set( rn.viewport, fd->x, fd->y, fd->width, fd->height ); VectorCopy( fd->vieworg, rn.pvsOrigin ); VectorCopy( fd->vieworg, rn.lodOrigin ); if( gl_finish->integer && !( fd->rdflags & RDF_NOWORLDMODEL ) ) qglFinish(); if( fbFlags & 2 ) { // clear the framebuffer we're going to render the weapon model to // set the alpha to 0, visible parts of the model will overwrite that, // creating proper alpha mask R_BindFrameBufferObject( rsh.screenWeaponTexture->fbo ); RB_Clear( GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT, 0, 0, 0, 0 ); } R_BindFrameBufferObject( 0 ); R_BuildShadowGroups(); R_RenderView( fd ); R_RenderDebugSurface( fd ); R_RenderDebugBounds(); R_BindFrameBufferObject( 0 ); R_Set2DMode( qtrue ); // blit and blend framebuffers in proper order if( fbFlags & 1 ) { // copy to FXAA or default framebuffer R_BlitTextureToScrFbo( fd, rn.fbColorAttachment, fbFlags & 4 ? rsh.screenFxaaCopy->fbo : 0, GLSL_PROGRAM_TYPE_NONE, colorWhite, 0 ); } if( fbFlags & 2 ) { vec4_t color = { 1, 1, 1, 1 }; color[3] = fd->weaponAlpha; // blend to FXAA or default framebuffer R_BlitTextureToScrFbo( fd, rsh.screenWeaponTexture, fbFlags & 4 ? rsh.screenFxaaCopy->fbo : 0, GLSL_PROGRAM_TYPE_NONE, color, GLSTATE_SRCBLEND_SRC_ALPHA|GLSTATE_DSTBLEND_ONE_MINUS_SRC_ALPHA ); } // blit FXAA to default framebuffer if( fbFlags & 4 ) { // blend to FXAA or default framebuffer R_BlitTextureToScrFbo( fd, rsh.screenFxaaCopy, 0, GLSL_PROGRAM_TYPE_FXAA, colorWhite, 0 ); } }