/* * R_DrawBlackBottom * * Draw dummy skybox side to prevent the HOM effect */ static void R_DrawBlackBottom( const skydome_t *skydome, const visSkySide_t *visSides ) { int side = 5; const visSkySide_t *visSide = visSides + side; if( rn.skyMins[0][side] >= rn.skyMaxs[0][side] || rn.skyMins[1][side] >= rn.skyMaxs[1][side] ) return; RB_BindShader( rsc.worldent, rf.envShader, rn.skyFog ); RB_BindVBO( skydome->linearVbos[side]->index, GL_TRIANGLES ); RB_DrawElements( visSide->firstVert, visSide->numVerts, visSide->firstElem, visSide->numElems ); }
/* * R_DrawBlackBottom * * Draw dummy skybox side to prevent the HOM effect */ static void R_DrawBlackBottom( const skydome_t *skydome, const visSkySide_t *visSides, const mfog_t *fog, drawSurfaceSky_t *drawSurf ) { int side = 5; const visSkySide_t *visSide = visSides + side; if( drawSurf->skyMins[0][side] >= drawSurf->skyMaxs[0][side] || drawSurf->skyMins[1][side] >= drawSurf->skyMaxs[1][side] ) { return; } RB_BindShader( rsc.skyent, rsh.envShader, fog ); RB_BindVBO( skydome->linearVbos[side]->index, GL_TRIANGLES ); RB_DrawElements( visSide->firstVert, visSide->numVerts, visSide->firstElem, visSide->numElems, 0, 0, 0, 0 ); }
/* * R_DrawSkyBoxSide */ static void R_DrawSkyBoxSide( const skydome_t *skydome, const visSkySide_t *visSide, const shader_t *skyShader, const shader_t *skyboxShader, const mfog_t *fog, int imageIndex, drawSurfaceSky_t *drawSurf ) { int side = visSide->index; if( drawSurf->skyMins[0][side] >= drawSurf->skyMaxs[0][side] || drawSurf->skyMins[1][side] >= drawSurf->skyMaxs[1][side] ) { return; } RB_BindShader( rsc.skyent, skyShader, fog ); RB_BindVBO( skydome->linearVbos[side]->index, GL_TRIANGLES ); RB_SetSkyboxShader( skyboxShader ); RB_SetSkyboxSide( imageIndex ); RB_DrawElements( visSide->firstVert, visSide->numVerts, visSide->firstElem, visSide->numElems, 0, 0, 0, 0 ); }
/* * R_DrawSkyBoxSide */ static void R_DrawSkyBoxSide( const skydome_t *skydome, const visSkySide_t *visSide, const shader_t *shader, int imageIndex ) { int side = visSide->index; if( rn.skyMins[0][side] >= rn.skyMaxs[0][side] || rn.skyMins[1][side] >= rn.skyMaxs[1][side] ) return; RB_BindShader( rsc.worldent, rf.skyShader, rn.skyFog ); RB_BindVBO( skydome->linearVbos[side]->index, GL_TRIANGLES ); RB_SetSkyboxShader( shader ); RB_SetSkyboxSide( imageIndex ); RB_DrawElements( visSide->firstVert, visSide->numVerts, visSide->firstElem, visSide->numElems ); }
/* * R_RenderDebugBounds */ static void R_RenderDebugBounds( void ) { int i, j; vec3_t corner; const vec_t *mins, *maxs; mesh_t *rb_mesh; elem_t elems[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; if( !r_num_debug_bounds ) return; RB_EnableTriangleOutlines( qtrue ); RB_BindShader( rsc.worldent, rsh.whiteShader, NULL ); RB_BindVBO( RB_VBO_STREAM, GL_TRIANGLE_STRIP ); for( i = 0; i < r_num_debug_bounds; i++ ) { mins = r_debug_bounds[i].mins; maxs = r_debug_bounds[i].maxs; rb_mesh = RB_MapBatchMesh( 8, 8 ); for( j = 0; j < 8; j++ ) { corner[0] = ( ( j & 1 ) ? mins[0] : maxs[0] ); corner[1] = ( ( j & 2 ) ? mins[1] : maxs[1] ); corner[2] = ( ( j & 4 ) ? mins[2] : maxs[2] ); VectorCopy( corner, rb_mesh->xyzArray[j] ); } rb_mesh->numVerts = 8; rb_mesh->numElems = 8; rb_mesh->elems = elems; RB_UploadMesh( rb_mesh ); RB_EndBatch(); } RB_EnableTriangleOutlines( qfalse ); }
/* * RB_FlushDynamicMeshes */ void RB_FlushDynamicMeshes( void ) { int i, numDraws = rb.numDynamicDraws; rbDynamicStream_t *stream; rbDynamicDraw_t *draw; int sx, sy, sw, sh; float offsetx = 0.0f, offsety = 0.0f, transx, transy; mat4_t m; if( !numDraws ) { return; } for( i = 0; i < RB_VBO_NUM_STREAMS; i++ ) { stream = &rb.dynamicStreams[i]; // R_UploadVBO* are going to rebind buffer arrays for upload // so update our local VBO state cache by calling RB_BindVBO RB_BindVBO( -i - 1, GL_TRIANGLES ); // dummy value for primitive here // because of firstVert, upload elems first if( stream->drawElements.numElems ) { mesh_t elemMesh; memset( &elemMesh, 0, sizeof( elemMesh ) ); elemMesh.elems = dynamicStreamElems[i] + stream->drawElements.firstElem; elemMesh.numElems = stream->drawElements.numElems; R_UploadVBOElemData( stream->vbo, 0, stream->drawElements.firstElem, &elemMesh ); stream->drawElements.firstElem += stream->drawElements.numElems; stream->drawElements.numElems = 0; } if( stream->drawElements.numVerts ) { R_UploadVBOVertexRawData( stream->vbo, stream->drawElements.firstVert, stream->drawElements.numVerts, stream->vertexData + stream->drawElements.firstVert * stream->vbo->vertexSize ); stream->drawElements.firstVert += stream->drawElements.numVerts; stream->drawElements.numVerts = 0; } } RB_GetScissor( &sx, &sy, &sw, &sh ); Matrix4_Copy( rb.objectMatrix, m ); transx = m[12]; transy = m[13]; for( i = 0, draw = rb.dynamicDraws; i < numDraws; i++, draw++ ) { RB_BindShader( draw->entity, draw->shader, draw->fog ); RB_BindVBO( draw->streamId, draw->primitive ); RB_SetPortalSurface( draw->portalSurface ); RB_SetShadowBits( draw->shadowBits ); RB_Scissor( draw->scissor[0], draw->scissor[1], draw->scissor[2], draw->scissor[3] ); // translate the mesh in 2D if( ( offsetx != draw->offset[0] ) || ( offsety != draw->offset[1] ) ) { offsetx = draw->offset[0]; offsety = draw->offset[1]; m[12] = transx + offsetx; m[13] = transy + offsety; RB_LoadObjectMatrix( m ); } RB_DrawElements( draw->drawElements.firstVert, draw->drawElements.numVerts, draw->drawElements.firstElem, draw->drawElements.numElems, draw->drawElements.firstVert, draw->drawElements.numVerts, draw->drawElements.firstElem, draw->drawElements.numElems ); } rb.numDynamicDraws = 0; RB_Scissor( sx, sy, sw, sh ); // restore the original translation in the object matrix if it has been changed if( offsetx || offsety ) { m[12] = transx; m[13] = transy; RB_LoadObjectMatrix( m ); } }
/* * RB_AddDynamicMesh */ void RB_AddDynamicMesh( const entity_t *entity, const shader_t *shader, const struct mfog_s *fog, const struct portalSurface_s *portalSurface, unsigned int shadowBits, const struct mesh_s *mesh, int primitive, float x_offset, float y_offset ) { int numVerts = mesh->numVerts, numElems = mesh->numElems; bool trifan = false; int scissor[4]; rbDynamicDraw_t *prev = NULL, *draw; bool merge = false; vattribmask_t vattribs; int streamId = RB_VBO_NONE; rbDynamicStream_t *stream; int destVertOffset; elem_t *destElems; // can't (and shouldn't because that would break batching) merge strip draw calls // (consider simply disabling merge later in this case if models with tristrips are added in the future, but that's slow) assert( ( primitive == GL_TRIANGLES ) || ( primitive == GL_LINES ) ); if( !numElems ) { numElems = ( max( numVerts, 2 ) - 2 ) * 3; trifan = true; } if( !numVerts || !numElems || ( numVerts > MAX_STREAM_VBO_VERTS ) || ( numElems > MAX_STREAM_VBO_ELEMENTS ) ) { return; } RB_GetScissor( &scissor[0], &scissor[1], &scissor[2], &scissor[3] ); if( rb.numDynamicDraws ) { prev = &rb.dynamicDraws[rb.numDynamicDraws - 1]; } if( prev ) { int prevRenderFX = 0, renderFX = 0; if( prev->entity ) { prevRenderFX = prev->entity->renderfx; } if( entity ) { renderFX = entity->renderfx; } if( ( ( shader->flags & SHADER_ENTITY_MERGABLE ) || ( prev->entity == entity ) ) && ( prevRenderFX == renderFX ) && ( prev->shader == shader ) && ( prev->fog == fog ) && ( prev->portalSurface == portalSurface ) && ( ( prev->shadowBits && shadowBits ) || ( !prev->shadowBits && !shadowBits ) ) ) { // don't rebind the shader to get the VBO in this case streamId = prev->streamId; if( ( prev->shadowBits == shadowBits ) && ( prev->primitive == primitive ) && ( prev->offset[0] == x_offset ) && ( prev->offset[1] == y_offset ) && !memcmp( prev->scissor, scissor, sizeof( scissor ) ) ) { merge = true; } } } if( streamId == RB_VBO_NONE ) { RB_BindShader( entity, shader, fog ); vattribs = rb.currentVAttribs; streamId = ( ( vattribs & ~COMPACT_STREAM_VATTRIBS ) ? RB_VBO_STREAM : RB_VBO_STREAM_COMPACT ); } else { vattribs = prev->vattribs; } stream = &rb.dynamicStreams[-streamId - 1]; if( ( !merge && ( ( rb.numDynamicDraws + 1 ) > MAX_DYNAMIC_DRAWS ) ) || ( ( stream->drawElements.firstVert + stream->drawElements.numVerts + numVerts ) > MAX_STREAM_VBO_VERTS ) || ( ( stream->drawElements.firstElem + stream->drawElements.numElems + numElems ) > MAX_STREAM_VBO_ELEMENTS ) ) { // wrap if overflows RB_FlushDynamicMeshes(); stream->drawElements.firstVert = 0; stream->drawElements.numVerts = 0; stream->drawElements.firstElem = 0; stream->drawElements.numElems = 0; merge = false; } if( merge ) { // merge continuous draw calls draw = prev; draw->drawElements.numVerts += numVerts; draw->drawElements.numElems += numElems; } else { draw = &rb.dynamicDraws[rb.numDynamicDraws++]; draw->entity = entity; draw->shader = shader; draw->fog = fog; draw->portalSurface = portalSurface; draw->shadowBits = shadowBits; draw->vattribs = vattribs; draw->streamId = streamId; draw->primitive = primitive; draw->offset[0] = x_offset; draw->offset[1] = y_offset; memcpy( draw->scissor, scissor, sizeof( scissor ) ); draw->drawElements.firstVert = stream->drawElements.firstVert + stream->drawElements.numVerts; draw->drawElements.numVerts = numVerts; draw->drawElements.firstElem = stream->drawElements.firstElem + stream->drawElements.numElems; draw->drawElements.numElems = numElems; draw->drawElements.numInstances = 0; } destVertOffset = stream->drawElements.firstVert + stream->drawElements.numVerts; R_FillVBOVertexDataBuffer( stream->vbo, vattribs, mesh, stream->vertexData + destVertOffset * stream->vbo->vertexSize ); destElems = dynamicStreamElems[-streamId - 1] + stream->drawElements.firstElem + stream->drawElements.numElems; if( trifan ) { R_BuildTrifanElements( destVertOffset, numElems, destElems ); } else { if( primitive == GL_TRIANGLES ) { R_CopyOffsetTriangles( mesh->elems, numElems, destVertOffset, destElems ); } else { R_CopyOffsetElements( mesh->elems, numElems, destVertOffset, destElems ); } } stream->drawElements.numVerts += numVerts; stream->drawElements.numElems += numElems; }
/* * R_DrawSkySurf */ void R_DrawSkySurf( const entity_t *e, const shader_t *shader, const mfog_t *fog, const portalSurface_t *portalSurface, unsigned int shadowBits, drawSurfaceSky_t *drawSurf ) { int i; int numVisSides; visSkySide_t visSkySides[6]; vec3_t mins, maxs; int umin, umax, vmin, vmax; bool skyportal = portalSurface != NULL && portalSurface->skyPortal; skydome_t *skydome = rsh.worldBrushModel->skydome; if( !skydome ) { return; } if( skyportal && !fog ) { return; } numVisSides = 0; ClearBounds( mins, maxs ); memset( visSkySides, 0, sizeof( visSkySides ) ); for( i = 0; i < 6; i++ ) { if( drawSurf->skyMins[0][i] >= drawSurf->skyMaxs[0][i] || drawSurf->skyMins[1][i] >= drawSurf->skyMaxs[1][i] ) { continue; } // increase the visible sides counter numVisSides++; umin = (int)( ( drawSurf->skyMins[0][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE - 1 ) ); umax = (int)( ( drawSurf->skyMaxs[0][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE - 1 ) ) + 1; vmin = (int)( ( drawSurf->skyMins[1][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE - 1 ) ); vmax = (int)( ( drawSurf->skyMaxs[1][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE - 1 ) ) + 1; clamp( umin, 0, SIDE_SIZE - 1 ); clamp( umax, 0, SIDE_SIZE - 1 ); clamp( vmin, 0, SIDE_SIZE - 1 ); clamp( vmax, 0, SIDE_SIZE - 1 ); visSkySides[i].index = i; visSkySides[i].firstVert = vmin * SIDE_SIZE + umin; visSkySides[i].numVerts = ( vmax - vmin ) * SIDE_SIZE + ( umax - umin ) + 1; visSkySides[i].firstElem = ( vmin * ( SIDE_SIZE - 2 ) + umin ) * 6; visSkySides[i].numElems = ( ( vmax - vmin ) * ( SIDE_SIZE - 2 ) + ( umax - umin ) ) * 6; clamp( visSkySides[i].firstVert, 0, POINTS_LEN - 1 ); clamp( visSkySides[i].numVerts, 0, POINTS_LEN ); clamp( visSkySides[i].firstElem, 0, ELEM_LEN - 1 ); clamp( visSkySides[i].numElems, 0, ELEM_LEN ); skydome->meshes[i].numElems = visSkySides[i].numElems; } // no sides are truly visible, ignore if( !numVisSides ) { return; } // center skydome on camera to give the illusion of a larger space rsc.skyent->scale = shader->skyHeight; VectorCopy( rn.viewOrigin, rsc.skyent->origin ); R_TransformForEntity( rsc.skyent ); if( skyportal ) { // render fake fogged skybox R_DrawSkyBox( skydome, visSkySides, rsh.emptyFogShader, shader, fog, drawSurf ); } else { if( shader->skyboxImages[0] ) { R_DrawSkyBox( skydome, visSkySides, rsh.skyShader, shader, fog, drawSurf ); } else { R_DrawBlackBottom( skydome, visSkySides, fog, drawSurf ); } if( shader->numpasses ) { for( i = 0; i < 5; i++ ) { const visSkySide_t *visSide = visSkySides + i; if( drawSurf->skyMins[0][i] >= drawSurf->skyMaxs[0][i] || drawSurf->skyMins[1][i] >= drawSurf->skyMaxs[1][i] ) { continue; } RB_BindShader( rsc.skyent, shader, NULL ); // must be called for every side to reset backend state RB_BindVBO( skydome->sphereVbos[i]->index, GL_TRIANGLES ); RB_DrawElements( visSide->firstVert, visSide->numVerts, visSide->firstElem, visSide->numElems, 0, 0, 0, 0 ); } } } R_TransformForEntity( e ); }
/* * R_BlitTextureToScrFbo */ static void R_BlitTextureToScrFbo( const refdef_t *fd, image_t *image, int dstFbo, int program_type, const vec4_t color, int blendMask, int numShaderImages, image_t **shaderImages ) { int x, y; int w, h, fw, fh; static char s_name[] = "$builtinpostprocessing"; static shaderpass_t p; static shader_t s; int i; static tcmod_t tcmod; mat4_t m; assert( rsh.postProcessingVBO ); // blit + flip using a static mesh to avoid redundant buffer uploads // (also using custom PP effects like FXAA with the stream VBO causes // Adreno to mark the VBO as "slow" (due to some weird bug) // for the rest of the frame and drop FPS to 10-20). RB_FlushDynamicMeshes(); RB_BindFrameBufferObject( dstFbo ); if( !dstFbo ) { // default framebuffer // set the viewport to full resolution // but keep the scissoring region x = fd->x; y = fd->y; w = fw = fd->width; h = fh = fd->height; RB_Viewport( 0, 0, glConfig.width, glConfig.height ); RB_Scissor( rn.scissor[0], rn.scissor[1], rn.scissor[2], rn.scissor[3] ); } else { // aux framebuffer // set the viewport to full resolution of the framebuffer (without the NPOT padding if there's one) // draw quad on the whole framebuffer texture // set scissor to default framebuffer resolution image_t *cb = RFB_GetObjectTextureAttachment( dstFbo, false ); x = 0; y = 0; w = fw = rf.frameBufferWidth; h = fh = rf.frameBufferHeight; if( cb ) { fw = cb->upload_width; fh = cb->upload_height; } RB_Viewport( 0, 0, w, h ); RB_Scissor( 0, 0, glConfig.width, glConfig.height ); } s.vattribs = VATTRIB_POSITION_BIT|VATTRIB_TEXCOORDS_BIT; s.sort = SHADER_SORT_NEAREST; s.numpasses = 1; s.name = s_name; s.passes = &p; p.rgbgen.type = RGB_GEN_IDENTITY; p.alphagen.type = ALPHA_GEN_IDENTITY; p.tcgen = TC_GEN_NONE; p.images[0] = image; for( i = 0; i < numShaderImages; i++ ) p.images[i + 1] = shaderImages[i]; p.flags = blendMask; p.program_type = program_type; if( !dstFbo ) { tcmod.type = TC_MOD_TRANSFORM; tcmod.args[0] = ( float )( w ) / ( float )( image->upload_width ); tcmod.args[1] = ( float )( h ) / ( float )( image->upload_height ); tcmod.args[4] = ( float )( x ) / ( float )( image->upload_width ); tcmod.args[5] = ( float )( image->upload_height - h - y ) / ( float )( image->upload_height ); p.numtcmods = 1; p.tcmods = &tcmod; } else { p.numtcmods = 0; } Matrix4_Identity( m ); Matrix4_Scale2D( m, fw, fh ); Matrix4_Translate2D( m, x, y ); RB_LoadObjectMatrix( m ); RB_BindShader( NULL, &s, NULL ); RB_BindVBO( rsh.postProcessingVBO->index, GL_TRIANGLES ); RB_DrawElements( 0, 4, 0, 6, 0, 0, 0, 0 ); RB_LoadObjectMatrix( mat4x4_identity ); // restore 2D viewport and scissor RB_Viewport( 0, 0, rf.frameBufferWidth, rf.frameBufferHeight ); RB_Scissor( 0, 0, rf.frameBufferWidth, rf.frameBufferHeight ); }
/* * R_DrawSkySurf */ qboolean R_DrawSkySurf( const entity_t *e, const shader_t *shader, const mfog_t *fog, drawSurfaceBSP_t *drawSurf ) { int i; int numVisSides; visSkySide_t visSkySides[6]; vec3_t mins, maxs; int umin, umax, vmin, vmax; entity_t skyent; refdef_t *rd = &rn.refdef; skydome_t *skydome = r_worldbrushmodel->skydome; if( !skydome ) return qfalse; numVisSides = 0; ClearBounds( mins, maxs ); memset( visSkySides, 0, sizeof( visSkySides ) ); for( i = 0; i < 6; i++ ) { if( rn.skyMins[0][i] >= rn.skyMaxs[0][i] || rn.skyMins[1][i] >= rn.skyMaxs[1][i] ) continue; // increase the visible sides counter numVisSides++; umin = (int)( ( rn.skyMins[0][i]+1.0f )*0.5f*(float)( SIDE_SIZE-1 ) ); umax = (int)( ( rn.skyMaxs[0][i]+1.0f )*0.5f*(float)( SIDE_SIZE-1 ) ) + 1; vmin = (int)( ( rn.skyMins[1][i]+1.0f )*0.5f*(float)( SIDE_SIZE-1 ) ); vmax = (int)( ( rn.skyMaxs[1][i]+1.0f )*0.5f*(float)( SIDE_SIZE-1 ) ) + 1; clamp( umin, 0, SIDE_SIZE-1 ); clamp( umax, 0, SIDE_SIZE-1 ); clamp( vmin, 0, SIDE_SIZE-1 ); clamp( vmax, 0, SIDE_SIZE-1 ); visSkySides[i].index = i; visSkySides[i].firstVert = vmin * SIDE_SIZE + umin; visSkySides[i].numVerts = (vmax - vmin) * SIDE_SIZE + (umax - umin); visSkySides[i].firstElem = (vmin * (SIDE_SIZE-1) + umin) * 6; visSkySides[i].numElems = ((vmax - vmin) * (SIDE_SIZE-1) + (umax - umin)) * 6; AddPointToBounds( skydome->meshes[i].xyzArray[vmin*SIDE_SIZE+umin], mins, maxs ); AddPointToBounds( skydome->meshes[i].xyzArray[vmax*SIDE_SIZE+umax], mins, maxs ); skydome->meshes[i].numElems = visSkySides[i].numElems; } // no sides are truly visible, ignore if( !numVisSides ) return qfalse; VectorAdd( mins, rn.viewOrigin, mins ); VectorAdd( maxs, rn.viewOrigin, maxs ); if( rd->rdflags & RDF_SKYPORTALINVIEW ) { R_DrawSkyPortal( e, &rd->skyportal, mins, maxs ); return qfalse; } // center skydome on camera to give the illusion of a larger space skyent = *rsc.worldent; skyent.scale = shader->skyHeight; VectorCopy( rn.viewOrigin, skyent.origin ); R_TransformForEntity( &skyent ); if( shader->skyboxImages[0] ) R_DrawSkyBox( skydome, visSkySides, shader ); else R_DrawBlackBottom( skydome, visSkySides ); if( shader->numpasses ) { RB_BindShader( rsc.worldent, shader, rn.skyFog ); for( i = 0; i < 5; i++ ) { const visSkySide_t *visSide = visSkySides + i; if( rn.skyMins[0][i] >= rn.skyMaxs[0][i] || rn.skyMins[1][i] >= rn.skyMaxs[1][i] ) continue; RB_BindVBO( skydome->sphereVbos[i]->index, GL_TRIANGLES ); RB_DrawElements( visSide->firstVert, visSide->numVerts, visSide->firstElem, visSide->numElems ); } } R_TransformForEntity( e ); return qfalse; }