summaryrefslogtreecommitdiff
path: root/src/renderergl2/tr_backend.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/renderergl2/tr_backend.cpp')
-rw-r--r--src/renderergl2/tr_backend.cpp1817
1 files changed, 1817 insertions, 0 deletions
diff --git a/src/renderergl2/tr_backend.cpp b/src/renderergl2/tr_backend.cpp
new file mode 100644
index 0000000..3459f58
--- /dev/null
+++ b/src/renderergl2/tr_backend.cpp
@@ -0,0 +1,1817 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+Tremulous is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+#include "tr_local.h"
+#include "tr_fbo.h"
+#include "tr_dsa.h"
+
+backEndData_t *backEndData;
+backEndState_t backEnd;
+
+
+static float s_flipMatrix[16] = {
+ // convert from our coordinate system (looking down X)
+ // to OpenGL's coordinate system (looking down -Z)
+ 0, 0, -1, 0,
+ -1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 0, 1
+};
+
+
+/*
+** GL_BindToTMU
+*/
+void GL_BindToTMU( image_t *image, int tmu )
+{
+ GLuint texture = (tmu == TB_COLORMAP) ? tr.defaultImage->texnum : 0;
+ GLenum target = GL_TEXTURE_2D;
+
+ if (image)
+ {
+ if (image->flags & IMGFLAG_CUBEMAP)
+ target = GL_TEXTURE_CUBE_MAP;
+
+ image->frameUsed = tr.frameCount;
+ texture = image->texnum;
+ }
+ else
+ {
+ ri.Printf(PRINT_WARNING, "GL_BindToTMU: NULL image\n");
+ }
+
+ GL_BindMultiTexture(GL_TEXTURE0_ARB + tmu, target, texture);
+}
+
+
+/*
+** GL_Cull
+*/
+void GL_Cull( int cullType ) {
+ if ( glState.faceCulling == cullType ) {
+ return;
+ }
+
+ if ( cullType == CT_TWO_SIDED )
+ {
+ qglDisable( GL_CULL_FACE );
+ }
+ else
+ {
+ bool cullFront = (cullType == CT_FRONT_SIDED);
+
+ if ( glState.faceCulling == CT_TWO_SIDED )
+ qglEnable( GL_CULL_FACE );
+
+ if ( glState.faceCullFront != cullFront )
+ qglCullFace( cullFront ? GL_FRONT : GL_BACK );
+
+ glState.faceCullFront = cullFront;
+ }
+
+ glState.faceCulling = cullType;
+}
+
+/*
+** GL_State
+**
+** This routine is responsible for setting the most commonly changed state
+** in Q3.
+*/
+void GL_State( unsigned long stateBits )
+{
+ unsigned long diff = stateBits ^ glState.glStateBits;
+
+ if ( !diff )
+ {
+ return;
+ }
+
+ //
+ // check depthFunc bits
+ //
+ if ( diff & GLS_DEPTHFUNC_BITS )
+ {
+ if ( stateBits & GLS_DEPTHFUNC_EQUAL )
+ {
+ qglDepthFunc( GL_EQUAL );
+ }
+ else if ( stateBits & GLS_DEPTHFUNC_GREATER)
+ {
+ qglDepthFunc( GL_GREATER );
+ }
+ else
+ {
+ qglDepthFunc( GL_LEQUAL );
+ }
+ }
+
+ //
+ // check blend bits
+ //
+ if ( diff & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) )
+ {
+ uint32_t oldState = glState.glStateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS );
+ uint32_t newState = stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS );
+ uint32_t storedState = glState.storedGlState & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS );
+
+ if (oldState == 0)
+ {
+ qglEnable( GL_BLEND );
+ }
+ else if (newState == 0)
+ {
+ qglDisable( GL_BLEND );
+ }
+
+ if (newState != 0 && storedState != newState)
+ {
+ GLenum srcFactor = GL_ONE, dstFactor = GL_ONE;
+
+ glState.storedGlState &= ~( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS );
+ glState.storedGlState |= newState;
+
+ switch ( stateBits & GLS_SRCBLEND_BITS )
+ {
+ case GLS_SRCBLEND_ZERO:
+ srcFactor = GL_ZERO;
+ break;
+ case GLS_SRCBLEND_ONE:
+ srcFactor = GL_ONE;
+ break;
+ case GLS_SRCBLEND_DST_COLOR:
+ srcFactor = GL_DST_COLOR;
+ break;
+ case GLS_SRCBLEND_ONE_MINUS_DST_COLOR:
+ srcFactor = GL_ONE_MINUS_DST_COLOR;
+ break;
+ case GLS_SRCBLEND_SRC_ALPHA:
+ srcFactor = GL_SRC_ALPHA;
+ break;
+ case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA:
+ srcFactor = GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ case GLS_SRCBLEND_DST_ALPHA:
+ srcFactor = GL_DST_ALPHA;
+ break;
+ case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA:
+ srcFactor = GL_ONE_MINUS_DST_ALPHA;
+ break;
+ case GLS_SRCBLEND_ALPHA_SATURATE:
+ srcFactor = GL_SRC_ALPHA_SATURATE;
+ break;
+ default:
+ ri.Error( ERR_DROP, "GL_State: invalid src blend state bits" );
+ break;
+ }
+
+ switch ( stateBits & GLS_DSTBLEND_BITS )
+ {
+ case GLS_DSTBLEND_ZERO:
+ dstFactor = GL_ZERO;
+ break;
+ case GLS_DSTBLEND_ONE:
+ dstFactor = GL_ONE;
+ break;
+ case GLS_DSTBLEND_SRC_COLOR:
+ dstFactor = GL_SRC_COLOR;
+ break;
+ case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR:
+ dstFactor = GL_ONE_MINUS_SRC_COLOR;
+ break;
+ case GLS_DSTBLEND_SRC_ALPHA:
+ dstFactor = GL_SRC_ALPHA;
+ break;
+ case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA:
+ dstFactor = GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ case GLS_DSTBLEND_DST_ALPHA:
+ dstFactor = GL_DST_ALPHA;
+ break;
+ case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA:
+ dstFactor = GL_ONE_MINUS_DST_ALPHA;
+ break;
+ default:
+ ri.Error( ERR_DROP, "GL_State: invalid dst blend state bits" );
+ break;
+ }
+
+ qglBlendFunc( srcFactor, dstFactor );
+ }
+ }
+
+ //
+ // check depthmask
+ //
+ if ( diff & GLS_DEPTHMASK_TRUE )
+ {
+ if ( stateBits & GLS_DEPTHMASK_TRUE )
+ {
+ qglDepthMask( GL_TRUE );
+ }
+ else
+ {
+ qglDepthMask( GL_FALSE );
+ }
+ }
+
+ //
+ // fill/line mode
+ //
+ if ( diff & GLS_POLYMODE_LINE )
+ {
+ if ( stateBits & GLS_POLYMODE_LINE )
+ {
+ qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
+ }
+ else
+ {
+ qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
+ }
+ }
+
+ //
+ // depthtest
+ //
+ if ( diff & GLS_DEPTHTEST_DISABLE )
+ {
+ if ( stateBits & GLS_DEPTHTEST_DISABLE )
+ {
+ qglDisable( GL_DEPTH_TEST );
+ }
+ else
+ {
+ qglEnable( GL_DEPTH_TEST );
+ }
+ }
+
+ glState.glStateBits = stateBits;
+}
+
+
+void GL_SetProjectionMatrix(mat4_t matrix)
+{
+ Mat4Copy(matrix, glState.projection);
+ Mat4Multiply(glState.projection, glState.modelview, glState.modelviewProjection);
+}
+
+
+void GL_SetModelviewMatrix(mat4_t matrix)
+{
+ Mat4Copy(matrix, glState.modelview);
+ Mat4Multiply(glState.projection, glState.modelview, glState.modelviewProjection);
+}
+
+
+/*
+================
+RB_Hyperspace
+
+A player has predicted a teleport, but hasn't arrived yet
+================
+*/
+static void RB_Hyperspace( void ) {
+ float c;
+
+ if ( !backEnd.isHyperspace ) {
+ // do initialization shit
+ }
+
+ c = ( backEnd.refdef.time & 255 ) / 255.0f;
+ qglClearColor( c, c, c, 1 );
+ qglClear( GL_COLOR_BUFFER_BIT );
+ qglClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ backEnd.isHyperspace = true;
+}
+
+
+static void SetViewportAndScissor( void ) {
+ GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix );
+
+ // set the window clipping
+ qglViewport( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY,
+ backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight );
+ qglScissor( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY,
+ backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight );
+}
+
+/*
+=================
+RB_BeginDrawingView
+
+
+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 = true;
+ }
+ if ( r_finish->integer == 0 ) {
+ glState.finishCalled = true;
+ }
+
+ // we will need to change the projection matrix before drawing
+ // 2D images again
+ backEnd.projection2D = false;
+
+ 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 = false;
+ }
+
+ // we will only draw a sun if there was sky rendered in this view
+ backEnd.skyRenderedThisView = false;
+
+ // 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.orientation.axis[0], plane);
+ plane2[1] = DotProduct (backEnd.viewParms.orientation.axis[1], plane);
+ plane2[2] = DotProduct (backEnd.viewParms.orientation.axis[2], plane);
+ plane2[3] = DotProduct (plane, backEnd.viewParms.orientation.origin) - plane[3];
+#endif
+ GL_SetModelviewMatrix( s_flipMatrix );
+ }
+}
+
+
+/*
+==================
+RB_RenderDrawSurfList
+==================
+*/
+void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) {
+ shader_t *shader, *oldShader;
+ int fogNum, oldFogNum;
+ int entityNum, oldEntityNum;
+ int dlighted, oldDlighted;
+ int pshadowed, oldPshadowed;
+ int cubemapIndex, oldCubemapIndex;
+ bool depthRange, oldDepthRange, isCrosshair, wasCrosshair;
+ int i;
+ drawSurf_t *drawSurf;
+ int oldSort;
+ FBO_t* fbo = NULL;
+ bool inQuery = false;
+
+ float depth[2];
+
+
+ // save original time for entity shader offsets
+ double originalTime = backEnd.refdef.floatTime;
+
+ fbo = glState.currentFBO;
+
+ // draw everything
+ oldEntityNum = -1;
+ backEnd.currentEntity = &tr.worldEntity;
+ oldShader = NULL;
+ oldFogNum = -1;
+ oldDepthRange = false;
+ wasCrosshair = false;
+ oldDlighted = false;
+ oldPshadowed = false;
+ oldCubemapIndex = -1;
+ oldSort = -1;
+
+ depth[0] = 0.f;
+ depth[1] = 1.f;
+
+ backEnd.pc.c_surfaces += numDrawSurfs;
+
+ for (i = 0, drawSurf = drawSurfs ; i < numDrawSurfs ; i++, drawSurf++) {
+ if ( drawSurf->sort == oldSort && drawSurf->cubemapIndex == oldCubemapIndex) {
+ if (backEnd.depthFill && shader && shader->sort != SS_OPAQUE)
+ continue;
+
+ // fast path, same as previous sort
+ rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface );
+ continue;
+ }
+ oldSort = drawSurf->sort;
+ R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted, &pshadowed );
+ cubemapIndex = drawSurf->cubemapIndex;
+
+ //
+ // change the tess parameters if needed
+ // a "entityMergable" shader is a shader that can have surfaces from seperate
+ // entities merged into a single batch, like smoke and blood puff sprites
+ if ( shader != NULL && ( shader != oldShader || fogNum != oldFogNum || dlighted != oldDlighted || pshadowed != oldPshadowed || cubemapIndex != oldCubemapIndex
+ || ( entityNum != oldEntityNum && !shader->entityMergable ) ) ) {
+ if (oldShader != NULL) {
+ RB_EndSurface();
+ }
+ RB_BeginSurface( shader, fogNum, cubemapIndex );
+ backEnd.pc.c_surfBatches++;
+ oldShader = shader;
+ oldFogNum = fogNum;
+ oldDlighted = dlighted;
+ oldPshadowed = pshadowed;
+ oldCubemapIndex = cubemapIndex;
+ }
+
+ if (backEnd.depthFill && shader && shader->sort != SS_OPAQUE)
+ continue;
+
+ //
+ // change the modelview matrix if needed
+ //
+ if ( entityNum != oldEntityNum ) {
+ bool sunflare = false;
+ depthRange = isCrosshair = false;
+
+ if ( entityNum != REFENTITYNUM_WORLD ) {
+ backEnd.currentEntity = &backEnd.refdef.entities[entityNum];
+
+ // FIXME: e.shaderTime must be passed as int to avoid fp-precision loss issues
+ backEnd.refdef.floatTime = originalTime - (double)backEnd.currentEntity->e.shaderTime;
+
+ // we have to reset the shaderTime as well otherwise image animations start
+ // from the wrong frame
+ tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
+
+ // set up the transformation matrix
+ R_RotateForEntity( backEnd.currentEntity, &backEnd.viewParms, &backEnd.orientation );
+
+ // set up the dynamic lighting if needed
+ if ( backEnd.currentEntity->needDlights ) {
+ R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.orientation );
+ }
+
+ if(backEnd.currentEntity->e.renderfx & RF_DEPTHHACK)
+ {
+ // hack the depth range to prevent view model from poking into walls
+ depthRange = true;
+
+ if(backEnd.currentEntity->e.renderfx & RF_CROSSHAIR)
+ isCrosshair = true;
+ }
+ } else {
+ backEnd.currentEntity = &tr.worldEntity;
+ backEnd.refdef.floatTime = originalTime;
+ backEnd.orientation = backEnd.viewParms.world;
+ // we have to reset the shaderTime as well otherwise image animations on
+ // the world (like water) continue with the wrong frame
+ tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
+ R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.orientation );
+ }
+
+ GL_SetModelviewMatrix( backEnd.orientation.modelMatrix );
+
+ //
+ // change depthrange. Also change projection matrix so first person weapon does not look like coming
+ // out of the screen.
+ //
+ if (oldDepthRange != depthRange || wasCrosshair != isCrosshair)
+ {
+ if (depthRange)
+ {
+ if(backEnd.viewParms.stereoFrame != STEREO_CENTER)
+ {
+ if(isCrosshair)
+ {
+ if(oldDepthRange)
+ {
+ // was not a crosshair but now is, change back proj matrix
+ GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix );
+ }
+ }
+ else
+ {
+ viewParms_t temp = backEnd.viewParms;
+
+ R_SetupProjection(&temp, r_znear->value, 0, false);
+
+ GL_SetProjectionMatrix( temp.projectionMatrix );
+ }
+ }
+
+ if(!oldDepthRange)
+ {
+ depth[0] = 0;
+ depth[1] = 0.3f;
+ qglDepthRange (depth[0], depth[1]);
+ }
+ }
+ else
+ {
+ if(!wasCrosshair && backEnd.viewParms.stereoFrame != STEREO_CENTER)
+ {
+ GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix );
+ }
+
+ if (!sunflare)
+ qglDepthRange (0, 1);
+
+ depth[0] = 0;
+ depth[1] = 1;
+ }
+
+ oldDepthRange = depthRange;
+ wasCrosshair = isCrosshair;
+ }
+
+ oldEntityNum = entityNum;
+ }
+
+ // add the triangles for this surface
+ rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface );
+ }
+
+ backEnd.refdef.floatTime = originalTime;
+
+ // draw the contents of the last shader batch
+ if (oldShader != NULL) {
+ RB_EndSurface();
+ }
+
+ if (inQuery) {
+ qglEndQuery(GL_SAMPLES_PASSED);
+ }
+
+ if (glRefConfig.framebufferObject)
+ FBO_Bind(fbo);
+
+ // go back to the world modelview matrix
+
+ GL_SetModelviewMatrix( backEnd.viewParms.world.modelMatrix );
+
+ qglDepthRange (0, 1);
+}
+
+
+/*
+============================================================================
+
+RENDER BACK END FUNCTIONS
+
+============================================================================
+*/
+
+/*
+================
+RB_SetGL2D
+
+================
+*/
+void RB_SetGL2D (void) {
+ mat4_t matrix;
+ int width, height;
+
+ if (backEnd.projection2D && backEnd.last2DFBO == glState.currentFBO)
+ return;
+
+ backEnd.projection2D = true;
+ backEnd.last2DFBO = glState.currentFBO;
+
+ if (glState.currentFBO)
+ {
+ width = glState.currentFBO->width;
+ height = glState.currentFBO->height;
+ }
+ else
+ {
+ width = glConfig.vidWidth;
+ height = glConfig.vidHeight;
+ }
+
+ // set 2D virtual screen size
+ qglViewport( 0, 0, width, height );
+ qglScissor( 0, 0, width, height );
+
+ Mat4Ortho(0, width, height, 0, 0, 1, matrix);
+ GL_SetProjectionMatrix(matrix);
+ Mat4Identity(matrix);
+ GL_SetModelviewMatrix(matrix);
+
+ GL_State( GLS_DEPTHTEST_DISABLE |
+ GLS_SRCBLEND_SRC_ALPHA |
+ GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA );
+
+ GL_Cull( CT_TWO_SIDED );
+ qglDisable( GL_CLIP_PLANE0 );
+
+ // set time for 2D shaders
+ backEnd.refdef.time = ri.Milliseconds();
+ backEnd.refdef.floatTime = backEnd.refdef.time * 0.001;
+}
+
+
+/*
+=============
+RE_StretchRaw
+
+FIXME: not exactly backend
+Stretches a raw 32 bit power of 2 bitmap image over the given screen rectangle.
+Used for cinematics.
+=============
+*/
+void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, bool dirty) {
+ int i, j;
+ int start, end;
+ vec4_t quadVerts[4];
+ vec2_t texCoords[4];
+
+ if ( !tr.registered ) {
+ return;
+ }
+ R_IssuePendingRenderCommands();
+
+ if ( tess.numIndexes ) {
+ RB_EndSurface();
+ }
+
+ // we definately want to sync every frame for the cinematics
+ qglFinish();
+
+ start = 0;
+ if ( r_speeds->integer ) {
+ start = ri.Milliseconds();
+ }
+
+ // make sure rows and cols are powers of 2
+ for ( i = 0 ; ( 1 << i ) < cols ; i++ ) {
+ }
+ for ( j = 0 ; ( 1 << j ) < rows ; j++ ) {
+ }
+ if ( ( 1 << i ) != cols || ( 1 << j ) != rows) {
+ ri.Error (ERR_DROP, "Draw_StretchRaw: size not a power of 2: %i by %i", cols, rows);
+ }
+
+ RE_UploadCinematic (w, h, cols, rows, data, client, dirty);
+ GL_BindToTMU(tr.scratchImage[client], TB_COLORMAP);
+
+ if ( r_speeds->integer ) {
+ end = ri.Milliseconds();
+ ri.Printf( PRINT_ALL, "qglTexSubImage2D %i, %i: %i msec\n", cols, rows, end - start );
+ }
+
+ // FIXME: HUGE hack
+ if (glRefConfig.framebufferObject)
+ {
+ FBO_Bind(backEnd.framePostProcessed ? NULL : tr.renderFbo);
+ }
+
+ RB_SetGL2D();
+
+ VectorSet4(quadVerts[0], x, y, 0.0f, 1.0f);
+ VectorSet4(quadVerts[1], x + w, y, 0.0f, 1.0f);
+ VectorSet4(quadVerts[2], x + w, y + h, 0.0f, 1.0f);
+ VectorSet4(quadVerts[3], x, y + h, 0.0f, 1.0f);
+
+ VectorSet2(texCoords[0], 0.5f / cols, 0.5f / rows);
+ VectorSet2(texCoords[1], (cols - 0.5f) / cols, 0.5f / rows);
+ VectorSet2(texCoords[2], (cols - 0.5f) / cols, (rows - 0.5f) / rows);
+ VectorSet2(texCoords[3], 0.5f / cols, (rows - 0.5f) / rows);
+
+ GLSL_BindProgram(&tr.textureColorShader);
+
+ GLSL_SetUniformMat4(&tr.textureColorShader, UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection);
+ GLSL_SetUniformVec4(&tr.textureColorShader, UNIFORM_COLOR, colorWhite);
+
+ RB_InstantQuad2(quadVerts, texCoords);
+}
+
+void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, bool dirty) {
+ GLuint texture;
+
+ if (!tr.scratchImage[client])
+ {
+ ri.Printf(PRINT_WARNING, "RE_UploadCinematic: scratch images not initialized\n");
+ return;
+ }
+
+ texture = tr.scratchImage[client]->texnum;
+
+ // if the scratchImage isn't in the format we want, specify it as a new texture
+ if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) {
+ tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols;
+ tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows;
+ qglTextureImage2DEXT(texture, GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ qglTextureParameterfEXT(texture, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ qglTextureParameterfEXT(texture, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+ qglTextureParameterfEXT(texture, GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ qglTextureParameterfEXT(texture, GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ } else {
+ if (dirty) {
+ // otherwise, just subimage upload it so that drivers can tell we are going to be changing
+ // it and don't try and do a texture compression
+ qglTextureSubImage2DEXT(texture, GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ }
+ }
+}
+
+
+/*
+=============
+RB_SetColor
+
+=============
+*/
+const void *RB_SetColor( const void *data ) {
+ const setColorCommand_t *cmd;
+
+ cmd = (const setColorCommand_t *)data;
+
+ backEnd.color2D[0] = cmd->color[0] * 255;
+ backEnd.color2D[1] = cmd->color[1] * 255;
+ backEnd.color2D[2] = cmd->color[2] * 255;
+ backEnd.color2D[3] = cmd->color[3] * 255;
+
+ return (const void *)(cmd + 1);
+}
+
+/*
+=============
+RB_StretchPic
+=============
+*/
+const void *RB_StretchPic ( const void *data ) {
+ const stretchPicCommand_t *cmd;
+ shader_t *shader;
+ int numVerts, numIndexes;
+
+ cmd = (const stretchPicCommand_t *)data;
+
+ // FIXME: HUGE hack
+ if (glRefConfig.framebufferObject)
+ FBO_Bind(backEnd.framePostProcessed ? NULL : tr.renderFbo);
+
+ RB_SetGL2D();
+
+ shader = cmd->shader;
+ if ( shader != tess.shader ) {
+ if ( tess.numIndexes ) {
+ RB_EndSurface();
+ }
+ backEnd.currentEntity = &backEnd.entity2D;
+ RB_BeginSurface( shader, 0, 0 );
+ }
+
+ RB_CHECKOVERFLOW( 4, 6 );
+ numVerts = tess.numVertexes;
+ numIndexes = tess.numIndexes;
+
+ tess.numVertexes += 4;
+ tess.numIndexes += 6;
+
+ tess.indexes[ numIndexes ] = numVerts + 3;
+ tess.indexes[ numIndexes + 1 ] = numVerts + 0;
+ tess.indexes[ numIndexes + 2 ] = numVerts + 2;
+ tess.indexes[ numIndexes + 3 ] = numVerts + 2;
+ tess.indexes[ numIndexes + 4 ] = numVerts + 0;
+ tess.indexes[ numIndexes + 5 ] = numVerts + 1;
+
+ {
+ uint16_t color[4];
+
+ VectorScale4(backEnd.color2D, 257, color);
+
+ VectorCopy4(color, tess.color[ numVerts ]);
+ VectorCopy4(color, tess.color[ numVerts + 1]);
+ VectorCopy4(color, tess.color[ numVerts + 2]);
+ VectorCopy4(color, tess.color[ numVerts + 3 ]);
+ }
+
+ tess.xyz[ numVerts ][0] = cmd->x;
+ tess.xyz[ numVerts ][1] = cmd->y;
+ tess.xyz[ numVerts ][2] = 0;
+
+ tess.texCoords[ numVerts ][0] = cmd->s1;
+ tess.texCoords[ numVerts ][1] = cmd->t1;
+
+ tess.xyz[ numVerts + 1 ][0] = cmd->x + cmd->w;
+ tess.xyz[ numVerts + 1 ][1] = cmd->y;
+ tess.xyz[ numVerts + 1 ][2] = 0;
+
+ tess.texCoords[ numVerts + 1 ][0] = cmd->s2;
+ tess.texCoords[ numVerts + 1 ][1] = cmd->t1;
+
+ tess.xyz[ numVerts + 2 ][0] = cmd->x + cmd->w;
+ tess.xyz[ numVerts + 2 ][1] = cmd->y + cmd->h;
+ tess.xyz[ numVerts + 2 ][2] = 0;
+
+ tess.texCoords[ numVerts + 2 ][0] = cmd->s2;
+ tess.texCoords[ numVerts + 2 ][1] = cmd->t2;
+
+ tess.xyz[ numVerts + 3 ][0] = cmd->x;
+ tess.xyz[ numVerts + 3 ][1] = cmd->y + cmd->h;
+ tess.xyz[ numVerts + 3 ][2] = 0;
+
+ tess.texCoords[ numVerts + 3 ][0] = cmd->s1;
+ tess.texCoords[ numVerts + 3 ][1] = cmd->t2;
+
+ return (const void *)(cmd + 1);
+}
+
+
+/*
+=============
+RB_DrawSurfs
+
+=============
+*/
+const void *RB_DrawSurfs( const void *data ) {
+ const drawSurfsCommand_t *cmd;
+ bool isShadowView;
+
+ // finish any 2D drawing if needed
+ if ( tess.numIndexes ) {
+ RB_EndSurface();
+ }
+
+ cmd = (const drawSurfsCommand_t *)data;
+
+ backEnd.refdef = cmd->refdef;
+ backEnd.viewParms = cmd->viewParms;
+
+ isShadowView = !!(backEnd.viewParms.flags & VPF_DEPTHSHADOW);
+
+ // clear the z buffer, set the modelview, etc
+ RB_BeginDrawingView ();
+
+ if (glRefConfig.framebufferObject && (backEnd.viewParms.flags & VPF_DEPTHCLAMP) && glRefConfig.depthClamp)
+ {
+ qglEnable(GL_DEPTH_CLAMP);
+ }
+
+ if (glRefConfig.framebufferObject && !(backEnd.refdef.rdflags & RDF_NOWORLDMODEL) && (r_depthPrepass->integer || isShadowView))
+ {
+ FBO_t *oldFbo = glState.currentFBO;
+ vec4_t viewInfo;
+
+ VectorSet4(viewInfo, backEnd.viewParms.zFar / r_znear->value, backEnd.viewParms.zFar, 0.0, 0.0);
+
+ backEnd.depthFill = true;
+ qglColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ RB_RenderDrawSurfList( cmd->drawSurfs, cmd->numDrawSurfs );
+ qglColorMask(!backEnd.colorMask[0], !backEnd.colorMask[1], !backEnd.colorMask[2], !backEnd.colorMask[3]);
+ backEnd.depthFill = false;
+
+ if (!isShadowView)
+ {
+ if (tr.msaaResolveFbo)
+ {
+ // If we're using multisampling, resolve the depth first
+ FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+ }
+ else if (tr.renderFbo == NULL && tr.renderDepthImage)
+ {
+ // If we're rendering directly to the screen, copy the depth to a texture
+ // This is incredibly slow on Intel Graphics, so just skip it on there
+ if (!glRefConfig.intelGraphics)
+ qglCopyTextureSubImage2DEXT(tr.renderDepthImage->texnum, GL_TEXTURE_2D, 0, 0, 0, 0, 0, glConfig.vidWidth, glConfig.vidHeight);
+ }
+
+ if (tr.hdrDepthFbo)
+ {
+ // need the depth in a texture we can do GL_LINEAR sampling on, so copy it to an HDR image
+ vec4_t srcTexCoords;
+
+ VectorSet4(srcTexCoords, 0.0f, 0.0f, 1.0f, 1.0f);
+
+ FBO_BlitFromTexture(tr.renderDepthImage, srcTexCoords, NULL, tr.hdrDepthFbo, NULL, NULL, NULL, 0);
+ }
+
+ if (r_sunlightMode->integer && backEnd.viewParms.flags & VPF_USESUNLIGHT)
+ {
+ vec4_t quadVerts[4];
+ vec2_t texCoords[4];
+ vec4_t box;
+
+ FBO_Bind(tr.screenShadowFbo);
+
+ box[0] = backEnd.viewParms.viewportX * tr.screenShadowFbo->width / (float)glConfig.vidWidth;
+ box[1] = backEnd.viewParms.viewportY * tr.screenShadowFbo->height / (float)glConfig.vidHeight;
+ box[2] = backEnd.viewParms.viewportWidth * tr.screenShadowFbo->width / (float)glConfig.vidWidth;
+ box[3] = backEnd.viewParms.viewportHeight * tr.screenShadowFbo->height / (float)glConfig.vidHeight;
+
+ qglViewport(box[0], box[1], box[2], box[3]);
+ qglScissor(box[0], box[1], box[2], box[3]);
+
+ box[0] = backEnd.viewParms.viewportX / (float)glConfig.vidWidth;
+ box[1] = backEnd.viewParms.viewportY / (float)glConfig.vidHeight;
+ box[2] = box[0] + backEnd.viewParms.viewportWidth / (float)glConfig.vidWidth;
+ box[3] = box[1] + backEnd.viewParms.viewportHeight / (float)glConfig.vidHeight;
+
+ texCoords[0][0] = box[0]; texCoords[0][1] = box[3];
+ texCoords[1][0] = box[2]; texCoords[1][1] = box[3];
+ texCoords[2][0] = box[2]; texCoords[2][1] = box[1];
+ texCoords[3][0] = box[0]; texCoords[3][1] = box[1];
+
+ box[0] = -1.0f;
+ box[1] = -1.0f;
+ box[2] = 1.0f;
+ box[3] = 1.0f;
+
+ VectorSet4(quadVerts[0], box[0], box[3], 0, 1);
+ VectorSet4(quadVerts[1], box[2], box[3], 0, 1);
+ VectorSet4(quadVerts[2], box[2], box[1], 0, 1);
+ VectorSet4(quadVerts[3], box[0], box[1], 0, 1);
+
+ GL_State(GLS_DEPTHTEST_DISABLE);
+
+ GLSL_BindProgram(&tr.shadowmaskShader);
+
+ GL_BindToTMU(tr.renderDepthImage, TB_COLORMAP);
+
+ if (r_shadowCascadeZFar->integer != 0)
+ {
+ GL_BindToTMU(tr.sunShadowDepthImage[0], TB_SHADOWMAP);
+ GL_BindToTMU(tr.sunShadowDepthImage[1], TB_SHADOWMAP2);
+ GL_BindToTMU(tr.sunShadowDepthImage[2], TB_SHADOWMAP3);
+ GL_BindToTMU(tr.sunShadowDepthImage[3], TB_SHADOWMAP4);
+
+ GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP, backEnd.refdef.sunShadowMvp[0]);
+ GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP2, backEnd.refdef.sunShadowMvp[1]);
+ GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP3, backEnd.refdef.sunShadowMvp[2]);
+ GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP4, backEnd.refdef.sunShadowMvp[3]);
+ }
+ else
+ {
+ GL_BindToTMU(tr.sunShadowDepthImage[3], TB_SHADOWMAP);
+ GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP, backEnd.refdef.sunShadowMvp[3]);
+ }
+
+ GLSL_SetUniformVec3(&tr.shadowmaskShader, UNIFORM_VIEWORIGIN, backEnd.refdef.vieworg);
+ {
+ vec3_t viewVector;
+
+ float zmax = backEnd.viewParms.zFar;
+ float ymax = zmax * tan(backEnd.viewParms.fovY * M_PI / 360.0f);
+ float xmax = zmax * tan(backEnd.viewParms.fovX * M_PI / 360.0f);
+
+ VectorScale(backEnd.refdef.viewaxis[0], zmax, viewVector);
+ GLSL_SetUniformVec3(&tr.shadowmaskShader, UNIFORM_VIEWFORWARD, viewVector);
+ VectorScale(backEnd.refdef.viewaxis[1], xmax, viewVector);
+ GLSL_SetUniformVec3(&tr.shadowmaskShader, UNIFORM_VIEWLEFT, viewVector);
+ VectorScale(backEnd.refdef.viewaxis[2], ymax, viewVector);
+ GLSL_SetUniformVec3(&tr.shadowmaskShader, UNIFORM_VIEWUP, viewVector);
+
+ GLSL_SetUniformVec4(&tr.shadowmaskShader, UNIFORM_VIEWINFO, viewInfo);
+ }
+
+ RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes);
+
+ if (r_shadowBlur->integer)
+ {
+ viewInfo[2] = 1.0f / (float)(tr.screenScratchFbo->width);
+ viewInfo[3] = 1.0f / (float)(tr.screenScratchFbo->height);
+
+ FBO_Bind(tr.screenScratchFbo);
+
+ GLSL_BindProgram(&tr.depthBlurShader[0]);
+
+ GL_BindToTMU(tr.screenShadowImage, TB_COLORMAP);
+ GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP);
+
+ GLSL_SetUniformVec4(&tr.depthBlurShader[0], UNIFORM_VIEWINFO, viewInfo);
+
+ RB_InstantQuad2(quadVerts, texCoords);
+
+ FBO_Bind(tr.screenShadowFbo);
+
+ GLSL_BindProgram(&tr.depthBlurShader[1]);
+
+ GL_BindToTMU(tr.screenScratchImage, TB_COLORMAP);
+ GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP);
+
+ GLSL_SetUniformVec4(&tr.depthBlurShader[1], UNIFORM_VIEWINFO, viewInfo);
+
+ RB_InstantQuad2(quadVerts, texCoords);
+ }
+ }
+
+ if (r_ssao->integer)
+ {
+ vec4_t quadVerts[4];
+ vec2_t texCoords[4];
+
+ viewInfo[2] = 1.0f / ((float)(tr.quarterImage[0]->width) * tan(backEnd.viewParms.fovX * M_PI / 360.0f) * 2.0f);
+ viewInfo[3] = 1.0f / ((float)(tr.quarterImage[0]->height) * tan(backEnd.viewParms.fovY * M_PI / 360.0f) * 2.0f);
+ viewInfo[3] *= (float)backEnd.viewParms.viewportHeight / (float)backEnd.viewParms.viewportWidth;
+
+ FBO_Bind(tr.quarterFbo[0]);
+
+ qglViewport(0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height);
+ qglScissor(0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height);
+
+ VectorSet4(quadVerts[0], -1, 1, 0, 1);
+ VectorSet4(quadVerts[1], 1, 1, 0, 1);
+ VectorSet4(quadVerts[2], 1, -1, 0, 1);
+ VectorSet4(quadVerts[3], -1, -1, 0, 1);
+
+ texCoords[0][0] = 0; texCoords[0][1] = 1;
+ texCoords[1][0] = 1; texCoords[1][1] = 1;
+ texCoords[2][0] = 1; texCoords[2][1] = 0;
+ texCoords[3][0] = 0; texCoords[3][1] = 0;
+
+ GL_State( GLS_DEPTHTEST_DISABLE );
+
+ GLSL_BindProgram(&tr.ssaoShader);
+
+ GL_BindToTMU(tr.hdrDepthImage, TB_COLORMAP);
+
+ GLSL_SetUniformVec4(&tr.ssaoShader, UNIFORM_VIEWINFO, viewInfo);
+
+ RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes);
+
+
+ viewInfo[2] = 1.0f / (float)(tr.quarterImage[0]->width);
+ viewInfo[3] = 1.0f / (float)(tr.quarterImage[0]->height);
+
+ FBO_Bind(tr.quarterFbo[1]);
+
+ qglViewport(0, 0, tr.quarterFbo[1]->width, tr.quarterFbo[1]->height);
+ qglScissor(0, 0, tr.quarterFbo[1]->width, tr.quarterFbo[1]->height);
+
+ GLSL_BindProgram(&tr.depthBlurShader[0]);
+
+ GL_BindToTMU(tr.quarterImage[0], TB_COLORMAP);
+ GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP);
+
+ GLSL_SetUniformVec4(&tr.depthBlurShader[0], UNIFORM_VIEWINFO, viewInfo);
+
+ RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes);
+
+
+ FBO_Bind(tr.screenSsaoFbo);
+
+ qglViewport(0, 0, tr.screenSsaoFbo->width, tr.screenSsaoFbo->height);
+ qglScissor(0, 0, tr.screenSsaoFbo->width, tr.screenSsaoFbo->height);
+
+ GLSL_BindProgram(&tr.depthBlurShader[1]);
+
+ GL_BindToTMU(tr.quarterImage[1], TB_COLORMAP);
+ GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP);
+
+ GLSL_SetUniformVec4(&tr.depthBlurShader[1], UNIFORM_VIEWINFO, viewInfo);
+
+
+ RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes);
+ }
+ }
+
+ // reset viewport and scissor
+ FBO_Bind(oldFbo);
+ SetViewportAndScissor();
+ }
+
+ if (glRefConfig.framebufferObject && (backEnd.viewParms.flags & VPF_DEPTHCLAMP) && glRefConfig.depthClamp)
+ {
+ qglDisable(GL_DEPTH_CLAMP);
+ }
+
+ if (!isShadowView)
+ {
+ RB_RenderDrawSurfList( cmd->drawSurfs, cmd->numDrawSurfs );
+
+ if (r_drawSun->integer)
+ {
+ RB_DrawSun(0.1, tr.sunShader);
+ }
+
+ if (glRefConfig.framebufferObject && r_drawSunRays->integer)
+ {
+ FBO_t *oldFbo = glState.currentFBO;
+ FBO_Bind(tr.sunRaysFbo);
+
+ qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
+ qglClear( GL_COLOR_BUFFER_BIT );
+
+ if (glRefConfig.occlusionQuery)
+ {
+ tr.sunFlareQueryActive[tr.sunFlareQueryIndex] = true;
+ qglBeginQuery(GL_SAMPLES_PASSED, tr.sunFlareQuery[tr.sunFlareQueryIndex]);
+ }
+
+ RB_DrawSun(0.3, tr.sunFlareShader);
+
+ if (glRefConfig.occlusionQuery)
+ {
+ qglEndQuery(GL_SAMPLES_PASSED);
+ }
+
+ FBO_Bind(oldFbo);
+ }
+
+ // darken down any stencil shadows
+ RB_ShadowFinish();
+
+ // add light flares on lights that aren't obscured
+ RB_RenderFlares();
+ }
+
+ if (glRefConfig.framebufferObject && tr.renderCubeFbo && backEnd.viewParms.targetFbo == tr.renderCubeFbo)
+ {
+ cubemap_t *cubemap = &tr.cubemaps[backEnd.viewParms.targetFboCubemapIndex];
+
+ FBO_Bind(NULL);
+ if (cubemap && cubemap->image)
+ qglGenerateTextureMipmapEXT(cubemap->image->texnum, GL_TEXTURE_CUBE_MAP);
+ }
+
+ return (const void *)(cmd + 1);
+}
+
+
+/*
+=============
+RB_DrawBuffer
+
+=============
+*/
+const void *RB_DrawBuffer( const void *data ) {
+ const drawBufferCommand_t *cmd;
+
+ cmd = (const drawBufferCommand_t *)data;
+
+ // finish any 2D drawing if needed
+ if(tess.numIndexes)
+ RB_EndSurface();
+
+ if (glRefConfig.framebufferObject)
+ FBO_Bind(NULL);
+
+ qglDrawBuffer( cmd->buffer );
+
+ // clear screen for debugging
+ if ( r_clear->integer ) {
+ qglClearColor( 1, 0, 0.5, 1 );
+ qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+ }
+
+ return (const void *)(cmd + 1);
+}
+
+/*
+===============
+RB_ShowImages
+
+Draw all the images to the screen, on top of whatever
+was there. This is used to test for texture thrashing.
+
+Also called by RE_EndRegistration
+===============
+*/
+void RB_ShowImages( void ) {
+ int i;
+ image_t *image;
+ float x, y, w, h;
+ int start, end;
+
+ RB_SetGL2D();
+
+ qglClear( GL_COLOR_BUFFER_BIT );
+
+ qglFinish();
+
+ start = ri.Milliseconds();
+
+ for ( i=0 ; i<tr.numImages ; i++ ) {
+ image = tr.images[i];
+
+ w = glConfig.vidWidth / 20;
+ h = glConfig.vidHeight / 15;
+ x = i % 20 * w;
+ y = i / 20 * h;
+
+ // show in proportional size in mode 2
+ if ( r_showImages->integer == 2 ) {
+ w *= image->uploadWidth / 512.0f;
+ h *= image->uploadHeight / 512.0f;
+ }
+
+ {
+ vec4_t quadVerts[4];
+
+ GL_BindToTMU(image, TB_COLORMAP);
+
+ VectorSet4(quadVerts[0], x, y, 0, 1);
+ VectorSet4(quadVerts[1], x + w, y, 0, 1);
+ VectorSet4(quadVerts[2], x + w, y + h, 0, 1);
+ VectorSet4(quadVerts[3], x, y + h, 0, 1);
+
+ RB_InstantQuad(quadVerts);
+ }
+ }
+
+ qglFinish();
+
+ end = ri.Milliseconds();
+ ri.Printf( PRINT_ALL, "%i msec to draw all images\n", end - start );
+
+}
+
+/*
+=============
+RB_ColorMask
+
+=============
+*/
+const void *RB_ColorMask(const void *data)
+{
+ const colorMaskCommand_t *cmd = (colorMaskCommand_t*)data;
+
+ // finish any 2D drawing if needed
+ if(tess.numIndexes)
+ RB_EndSurface();
+
+ if (glRefConfig.framebufferObject)
+ {
+ // reverse color mask, so 0 0 0 0 is the default
+ backEnd.colorMask[0] = !cmd->rgba[0];
+ backEnd.colorMask[1] = !cmd->rgba[1];
+ backEnd.colorMask[2] = !cmd->rgba[2];
+ backEnd.colorMask[3] = !cmd->rgba[3];
+ }
+
+ qglColorMask(cmd->rgba[0], cmd->rgba[1], cmd->rgba[2], cmd->rgba[3]);
+
+ return (const void *)(cmd + 1);
+}
+
+/*
+=============
+RB_ClearDepth
+
+=============
+*/
+const void *RB_ClearDepth(const void *data)
+{
+ const clearDepthCommand_t *cmd = (clearDepthCommand_t*)data;
+
+ // finish any 2D drawing if needed
+ if(tess.numIndexes)
+ RB_EndSurface();
+
+ // texture swapping test
+ if (r_showImages->integer)
+ RB_ShowImages();
+
+ if (glRefConfig.framebufferObject)
+ {
+ if (!tr.renderFbo || backEnd.framePostProcessed)
+ {
+ FBO_Bind(NULL);
+ }
+ else
+ {
+ FBO_Bind(tr.renderFbo);
+ }
+ }
+
+ qglClear(GL_DEPTH_BUFFER_BIT);
+
+ // if we're doing MSAA, clear the depth texture for the resolve buffer
+ if (tr.msaaResolveFbo)
+ {
+ FBO_Bind(tr.msaaResolveFbo);
+ qglClear(GL_DEPTH_BUFFER_BIT);
+ }
+
+
+ return (const void *)(cmd + 1);
+}
+
+
+/*
+=============
+RB_SwapBuffers
+
+=============
+*/
+const void *RB_SwapBuffers( const void *data ) {
+ const swapBuffersCommand_t *cmd;
+
+ // finish any 2D drawing if needed
+ if ( tess.numIndexes ) {
+ RB_EndSurface();
+ }
+
+ // texture swapping test
+ if ( r_showImages->integer ) {
+ RB_ShowImages();
+ }
+
+ cmd = (const swapBuffersCommand_t *)data;
+
+ // we measure overdraw by reading back the stencil buffer and
+ // counting up the number of increments that have happened
+ if ( r_measureOverdraw->integer ) {
+ int i;
+ long sum = 0;
+ unsigned char *stencilReadback;
+
+ stencilReadback = (unsigned char*)ri.Hunk_AllocateTempMemory( glConfig.vidWidth * glConfig.vidHeight );
+ qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback );
+
+ for ( i = 0; i < glConfig.vidWidth * glConfig.vidHeight; i++ ) {
+ sum += stencilReadback[i];
+ }
+
+ backEnd.pc.c_overDraw += sum;
+ ri.Hunk_FreeTempMemory( stencilReadback );
+ }
+
+ if (glRefConfig.framebufferObject)
+ {
+ if (!backEnd.framePostProcessed)
+ {
+ if (tr.msaaResolveFbo && r_hdr->integer)
+ {
+ // Resolving an RGB16F MSAA FBO to the screen messes with the brightness, so resolve to an RGB16F FBO first
+ FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ FBO_FastBlit(tr.msaaResolveFbo, NULL, NULL, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ }
+ else if (tr.renderFbo)
+ {
+ FBO_FastBlit(tr.renderFbo, NULL, NULL, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ }
+ }
+ }
+
+ if ( !glState.finishCalled ) {
+ qglFinish();
+ }
+
+ GLimp_LogComment( "***************** RB_SwapBuffers *****************\n\n\n" );
+
+ GLimp_EndFrame();
+
+ backEnd.framePostProcessed = false;
+ backEnd.projection2D = false;
+
+ return (const void *)(cmd + 1);
+}
+
+/*
+=============
+RB_CapShadowMap
+
+=============
+*/
+const void *RB_CapShadowMap(const void *data)
+{
+ const capShadowmapCommand_t *cmd = (capShadowmapCommand_t*)data;
+
+ // finish any 2D drawing if needed
+ if(tess.numIndexes)
+ RB_EndSurface();
+
+ if (cmd->map != -1)
+ {
+ if (cmd->cubeSide != -1)
+ {
+ if (tr.shadowCubemaps[cmd->map])
+ {
+ qglCopyTextureSubImage2DEXT(tr.shadowCubemaps[cmd->map]->texnum, GL_TEXTURE_CUBE_MAP_POSITIVE_X + cmd->cubeSide, 0, 0, 0, backEnd.refdef.x, glConfig.vidHeight - ( backEnd.refdef.y + PSHADOW_MAP_SIZE ), PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE);
+ }
+ }
+ else
+ {
+ if (tr.pshadowMaps[cmd->map])
+ {
+ qglCopyTextureSubImage2DEXT(tr.pshadowMaps[cmd->map]->texnum, GL_TEXTURE_2D, 0, 0, 0, backEnd.refdef.x, glConfig.vidHeight - (backEnd.refdef.y + PSHADOW_MAP_SIZE), PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE);
+ }
+ }
+ }
+
+ return (const void *)(cmd + 1);
+}
+
+
+/*
+=============
+RB_PostProcess
+
+=============
+*/
+const void *RB_PostProcess(const void *data)
+{
+ const postProcessCommand_t *cmd = (const postProcessCommand_t*)data;
+ FBO_t *srcFbo;
+ ivec4_t srcBox, dstBox;
+ bool autoExposure;
+
+ // finish any 2D drawing if needed
+ if(tess.numIndexes)
+ RB_EndSurface();
+
+ if (!glRefConfig.framebufferObject || !r_postProcess->integer)
+ {
+ // do nothing
+ return (const void *)(cmd + 1);
+ }
+
+ if (cmd)
+ {
+ backEnd.refdef = cmd->refdef;
+ backEnd.viewParms = cmd->viewParms;
+ }
+
+ srcFbo = tr.renderFbo;
+ if (tr.msaaResolveFbo)
+ {
+ // Resolve the MSAA before anything else
+ // Can't resolve just part of the MSAA FBO, so multiple views will suffer a performance hit here
+ FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+ srcFbo = tr.msaaResolveFbo;
+ }
+
+ dstBox[0] = backEnd.viewParms.viewportX;
+ dstBox[1] = backEnd.viewParms.viewportY;
+ dstBox[2] = backEnd.viewParms.viewportWidth;
+ dstBox[3] = backEnd.viewParms.viewportHeight;
+
+ if (r_ssao->integer)
+ {
+ srcBox[0] = backEnd.viewParms.viewportX * tr.screenSsaoImage->width / (float)glConfig.vidWidth;
+ srcBox[1] = backEnd.viewParms.viewportY * tr.screenSsaoImage->height / (float)glConfig.vidHeight;
+ srcBox[2] = backEnd.viewParms.viewportWidth * tr.screenSsaoImage->width / (float)glConfig.vidWidth;
+ srcBox[3] = backEnd.viewParms.viewportHeight * tr.screenSsaoImage->height / (float)glConfig.vidHeight;
+
+ FBO_Blit(tr.screenSsaoFbo, srcBox, NULL, srcFbo, dstBox, NULL, NULL, GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO);
+ }
+
+ srcBox[0] = backEnd.viewParms.viewportX;
+ srcBox[1] = backEnd.viewParms.viewportY;
+ srcBox[2] = backEnd.viewParms.viewportWidth;
+ srcBox[3] = backEnd.viewParms.viewportHeight;
+
+ if (srcFbo)
+ {
+ if (r_hdr->integer && (r_toneMap->integer || r_forceToneMap->integer))
+ {
+ autoExposure = r_autoExposure->integer || r_forceAutoExposure->integer;
+ RB_ToneMap(srcFbo, srcBox, NULL, dstBox, autoExposure);
+ }
+ else if (r_cameraExposure->value == 0.0f)
+ {
+ FBO_FastBlit(srcFbo, srcBox, NULL, dstBox, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ }
+ else
+ {
+ vec4_t color;
+
+ color[0] =
+ color[1] =
+ color[2] = pow(2, r_cameraExposure->value); //exp2(r_cameraExposure->value);
+ color[3] = 1.0f;
+
+ FBO_Blit(srcFbo, srcBox, NULL, NULL, dstBox, NULL, color, 0);
+ }
+ }
+
+ if (r_drawSunRays->integer)
+ RB_SunRays(NULL, srcBox, NULL, dstBox);
+
+ if (1)
+ RB_BokehBlur(NULL, srcBox, NULL, dstBox, backEnd.refdef.blurFactor);
+ else
+ RB_GaussianBlur(backEnd.refdef.blurFactor);
+
+#if 0
+ if (0)
+ {
+ vec4_t quadVerts[4];
+ vec2_t texCoords[4];
+ ivec4_t iQtrBox;
+ vec4_t box;
+ vec4_t viewInfo;
+ static float scale = 5.0f;
+
+ scale -= 0.005f;
+ if (scale < 0.01f)
+ scale = 5.0f;
+
+ FBO_FastBlit(NULL, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+
+ iQtrBox[0] = backEnd.viewParms.viewportX * tr.quarterImage[0]->width / (float)glConfig.vidWidth;
+ iQtrBox[1] = backEnd.viewParms.viewportY * tr.quarterImage[0]->height / (float)glConfig.vidHeight;
+ iQtrBox[2] = backEnd.viewParms.viewportWidth * tr.quarterImage[0]->width / (float)glConfig.vidWidth;
+ iQtrBox[3] = backEnd.viewParms.viewportHeight * tr.quarterImage[0]->height / (float)glConfig.vidHeight;
+
+ qglViewport(iQtrBox[0], iQtrBox[1], iQtrBox[2], iQtrBox[3]);
+ qglScissor(iQtrBox[0], iQtrBox[1], iQtrBox[2], iQtrBox[3]);
+
+ VectorSet4(box, 0.0f, 0.0f, 1.0f, 1.0f);
+
+ texCoords[0][0] = box[0]; texCoords[0][1] = box[3];
+ texCoords[1][0] = box[2]; texCoords[1][1] = box[3];
+ texCoords[2][0] = box[2]; texCoords[2][1] = box[1];
+ texCoords[3][0] = box[0]; texCoords[3][1] = box[1];
+
+ VectorSet4(box, -1.0f, -1.0f, 1.0f, 1.0f);
+
+ VectorSet4(quadVerts[0], box[0], box[3], 0, 1);
+ VectorSet4(quadVerts[1], box[2], box[3], 0, 1);
+ VectorSet4(quadVerts[2], box[2], box[1], 0, 1);
+ VectorSet4(quadVerts[3], box[0], box[1], 0, 1);
+
+ GL_State(GLS_DEPTHTEST_DISABLE);
+
+
+ VectorSet4(viewInfo, backEnd.viewParms.zFar / r_znear->value, backEnd.viewParms.zFar, 0.0, 0.0);
+
+ viewInfo[2] = scale / (float)(tr.quarterImage[0]->width);
+ viewInfo[3] = scale / (float)(tr.quarterImage[0]->height);
+
+ FBO_Bind(tr.quarterFbo[1]);
+ GLSL_BindProgram(&tr.depthBlurShader[2]);
+ GL_BindToTMU(tr.quarterImage[0], TB_COLORMAP);
+ GLSL_SetUniformVec4(&tr.depthBlurShader[2], UNIFORM_VIEWINFO, viewInfo);
+ RB_InstantQuad2(quadVerts, texCoords);
+
+ FBO_Bind(tr.quarterFbo[0]);
+ GLSL_BindProgram(&tr.depthBlurShader[3]);
+ GL_BindToTMU(tr.quarterImage[1], TB_COLORMAP);
+ GLSL_SetUniformVec4(&tr.depthBlurShader[3], UNIFORM_VIEWINFO, viewInfo);
+ RB_InstantQuad2(quadVerts, texCoords);
+
+ SetViewportAndScissor();
+
+ FBO_FastBlit(tr.quarterFbo[1], NULL, NULL, NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ FBO_Bind(NULL);
+ }
+#endif
+
+ if (0 && r_sunlightMode->integer)
+ {
+ ivec4_t dstBox;
+ VectorSet4(dstBox, 0, glConfig.vidHeight - 128, 128, 128);
+ FBO_BlitFromTexture(tr.sunShadowDepthImage[0], NULL, NULL, NULL, dstBox, NULL, NULL, 0);
+ VectorSet4(dstBox, 128, glConfig.vidHeight - 128, 128, 128);
+ FBO_BlitFromTexture(tr.sunShadowDepthImage[1], NULL, NULL, NULL, dstBox, NULL, NULL, 0);
+ VectorSet4(dstBox, 256, glConfig.vidHeight - 128, 128, 128);
+ FBO_BlitFromTexture(tr.sunShadowDepthImage[2], NULL, NULL, NULL, dstBox, NULL, NULL, 0);
+ VectorSet4(dstBox, 384, glConfig.vidHeight - 128, 128, 128);
+ FBO_BlitFromTexture(tr.sunShadowDepthImage[3], NULL, NULL, NULL, dstBox, NULL, NULL, 0);
+ }
+
+ if (0 && r_shadows->integer == 4)
+ {
+ ivec4_t dstBox;
+ VectorSet4(dstBox, 512 + 0, glConfig.vidHeight - 128, 128, 128);
+ FBO_BlitFromTexture(tr.pshadowMaps[0], NULL, NULL, NULL, dstBox, NULL, NULL, 0);
+ VectorSet4(dstBox, 512 + 128, glConfig.vidHeight - 128, 128, 128);
+ FBO_BlitFromTexture(tr.pshadowMaps[1], NULL, NULL, NULL, dstBox, NULL, NULL, 0);
+ VectorSet4(dstBox, 512 + 256, glConfig.vidHeight - 128, 128, 128);
+ FBO_BlitFromTexture(tr.pshadowMaps[2], NULL, NULL, NULL, dstBox, NULL, NULL, 0);
+ VectorSet4(dstBox, 512 + 384, glConfig.vidHeight - 128, 128, 128);
+ FBO_BlitFromTexture(tr.pshadowMaps[3], NULL, NULL, NULL, dstBox, NULL, NULL, 0);
+ }
+
+ if (0)
+ {
+ ivec4_t dstBox;
+ VectorSet4(dstBox, 256, glConfig.vidHeight - 256, 256, 256);
+ FBO_BlitFromTexture(tr.renderDepthImage, NULL, NULL, NULL, dstBox, NULL, NULL, 0);
+ VectorSet4(dstBox, 512, glConfig.vidHeight - 256, 256, 256);
+ FBO_BlitFromTexture(tr.screenShadowImage, NULL, NULL, NULL, dstBox, NULL, NULL, 0);
+ }
+
+ if (0)
+ {
+ ivec4_t dstBox;
+ VectorSet4(dstBox, 256, glConfig.vidHeight - 256, 256, 256);
+ FBO_BlitFromTexture(tr.sunRaysImage, NULL, NULL, NULL, dstBox, NULL, NULL, 0);
+ }
+
+#if 0
+ if (r_cubeMapping->integer && tr.numCubemaps)
+ {
+ ivec4_t dstBox;
+ int cubemapIndex = R_CubemapForPoint( backEnd.viewParms.orientation.origin );
+
+ if (cubemapIndex)
+ {
+ VectorSet4(dstBox, 0, glConfig.vidHeight - 256, 256, 256);
+ //FBO_BlitFromTexture(tr.renderCubeImage, NULL, NULL, NULL, dstBox, &tr.testcubeShader, NULL, 0);
+ FBO_BlitFromTexture(tr.cubemaps[cubemapIndex - 1].image, NULL, NULL, NULL, dstBox, &tr.testcubeShader, NULL, 0);
+ }
+ }
+#endif
+
+ backEnd.framePostProcessed = true;
+
+ return (const void *)(cmd + 1);
+}
+
+// FIXME: put this function declaration elsewhere
+void R_SaveDDS(const char *filename, byte *pic, int width, int height, int depth);
+
+/*
+=============
+RB_ExportCubemaps
+
+=============
+*/
+const void *RB_ExportCubemaps(const void *data)
+{
+ const exportCubemapsCommand_t *cmd = (const exportCubemapsCommand_t*)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 = (byte*)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);
+}
+
+
+/*
+====================
+RB_ExecuteRenderCommands
+====================
+*/
+void RB_ExecuteRenderCommands( const void *data ) {
+ int t1, t2;
+
+ t1 = ri.Milliseconds ();
+
+ while ( 1 ) {
+ data = PADP(data, sizeof(void *));
+
+ switch ( *(const int *)data ) {
+ case RC_SET_COLOR:
+ data = RB_SetColor( data );
+ break;
+ case RC_STRETCH_PIC:
+ data = RB_StretchPic( data );
+ break;
+ case RC_DRAW_SURFS:
+ data = RB_DrawSurfs( data );
+ break;
+ case RC_DRAW_BUFFER:
+ data = RB_DrawBuffer( data );
+ break;
+ case RC_SWAP_BUFFERS:
+ data = RB_SwapBuffers( data );
+ break;
+ case RC_SCREENSHOT:
+ data = RB_TakeScreenshotCmd( data );
+ break;
+ case RC_VIDEOFRAME:
+ data = RB_TakeVideoFrameCmd( data );
+ break;
+ case RC_COLORMASK:
+ data = RB_ColorMask(data);
+ break;
+ case RC_CLEARDEPTH:
+ data = RB_ClearDepth(data);
+ break;
+ case RC_CAPSHADOWMAP:
+ data = RB_CapShadowMap(data);
+ break;
+ case RC_POSTPROCESS:
+ data = RB_PostProcess(data);
+ break;
+ case RC_EXPORT_CUBEMAPS:
+ data = RB_ExportCubemaps(data);
+ break;
+ case RC_END_OF_LIST:
+ default:
+ // finish any 2D drawing if needed
+ if(tess.numIndexes)
+ RB_EndSurface();
+
+ // stop rendering
+ t2 = ri.Milliseconds ();
+ backEnd.pc.msec = t2 - t1;
+ return;
+ }
+ }
+
+}