// r_main.c
#include "quakedef.h"
-#include "cl_dyntexture.h"
#include "r_shadow.h"
#include "polygon.h"
#include "image.h"
cvar_t r_usedepthtextures = {CVAR_SAVE, "r_usedepthtextures", "1", "use depth texture instead of depth renderbuffer where possible, uses less video memory but may render slower (or faster) depending on hardware"};
cvar_t r_viewfbo = {CVAR_SAVE, "r_viewfbo", "0", "enables use of an 8bit (1) or 16bit (2) or 32bit (3) per component float framebuffer render, which may be at a different resolution than the video mode"};
+cvar_t r_rendertarget_debug = {0, "r_rendertarget_debug", "-1", "replaces the view with the contents of the specified render target (by number - note that these can fluctuate depending on scene)"};
cvar_t r_viewscale = {CVAR_SAVE, "r_viewscale", "1", "scaling factor for resolution of the fbo rendering method, must be > 0, can be above 1 for a costly antialiasing behavior, typical values are 0.5 for 1/4th as many pixels rendered, or 1 for normal rendering"};
cvar_t r_viewscale_fpsscaling = {CVAR_SAVE, "r_viewscale_fpsscaling", "0", "change resolution based on framerate"};
cvar_t r_viewscale_fpsscaling_min = {CVAR_SAVE, "r_viewscale_fpsscaling_min", "0.0625", "worst acceptable quality"};
cvar_t r_water_scissormode = {0, "r_water_scissormode", "3", "scissor (1) or cull (2) or both (3) water renders"};
cvar_t r_water_lowquality = {0, "r_water_lowquality", "0", "special option to accelerate water rendering, 1 disables shadows and particles, 2 disables all dynamic lights"};
cvar_t r_water_hideplayer = {CVAR_SAVE, "r_water_hideplayer", "0", "if set to 1 then player will be hidden in refraction views, if set to 2 then player will also be hidden in reflection views, player is always visible in camera views"};
-cvar_t r_water_fbo = {CVAR_SAVE, "r_water_fbo", "1", "enables use of render to texture for water effects, otherwise copy to texture is used (slower)"};
cvar_t r_lerpsprites = {CVAR_SAVE, "r_lerpsprites", "0", "enables animation smoothing on sprites"};
cvar_t r_lerpmodels = {CVAR_SAVE, "r_lerpmodels", "1", "enables animation smoothing on models"};
{CVAR_SAVE, "r_buffermegs_uniform", "0.25", "uniform buffer size for one frame"},
};
-extern cvar_t v_glslgamma;
extern cvar_t v_glslgamma_2d;
extern qboolean v_flipped_state;
/// vertex coordinates for a quad that covers the screen exactly
extern const float r_screenvertex3f[12];
-extern const float r_d3dscreenvertex3f[12];
const float r_screenvertex3f[12] =
{
0, 0, 0,
1, 1, 0,
0, 1, 0
};
-const float r_d3dscreenvertex3f[12] =
-{
- 0, 1, 0,
- 1, 1, 0,
- 1, 0, 0,
- 0, 0, 0
-};
void R_ModulateColors(float *in, float *out, int verts, float r, float g, float b)
{
permutation |= SHADERPERMUTATION_GLOW;
else if (texturemode == GL_DECAL)
permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
- if (usegamma && v_glslgamma.integer && v_glslgamma_2d.integer && !vid.sRGB2D && r_texture_gammaramps && !vid_gammatables_trivial)
+ if (usegamma && v_glslgamma_2d.integer && !vid.sRGB2D && r_texture_gammaramps && !vid_gammatables_trivial)
permutation |= SHADERPERMUTATION_GAMMARAMPS;
if (suppresstexalpha)
permutation |= SHADERPERMUTATION_REFLECTCUBE;
R_Mesh_TexBind(GL20TU_FIRST , first );
R_Mesh_TexBind(GL20TU_SECOND, second);
if (permutation & SHADERPERMUTATION_GAMMARAMPS)
- R_Mesh_TexBind(r_glsl_permutation->tex_Texture_GammaRamps, r_texture_gammaramps);
+ R_Mesh_TexBind(GL20TU_GAMMARAMPS, r_texture_gammaramps);
#endif
break;
case RENDERPATH_D3D10:
}
}
-extern qboolean r_shadow_usingdeferredprepass;
-extern rtexture_t *r_shadow_attenuationgradienttexture;
-extern rtexture_t *r_shadow_attenuation2dtexture;
-extern rtexture_t *r_shadow_attenuation3dtexture;
-extern qboolean r_shadow_usingshadowmap2d;
-extern qboolean r_shadow_usingshadowmaportho;
-extern float r_shadow_modelshadowmap_texturescale[4];
-extern float r_shadow_modelshadowmap_parameters[4];
-extern float r_shadow_lightshadowmap_texturescale[4];
-extern float r_shadow_lightshadowmap_parameters[4];
-extern qboolean r_shadow_shadowmapvsdct;
-extern rtexture_t *r_shadow_shadowmap2ddepthbuffer;
-extern rtexture_t *r_shadow_shadowmap2ddepthtexture;
-extern rtexture_t *r_shadow_shadowmapvsdcttexture;
-extern matrix4x4_t r_shadow_shadowmapmatrix;
-extern int r_shadow_prepass_width;
-extern int r_shadow_prepass_height;
-extern rtexture_t *r_shadow_prepassgeometrydepthbuffer;
-extern rtexture_t *r_shadow_prepassgeometrynormalmaptexture;
-extern rtexture_t *r_shadow_prepasslightingdiffusetexture;
-extern rtexture_t *r_shadow_prepasslightingspeculartexture;
-
#define BLENDFUNC_ALLOWS_COLORMOD 1
#define BLENDFUNC_ALLOWS_FOG 2
#define BLENDFUNC_ALLOWS_FOG_HACK0 4
if (rsurface.rtlight ) R_Mesh_TexBind(GL20TU_ATTENUATION , r_shadow_attenuationgradienttexture );
if (rsurfacepass == RSURFPASS_BACKGROUND)
{
- R_Mesh_TexBind(GL20TU_REFRACTION , waterplane->texture_refraction ? waterplane->texture_refraction : r_texture_black);
- if(mode == SHADERMODE_GENERIC) R_Mesh_TexBind(GL20TU_FIRST , waterplane->texture_camera ? waterplane->texture_camera : r_texture_black);
- R_Mesh_TexBind(GL20TU_REFLECTION , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
+ R_Mesh_TexBind(GL20TU_REFRACTION , waterplane->rt_refraction ? waterplane->rt_refraction->colortexture[0] : r_texture_black);
+ if(mode == SHADERMODE_GENERIC) R_Mesh_TexBind(GL20TU_FIRST , waterplane->rt_camera ? waterplane->rt_camera->colortexture[0] : r_texture_black);
+ R_Mesh_TexBind(GL20TU_REFLECTION , waterplane->rt_reflection ? waterplane->rt_reflection->colortexture[0] : r_texture_black);
}
else
{
- if (permutation & SHADERPERMUTATION_REFLECTION ) R_Mesh_TexBind(GL20TU_REFLECTION , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
+ if (permutation & SHADERPERMUTATION_REFLECTION ) R_Mesh_TexBind(GL20TU_REFLECTION , waterplane->rt_reflection ? waterplane->rt_reflection->colortexture[0] : r_texture_black);
}
// if (rsurfacepass == RSURFPASS_DEFERREDLIGHT ) R_Mesh_TexBind(GL20TU_SCREENNORMALMAP , r_shadow_prepassgeometrynormalmaptexture );
if (permutation & SHADERPERMUTATION_DEFERREDLIGHTMAP ) R_Mesh_TexBind(GL20TU_SCREENDIFFUSE , r_shadow_prepasslightingdiffusetexture );
if (r_glsl_permutation->tex_Texture_Attenuation >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Attenuation , r_shadow_attenuationgradienttexture );
if (rsurfacepass == RSURFPASS_BACKGROUND)
{
- if (r_glsl_permutation->tex_Texture_Refraction >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Refraction , waterplane->texture_refraction ? waterplane->texture_refraction : r_texture_black);
- if (r_glsl_permutation->tex_Texture_First >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , waterplane->texture_camera ? waterplane->texture_camera : r_texture_black);
- if (r_glsl_permutation->tex_Texture_Reflection >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Reflection , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
+ if (r_glsl_permutation->tex_Texture_Refraction >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Refraction , waterplane->rt_refraction ? waterplane->rt_refraction->colortexture[0] : r_texture_black);
+ if (r_glsl_permutation->tex_Texture_First >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , waterplane->rt_camera ? waterplane->rt_camera->colortexture[0] : r_texture_black);
+ if (r_glsl_permutation->tex_Texture_Reflection >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Reflection , waterplane->rt_reflection ? waterplane->rt_reflection->colortexture[0] : r_texture_black);
}
else
{
- if (r_glsl_permutation->tex_Texture_Reflection >= 0 && waterplane) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Reflection , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
+ if (r_glsl_permutation->tex_Texture_Reflection >= 0 && waterplane) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Reflection , waterplane->rt_reflection ? waterplane->rt_reflection->colortexture[0] : r_texture_black);
}
if (r_glsl_permutation->tex_Texture_ScreenNormalMap >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_ScreenNormalMap , r_shadow_prepassgeometrynormalmaptexture );
if (r_glsl_permutation->tex_Texture_ScreenDiffuse >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_ScreenDiffuse , r_shadow_prepasslightingdiffusetexture );
if (rsurface.rtlight ) R_Mesh_TexBind(GL20TU_ATTENUATION , r_shadow_attenuationgradienttexture );
if (rsurfacepass == RSURFPASS_BACKGROUND)
{
- R_Mesh_TexBind(GL20TU_REFRACTION , waterplane->texture_refraction ? waterplane->texture_refraction : r_texture_black);
- if(mode == SHADERMODE_GENERIC) R_Mesh_TexBind(GL20TU_FIRST , waterplane->texture_camera ? waterplane->texture_camera : r_texture_black);
- R_Mesh_TexBind(GL20TU_REFLECTION , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
+ R_Mesh_TexBind(GL20TU_REFRACTION , waterplane->rt_refraction ? waterplane->rt_refraction->colortexture[0] : r_texture_black);
+ if(mode == SHADERMODE_GENERIC) R_Mesh_TexBind(GL20TU_FIRST , waterplane->rt_camera ? waterplane->rt_camera->colortexture[0] : r_texture_black);
+ R_Mesh_TexBind(GL20TU_REFLECTION , waterplane->rt_reflection ? waterplane->rt_reflection->colortexture[0] : r_texture_black);
}
else
{
- if (permutation & SHADERPERMUTATION_REFLECTION ) R_Mesh_TexBind(GL20TU_REFLECTION , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
+ if (permutation & SHADERPERMUTATION_REFLECTION ) R_Mesh_TexBind(GL20TU_REFLECTION , waterplane->rt_reflection ? waterplane->rt_reflection->colortexture[0] : r_texture_black);
}
// if (rsurfacepass == RSURFPASS_DEFERREDLIGHT ) R_Mesh_TexBind(GL20TU_SCREENNORMALMAP , r_shadow_prepassgeometrynormalmaptexture );
if (permutation & SHADERPERMUTATION_DEFERREDLIGHTMAP ) R_Mesh_TexBind(GL20TU_SCREENDIFFUSE , r_shadow_prepasslightingdiffusetexture );
skinframe->loadsequence = r_skinframe.loadsequence;
}
+void R_SkinFrame_PurgeSkinFrame(skinframe_t *s)
+{
+ if (s->merged == s->base)
+ s->merged = NULL;
+ R_PurgeTexture(s->stain); s->stain = NULL;
+ R_PurgeTexture(s->merged); s->merged = NULL;
+ R_PurgeTexture(s->base); s->base = NULL;
+ R_PurgeTexture(s->pants); s->pants = NULL;
+ R_PurgeTexture(s->shirt); s->shirt = NULL;
+ R_PurgeTexture(s->nmap); s->nmap = NULL;
+ R_PurgeTexture(s->gloss); s->gloss = NULL;
+ R_PurgeTexture(s->glow); s->glow = NULL;
+ R_PurgeTexture(s->fog); s->fog = NULL;
+ R_PurgeTexture(s->reflect); s->reflect = NULL;
+ s->loadsequence = 0;
+}
+
void R_SkinFrame_Purge(void)
{
int i;
for (s = r_skinframe.hash[i];s;s = s->next)
{
if (s->loadsequence && s->loadsequence != r_skinframe.loadsequence)
- {
- if (s->merged == s->base)
- s->merged = NULL;
- // FIXME: maybe pass a pointer to the pointer to R_PurgeTexture and reset it to NULL inside? [11/29/2007 Black]
- R_PurgeTexture(s->stain );s->stain = NULL;
- R_PurgeTexture(s->merged);s->merged = NULL;
- R_PurgeTexture(s->base );s->base = NULL;
- R_PurgeTexture(s->pants );s->pants = NULL;
- R_PurgeTexture(s->shirt );s->shirt = NULL;
- R_PurgeTexture(s->nmap );s->nmap = NULL;
- R_PurgeTexture(s->gloss );s->gloss = NULL;
- R_PurgeTexture(s->glow );s->glow = NULL;
- R_PurgeTexture(s->fog );s->fog = NULL;
- R_PurgeTexture(s->reflect);s->reflect = NULL;
- s->loadsequence = 0;
- }
+ R_SkinFrame_PurgeSkinFrame(s);
}
}
}
if (!strcmp(item->basename, basename) && (comparecrc < 0 || (item->textureflags == textureflags && item->comparewidth == comparewidth && item->compareheight == compareheight && item->comparecrc == comparecrc)))
break;
- if (!item) {
- rtexture_t *dyntexture;
- // check whether its a dynamic texture
- dyntexture = CL_GetDynTexture( basename );
- if (!add && !dyntexture)
+ if (!item)
+ {
+ if (!add)
return NULL;
item = (skinframe_t *)Mem_ExpandableArray_AllocRecord(&r_skinframe.array);
memset(item, 0, sizeof(*item));
strlcpy(item->basename, basename, sizeof(item->basename));
- item->base = dyntexture; // either NULL or dyntexture handle
item->textureflags = textureflags & ~TEXF_FORCE_RELOAD;
item->comparewidth = comparewidth;
item->compareheight = compareheight;
}
else if (textureflags & TEXF_FORCE_RELOAD)
{
- rtexture_t *dyntexture;
- // check whether its a dynamic texture
- dyntexture = CL_GetDynTexture( basename );
- if (!add && !dyntexture)
+ if (!add)
return NULL;
- if (item->merged == item->base)
- item->merged = NULL;
- // FIXME: maybe pass a pointer to the pointer to R_PurgeTexture and reset it to NULL inside? [11/29/2007 Black]
- R_PurgeTexture(item->stain );item->stain = NULL;
- R_PurgeTexture(item->merged);item->merged = NULL;
- R_PurgeTexture(item->base );item->base = NULL;
- R_PurgeTexture(item->pants );item->pants = NULL;
- R_PurgeTexture(item->shirt );item->shirt = NULL;
- R_PurgeTexture(item->nmap );item->nmap = NULL;
- R_PurgeTexture(item->gloss );item->gloss = NULL;
- R_PurgeTexture(item->glow );item->glow = NULL;
- R_PurgeTexture(item->fog );item->fog = NULL;
- R_PurgeTexture(item->reflect);item->reflect = NULL;
- item->loadsequence = 0;
- }
- else if( item->base == NULL )
- {
- rtexture_t *dyntexture;
- // check whether its a dynamic texture
- // this only needs to be done because Purge doesnt delete skinframes - only sets the texture pointers to NULL and we need to restore it before returing.. [11/29/2007 Black]
- dyntexture = CL_GetDynTexture( basename );
- item->base = dyntexture; // either NULL or dyntexture handle
+ R_SkinFrame_PurgeSkinFrame(item);
}
R_SkinFrame_MarkUsed(item);
}
extern cvar_t gl_picmip;
-skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboolean complain)
+skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboolean complain, qboolean fallbacknotexture)
{
int j;
unsigned char *pixels;
if (!r_loaddds || !(ddsbase = R_LoadTextureDDSFile(r_main_texturepool, va(vabuf, sizeof(vabuf), "dds/%s.dds", basename), vid.sRGB3D, textureflags, &ddshasalpha, ddsavgcolor, miplevel, false)))
{
basepixels = loadimagepixelsbgra(name, complain, true, false, &miplevel);
+ if (basepixels == NULL && fallbacknotexture)
+ basepixels = Image_GenerateNoTexture();
if (basepixels == NULL)
return NULL;
}
Con_Printf("loading embedded 8bit image \"%s\"\n", name);
skinframe->base = skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_PALETTE, textureflags, -1, palette);
- if (textureflags & TEXF_ALPHA)
+ if ((textureflags & TEXF_ALPHA) && alphapalette)
{
for (i = 0;i < width * height;i++)
{
return skinframe;
}
+skinframe_t *R_SkinFrame_LoadNoTexture(void)
+{
+ int x, y;
+ static unsigned char pix[16][16][4];
+
+ if (cls.state == ca_dedicated)
+ return NULL;
+
+ // this makes a light grey/dark grey checkerboard texture
+ if (!pix[0][0][3])
+ {
+ for (y = 0; y < 16; y++)
+ {
+ for (x = 0; x < 16; x++)
+ {
+ if ((y < 8) ^ (x < 8))
+ {
+ pix[y][x][0] = 128;
+ pix[y][x][1] = 128;
+ pix[y][x][2] = 128;
+ pix[y][x][3] = 255;
+ }
+ else
+ {
+ pix[y][x][0] = 64;
+ pix[y][x][1] = 64;
+ pix[y][x][2] = 64;
+ pix[y][x][3] = 255;
+ }
+ }
+ }
+ }
+
+ return R_SkinFrame_LoadInternalBGRA("notexture", TEXF_FORCENEAREST, pix[0][0], 16, 16, false);
+}
+
+skinframe_t *R_SkinFrame_LoadInternalUsingTexture(const char *name, int textureflags, rtexture_t *tex, int width, int height, qboolean sRGB)
+{
+ skinframe_t *skinframe;
+ if (cls.state == ca_dedicated)
+ return NULL;
+ // if already loaded just return it, otherwise make a new skinframe
+ skinframe = R_SkinFrame_Find(name, textureflags, width, height, (textureflags & TEXF_FORCE_RELOAD) ? -1 : 0, true);
+ if (skinframe->base)
+ return skinframe;
+ textureflags &= ~TEXF_FORCE_RELOAD;
+ skinframe->stain = NULL;
+ skinframe->merged = NULL;
+ skinframe->base = NULL;
+ skinframe->pants = NULL;
+ skinframe->shirt = NULL;
+ skinframe->nmap = NULL;
+ skinframe->gloss = NULL;
+ skinframe->glow = NULL;
+ skinframe->fog = NULL;
+ skinframe->reflect = NULL;
+ skinframe->hasalpha = (textureflags & TEXF_ALPHA) != 0;
+ // if no data was provided, then clearly the caller wanted to get a blank skinframe
+ if (!tex)
+ return NULL;
+ if (developer_loading.integer)
+ Con_Printf("loading 32bit skin \"%s\"\n", name);
+ skinframe->base = skinframe->merged = tex;
+ Vector4Set(skinframe->avgcolor, 1, 1, 1, 1); // bogus placeholder
+ return skinframe;
+}
+
//static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
typedef struct suffixinfo_s
{
r_texture_gammaramps = NULL;
//r_texture_fogintensity = NULL;
memset(&r_fb, 0, sizeof(r_fb));
+ Mem_ExpandableArray_NewArray(&r_fb.rendertargets, r_main_mempool, sizeof(r_rendertarget_t), 128);
r_glsl_permutation = NULL;
memset(r_glsl_permutationhash, 0, sizeof(r_glsl_permutationhash));
Mem_ExpandableArray_NewArray(&r_glsl_permutationarray, r_main_mempool, sizeof(r_glsl_permutation_t), 256);
static void gl_main_shutdown(void)
{
+ R_RenderTarget_FreeUnused(true);
+ Mem_ExpandableArray_FreeArray(&r_fb.rendertargets);
R_AnimCache_Free();
R_FrameData_Reset();
R_BufferData_Reset();
Cvar_RegisterVariable(&gl_combine);
Cvar_RegisterVariable(&r_usedepthtextures);
Cvar_RegisterVariable(&r_viewfbo);
+ Cvar_RegisterVariable(&r_rendertarget_debug);
Cvar_RegisterVariable(&r_viewscale);
Cvar_RegisterVariable(&r_viewscale_fpsscaling);
Cvar_RegisterVariable(&r_viewscale_fpsscaling_min);
Cvar_RegisterVariable(&r_water_scissormode);
Cvar_RegisterVariable(&r_water_lowquality);
Cvar_RegisterVariable(&r_water_hideplayer);
- Cvar_RegisterVariable(&r_water_fbo);
Cvar_RegisterVariable(&r_lerpsprites);
Cvar_RegisterVariable(&r_lerpmodels);
*outheight = (int)ceil(height * scale);
}
-void R_SetupView(qboolean allowwaterclippingplane, int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
+void R_SetupView(qboolean allowwaterclippingplane, int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight)
{
const float *customclipplane = NULL;
float plane[4];
- int /*rtwidth,*/ rtheight, scaledwidth, scaledheight;
+ int /*rtwidth,*/ rtheight;
if (r_refdef.view.useclipplane && allowwaterclippingplane)
{
- // LordHavoc: couldn't figure out how to make this approach the
+ // LadyHavoc: couldn't figure out how to make this approach work the same in DPSOFTRAST
vec_t dist = r_refdef.view.clipplane.dist - r_water_clippingplanebias.value;
vec_t viewdist = DotProduct(r_refdef.view.origin, r_refdef.view.clipplane.normal);
if (viewdist < r_refdef.view.clipplane.dist + r_water_clippingplanebias.value)
if(vid.renderpath != RENDERPATH_SOFT) customclipplane = plane;
}
- //rtwidth = fbo ? R_TextureWidth(depthtexture ? depthtexture : colortexture) : vid.width;
- rtheight = fbo ? R_TextureHeight(depthtexture ? depthtexture : colortexture) : vid.height;
+ //rtwidth = viewfbo ? R_TextureWidth(viewdepthtexture ? viewdepthtexture : viewcolortexture) : vid.width;
+ rtheight = viewfbo ? R_TextureHeight(viewdepthtexture ? viewdepthtexture : viewcolortexture) : vid.height;
- R_GetScaledViewSize(r_refdef.view.width, r_refdef.view.height, &scaledwidth, &scaledheight);
if (!r_refdef.view.useperspective)
- R_Viewport_InitOrtho(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, rtheight - scaledheight - r_refdef.view.y, scaledwidth, scaledheight, -r_refdef.view.ortho_x, -r_refdef.view.ortho_y, r_refdef.view.ortho_x, r_refdef.view.ortho_y, -r_refdef.farclip, r_refdef.farclip, customclipplane);
+ R_Viewport_InitOrtho(&r_refdef.view.viewport, &r_refdef.view.matrix, viewx, rtheight - viewheight - viewy, viewwidth, viewheight, -r_refdef.view.ortho_x, -r_refdef.view.ortho_y, r_refdef.view.ortho_x, r_refdef.view.ortho_y, -r_refdef.farclip, r_refdef.farclip, customclipplane);
else if (vid.stencil && r_useinfinitefarclip.integer)
- R_Viewport_InitPerspectiveInfinite(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, rtheight - scaledheight - r_refdef.view.y, scaledwidth, scaledheight, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, customclipplane);
+ R_Viewport_InitPerspectiveInfinite(&r_refdef.view.viewport, &r_refdef.view.matrix, viewx, rtheight - viewheight - viewy, viewwidth, viewheight, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, customclipplane);
else
- R_Viewport_InitPerspective(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, rtheight - scaledheight - r_refdef.view.y, scaledwidth, scaledheight, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, r_refdef.farclip, customclipplane);
- R_Mesh_SetRenderTargets(fbo, depthtexture, colortexture, NULL, NULL, NULL);
+ R_Viewport_InitPerspective(&r_refdef.view.viewport, &r_refdef.view.matrix, viewx, rtheight - viewheight - viewy, viewwidth, viewheight, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, r_refdef.farclip, customclipplane);
+ R_Mesh_SetRenderTargets(viewfbo, viewdepthtexture, viewcolortexture, NULL, NULL, NULL);
R_SetViewport(&r_refdef.view.viewport);
if (r_refdef.view.useclipplane && allowwaterclippingplane && vid.renderpath == RENDERPATH_SOFT)
{
}
}
-void R_ResetViewRendering2D_Common(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, float x2, float y2)
+void R_ResetViewRendering2D_Common(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight, float x2, float y2)
{
r_viewport_t viewport;
CHECKGLERROR
// GL is weird because it's bottom to top, r_refdef.view.y is top to bottom
- R_Viewport_InitOrtho(&viewport, &identitymatrix, r_refdef.view.x, vid.height - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height, 0, 0, x2, y2, -10, 100, NULL);
- R_Mesh_SetRenderTargets(fbo, depthtexture, colortexture, NULL, NULL, NULL);
+ R_Viewport_InitOrtho(&viewport, &identitymatrix, viewx, vid.height - viewheight - viewy, viewwidth, viewheight, 0, 0, x2, y2, -10, 100, NULL);
+ R_Mesh_SetRenderTargets(viewfbo, viewdepthtexture, viewcolortexture, NULL, NULL, NULL);
R_SetViewport(&viewport);
GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height);
GL_Color(1, 1, 1, 1);
CHECKGLERROR
}
-void R_ResetViewRendering2D(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
+void R_ResetViewRendering2D(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight)
{
DrawQ_Finish();
- R_ResetViewRendering2D_Common(fbo, depthtexture, colortexture, 1, 1);
+ R_ResetViewRendering2D_Common(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight, 1.0f, 1.0f);
}
-void R_ResetViewRendering3D(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
+void R_ResetViewRendering3D(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight)
{
DrawQ_Finish();
- R_SetupView(true, fbo, depthtexture, colortexture);
+ R_SetupView(true, viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
GL_Scissor(r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
GL_Color(1, 1, 1, 1);
GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
Matrix4x4_Invert_Full(&r_refdef.view.inverse_matrix, &r_refdef.view.matrix);
}
-void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture);
-void R_RenderWaterPlanes(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture);
+void R_RenderTarget_FreeUnused(qboolean force)
+{
+ int i, j, end;
+ end = Mem_ExpandableArray_IndexRange(&r_fb.rendertargets);
+ for (i = 0; i < end; i++)
+ {
+ r_rendertarget_t *r = (r_rendertarget_t *)Mem_ExpandableArray_RecordAtIndex(&r_fb.rendertargets, i);
+ // free resources for rendertargets that have not been used for a while
+ // (note: this check is run after the frame render, so any targets used
+ // this frame will not be affected even at low framerates)
+ if (r && (realtime - r->lastusetime > 0.2 || force))
+ {
+ if (r->fbo)
+ R_Mesh_DestroyFramebufferObject(r->fbo);
+ for (j = 0; j < sizeof(r->colortexture) / sizeof(r->colortexture[0]); j++)
+ if (r->colortexture[j])
+ R_FreeTexture(r->colortexture[j]);
+ if (r->depthtexture)
+ R_FreeTexture(r->depthtexture);
+ Mem_ExpandableArray_FreeRecord(&r_fb.rendertargets, r);
+ }
+ }
+}
+
+static void R_CalcTexCoordsForView(float x, float y, float w, float h, float tw, float th, float *texcoord2f)
+{
+ float iw = 1.0f / tw, ih = 1.0f / th, x1, y1, x2, y2;
+ switch (vid.renderpath)
+ {
+ case RENDERPATH_D3D9:
+ x1 = (x + 0.5f) * iw;
+ x2 = (x + 0.5f + w) * iw;
+ y1 = (y + 0.5f) * ih;
+ y2 = (y + 0.5f + h) * ih;
+ break;
+ default:
+ x1 = x * iw;
+ x2 = (x + w) * iw;
+ y1 = (th - y) * ih;
+ y2 = (th - y - h) * ih;
+ break;
+ }
+ texcoord2f[0] = x1;
+ texcoord2f[2] = x2;
+ texcoord2f[4] = x2;
+ texcoord2f[6] = x1;
+ texcoord2f[1] = y1;
+ texcoord2f[3] = y1;
+ texcoord2f[5] = y2;
+ texcoord2f[7] = y2;
+}
+
+r_rendertarget_t *R_RenderTarget_Get(int texturewidth, int textureheight, textype_t depthtextype, qboolean depthisrenderbuffer, textype_t colortextype0, textype_t colortextype1, textype_t colortextype2, textype_t colortextype3)
+{
+ int i, j, end;
+ r_rendertarget_t *r = NULL;
+ char vabuf[256];
+ // first try to reuse an existing slot if possible
+ end = Mem_ExpandableArray_IndexRange(&r_fb.rendertargets);
+ for (i = 0; i < end; i++)
+ {
+ r = (r_rendertarget_t *)Mem_ExpandableArray_RecordAtIndex(&r_fb.rendertargets, i);
+ if (r && r->lastusetime != realtime && r->texturewidth == texturewidth && r->textureheight == textureheight && r->depthtextype == depthtextype && r->colortextype[0] == colortextype0 && r->colortextype[1] == colortextype1 && r->colortextype[2] == colortextype2 && r->colortextype[3] == colortextype3)
+ break;
+ }
+ if (i == end)
+ {
+ // no unused exact match found, so we have to make one in the first unused slot
+ r = (r_rendertarget_t *)Mem_ExpandableArray_AllocRecord(&r_fb.rendertargets);
+ r->texturewidth = texturewidth;
+ r->textureheight = textureheight;
+ r->colortextype[0] = colortextype0;
+ r->colortextype[1] = colortextype1;
+ r->colortextype[2] = colortextype2;
+ r->colortextype[3] = colortextype3;
+ r->depthtextype = depthtextype;
+ r->depthisrenderbuffer = depthisrenderbuffer;
+ for (j = 0; j < 4; j++)
+ if (r->colortextype[j])
+ r->colortexture[j] = R_LoadTexture2D(r_main_texturepool, va(vabuf, sizeof(vabuf), "rendertarget%i_%i_type%i", i, j, (int)r->colortextype[j]), r->texturewidth, r->textureheight, NULL, r->colortextype[j], TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
+ if (r->depthtextype)
+ {
+ if (r->depthisrenderbuffer)
+ r->depthtexture = R_LoadTextureRenderBuffer(r_main_texturepool, va(vabuf, sizeof(vabuf), "renderbuffer%i_depth_type%i", i, (int)r->depthtextype), r->texturewidth, r->textureheight, r->depthtextype);
+ else
+ r->depthtexture = R_LoadTexture2D(r_main_texturepool, va(vabuf, sizeof(vabuf), "rendertarget%i_depth_type%i", i, j, (int)r->depthtextype), r->texturewidth, r->textureheight, NULL, r->depthtextype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
+ }
+ r->fbo = R_Mesh_CreateFramebufferObject(r->depthtexture, r->colortexture[0], r->colortexture[1], r->colortexture[2], r->colortexture[3]);
+ }
+ r_refdef.stats[r_stat_rendertargets_used]++;
+ r_refdef.stats[r_stat_rendertargets_pixels] += r->texturewidth * r->textureheight;
+ r->lastusetime = realtime;
+ R_CalcTexCoordsForView(0, 0, r->texturewidth, r->textureheight, r->texturewidth, r->textureheight, r->texcoord2f);
+ return r;
+}
static void R_Water_StartFrame(void)
{
- int i;
- int waterwidth, waterheight, texturewidth, textureheight, camerawidth, cameraheight;
- r_waterstate_waterplane_t *p;
- qboolean usewaterfbo = (r_viewfbo.integer >= 1 || r_water_fbo.integer >= 1) && vid.support.ext_framebuffer_object && vid.support.arb_texture_non_power_of_two && vid.samples < 2;
+ int waterwidth, waterheight;
if (vid.width > (int)vid.maxtexturesize_2d || vid.height > (int)vid.maxtexturesize_2d)
return;
// set waterwidth and waterheight to the water resolution that will be
// used (often less than the screen resolution for faster rendering)
- R_GetScaledViewSize(bound(1, vid.width * r_water_resolutionmultiplier.value, vid.width), bound(1, vid.height * r_water_resolutionmultiplier.value, vid.height), &waterwidth, &waterheight);
+ waterwidth = (int)bound(1, r_refdef.view.width * r_water_resolutionmultiplier.value, r_refdef.view.width);
+ waterheight = (int)bound(1, r_refdef.view.height * r_water_resolutionmultiplier.value, r_refdef.view.height);
+ R_GetScaledViewSize(waterwidth, waterheight, &waterwidth, &waterheight);
- // calculate desired texture sizes
- // can't use water if the card does not support the texture size
if (!r_water.integer || r_showsurfaces.integer)
- texturewidth = textureheight = waterwidth = waterheight = camerawidth = cameraheight = 0;
- else if (vid.support.arb_texture_non_power_of_two)
- {
- texturewidth = waterwidth;
- textureheight = waterheight;
- camerawidth = waterwidth;
- cameraheight = waterheight;
- }
- else
- {
- for (texturewidth = 1;texturewidth < waterwidth ;texturewidth *= 2);
- for (textureheight = 1;textureheight < waterheight;textureheight *= 2);
- for (camerawidth = 1;camerawidth * 2 <= waterwidth ;camerawidth *= 2);
- for (cameraheight = 1;cameraheight * 2 <= waterheight;cameraheight *= 2);
- }
-
- // allocate textures as needed
- if (r_fb.water.texturewidth != texturewidth || r_fb.water.textureheight != textureheight || r_fb.water.camerawidth != camerawidth || r_fb.water.cameraheight != cameraheight || (r_fb.depthtexture && !usewaterfbo))
- {
- r_fb.water.maxwaterplanes = MAX_WATERPLANES;
- for (i = 0, p = r_fb.water.waterplanes;i < r_fb.water.maxwaterplanes;i++, p++)
- {
- if (p->texture_refraction)
- R_FreeTexture(p->texture_refraction);
- p->texture_refraction = NULL;
- if (p->fbo_refraction)
- R_Mesh_DestroyFramebufferObject(p->fbo_refraction);
- p->fbo_refraction = 0;
- if (p->texture_reflection)
- R_FreeTexture(p->texture_reflection);
- p->texture_reflection = NULL;
- if (p->fbo_reflection)
- R_Mesh_DestroyFramebufferObject(p->fbo_reflection);
- p->fbo_reflection = 0;
- if (p->texture_camera)
- R_FreeTexture(p->texture_camera);
- p->texture_camera = NULL;
- if (p->fbo_camera)
- R_Mesh_DestroyFramebufferObject(p->fbo_camera);
- p->fbo_camera = 0;
- }
- memset(&r_fb.water, 0, sizeof(r_fb.water));
- r_fb.water.texturewidth = texturewidth;
- r_fb.water.textureheight = textureheight;
- r_fb.water.camerawidth = camerawidth;
- r_fb.water.cameraheight = cameraheight;
- }
-
- if (r_fb.water.texturewidth)
- {
- int scaledwidth, scaledheight;
-
- r_fb.water.enabled = true;
-
- // water resolution is usually reduced
- r_fb.water.waterwidth = (int)bound(1, r_refdef.view.width * r_water_resolutionmultiplier.value, r_refdef.view.width);
- r_fb.water.waterheight = (int)bound(1, r_refdef.view.height * r_water_resolutionmultiplier.value, r_refdef.view.height);
- R_GetScaledViewSize(r_fb.water.waterwidth, r_fb.water.waterheight, &scaledwidth, &scaledheight);
-
- // set up variables that will be used in shader setup
- r_fb.water.screenscale[0] = 0.5f * (float)scaledwidth / (float)r_fb.water.texturewidth;
- r_fb.water.screenscale[1] = 0.5f * (float)scaledheight / (float)r_fb.water.textureheight;
- r_fb.water.screencenter[0] = 0.5f * (float)scaledwidth / (float)r_fb.water.texturewidth;
- r_fb.water.screencenter[1] = 0.5f * (float)scaledheight / (float)r_fb.water.textureheight;
- }
+ waterwidth = waterheight = 0;
+
+ // set up variables that will be used in shader setup
+ r_fb.water.waterwidth = waterwidth;
+ r_fb.water.waterheight = waterheight;
+ r_fb.water.texturewidth = waterwidth;
+ r_fb.water.textureheight = waterheight;
+ r_fb.water.camerawidth = waterwidth;
+ r_fb.water.cameraheight = waterheight;
+ r_fb.water.screenscale[0] = 0.5f;
+ r_fb.water.screenscale[1] = 0.5f;
+ r_fb.water.screencenter[0] = 0.5f;
+ r_fb.water.screencenter[1] = 0.5f;
+ r_fb.water.enabled = waterwidth != 0;
r_fb.water.maxwaterplanes = MAX_WATERPLANES;
r_fb.water.numwaterplanes = 0;
extern cvar_t r_drawparticles;
extern cvar_t r_drawdecals;
-static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
+static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, int viewx, int viewy, int viewwidth, int viewheight)
{
int myscissor[4];
r_refdef_view_t originalview;
int planeindex, qualityreduction = 0, old_r_dynamic = 0, old_r_shadows = 0, old_r_worldrtlight = 0, old_r_dlight = 0, old_r_particles = 0, old_r_decals = 0;
r_waterstate_waterplane_t *p;
vec3_t visorigin;
- qboolean usewaterfbo = (r_viewfbo.integer >= 1 || r_water_fbo.integer >= 1) && vid.support.ext_framebuffer_object && vid.support.arb_texture_non_power_of_two && vid.samples < 2;
- char vabuf[1024];
+ r_rendertarget_t *rt;
originalview = r_refdef.view;
}
}
- // make sure enough textures are allocated
- for (planeindex = 0, p = r_fb.water.waterplanes;planeindex < r_fb.water.numwaterplanes;planeindex++, p++)
+ for (planeindex = 0, p = r_fb.water.waterplanes; planeindex < r_fb.water.numwaterplanes; planeindex++, p++)
{
- if (r_water_cameraentitiesonly.value != 0 && !p->camera_entity)
- continue;
- if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
- {
- if (!p->texture_refraction)
- p->texture_refraction = R_LoadTexture2D(r_main_texturepool, va(vabuf, sizeof(vabuf), "waterplane%i_refraction", planeindex), r_fb.water.texturewidth, r_fb.water.textureheight, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
- if (!p->texture_refraction)
- goto error;
- if (usewaterfbo)
- {
- if (r_fb.water.depthtexture == NULL)
- r_fb.water.depthtexture = R_LoadTextureRenderBuffer(r_main_texturepool, "waterviewdepth", r_fb.water.texturewidth, r_fb.water.textureheight, TEXTYPE_DEPTHBUFFER24STENCIL8);
- if (p->fbo_refraction == 0)
- p->fbo_refraction = R_Mesh_CreateFramebufferObject(r_fb.water.depthtexture, p->texture_refraction, NULL, NULL, NULL);
- }
- }
- else if (p->materialflags & MATERIALFLAG_CAMERA)
- {
- if (!p->texture_camera)
- p->texture_camera = R_LoadTexture2D(r_main_texturepool, va(vabuf, sizeof(vabuf), "waterplane%i_camera", planeindex), r_fb.water.camerawidth, r_fb.water.cameraheight, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR, -1, NULL);
- if (!p->texture_camera)
- goto error;
- if (usewaterfbo)
- {
- if (r_fb.water.depthtexture == NULL)
- r_fb.water.depthtexture = R_LoadTextureRenderBuffer(r_main_texturepool, "waterviewdepth", r_fb.water.texturewidth, r_fb.water.textureheight, TEXTYPE_DEPTHBUFFER24STENCIL8);
- if (p->fbo_camera == 0)
- p->fbo_camera = R_Mesh_CreateFramebufferObject(r_fb.water.depthtexture, p->texture_camera, NULL, NULL, NULL);
- }
- }
-
- if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION))
- {
- if (!p->texture_reflection)
- p->texture_reflection = R_LoadTexture2D(r_main_texturepool, va(vabuf, sizeof(vabuf), "waterplane%i_reflection", planeindex), r_fb.water.texturewidth, r_fb.water.textureheight, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
- if (!p->texture_reflection)
- goto error;
- if (usewaterfbo)
- {
- if (r_fb.water.depthtexture == NULL)
- r_fb.water.depthtexture = R_LoadTextureRenderBuffer(r_main_texturepool, "waterviewdepth", r_fb.water.texturewidth, r_fb.water.textureheight, TEXTYPE_DEPTHBUFFER24STENCIL8);
- if (p->fbo_reflection == 0)
- p->fbo_reflection = R_Mesh_CreateFramebufferObject(r_fb.water.depthtexture, p->texture_reflection, NULL, NULL, NULL);
- }
- }
+ p->rt_reflection = NULL;
+ p->rt_refraction = NULL;
+ p->rt_camera = NULL;
}
// render views
{
if (r_water_cameraentitiesonly.value != 0 && !p->camera_entity)
continue;
+
if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION))
{
+ rt = R_RenderTarget_Get(r_fb.water.waterwidth, r_fb.water.waterheight, TEXTYPE_DEPTHBUFFER24STENCIL8, true, r_fb.rt_screen->colortextype[0], TEXTYPE_UNUSED, TEXTYPE_UNUSED, TEXTYPE_UNUSED);
+ if (rt->colortexture[0] == NULL || rt->depthtexture == NULL)
+ goto error;
r_refdef.view = myview;
+ Matrix4x4_Reflect(&r_refdef.view.matrix, p->plane.normal[0], p->plane.normal[1], p->plane.normal[2], p->plane.dist, -2);
+ Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, r_refdef.view.origin);
if(r_water_scissormode.integer)
{
- R_SetupView(true, p->fbo_reflection, r_fb.water.depthtexture, p->texture_reflection);
- if(R_ScissorForBBox(p->mins, p->maxs, myscissor))
- continue; // FIXME the plane then still may get rendered but with broken texture, but it sure won't be visible
+ R_SetupView(true, rt->fbo, rt->depthtexture, rt->colortexture[0], 0, 0, r_fb.water.waterwidth, r_fb.water.waterheight);
+ if (R_ScissorForBBox(p->mins, p->maxs, myscissor))
+ {
+ p->rt_reflection = NULL;
+ p->rt_refraction = NULL;
+ p->rt_camera = NULL;
+ continue;
+ }
}
- // render reflected scene and copy into texture
- Matrix4x4_Reflect(&r_refdef.view.matrix, p->plane.normal[0], p->plane.normal[1], p->plane.normal[2], p->plane.dist, -2);
- // update the r_refdef.view.origin because otherwise the sky renders at the wrong location (amongst other problems)
- Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, r_refdef.view.origin);
r_refdef.view.clipplane = p->plane;
// reverse the cullface settings for this render
r_refdef.view.cullface_front = GL_FRONT;
}
r_fb.water.hideplayer = ((r_water_hideplayer.integer >= 2) && !chase_active.integer);
- R_ResetViewRendering3D(p->fbo_reflection, r_fb.water.depthtexture, p->texture_reflection);
+ R_ResetViewRendering3D(rt->fbo, rt->depthtexture, rt->colortexture[0], 0, 0, rt->texturewidth, rt->textureheight);
+ GL_ScissorTest(false);
R_ClearScreen(r_refdef.fogenabled);
+ GL_ScissorTest(true);
if(r_water_scissormode.integer & 2)
R_View_UpdateWithScissor(myscissor);
else
R_AnimCache_CacheVisibleEntities();
if(r_water_scissormode.integer & 1)
GL_Scissor(myscissor[0], myscissor[1], myscissor[2], myscissor[3]);
- R_RenderScene(p->fbo_reflection, r_fb.water.depthtexture, p->texture_reflection);
+ R_RenderScene(rt->fbo, rt->depthtexture, rt->colortexture[0], 0, 0, rt->texturewidth, rt->textureheight);
- if (!p->fbo_reflection)
- R_Mesh_CopyToTexture(p->texture_reflection, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
r_fb.water.hideplayer = false;
+ p->rt_reflection = rt;
}
// render the normal view scene and copy into texture
// (except that a clipping plane should be used to hide everything on one side of the water, and the viewer's weapon model should be omitted)
if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
{
+ rt = R_RenderTarget_Get(r_fb.water.waterwidth, r_fb.water.waterheight, TEXTYPE_DEPTHBUFFER24STENCIL8, true, r_fb.rt_screen->colortextype[0], TEXTYPE_UNUSED, TEXTYPE_UNUSED, TEXTYPE_UNUSED);
+ if (rt->colortexture[0] == NULL || rt->depthtexture == NULL)
+ goto error;
r_refdef.view = myview;
if(r_water_scissormode.integer)
{
- R_SetupView(true, p->fbo_refraction, r_fb.water.depthtexture, p->texture_refraction);
- if(R_ScissorForBBox(p->mins, p->maxs, myscissor))
- continue; // FIXME the plane then still may get rendered but with broken texture, but it sure won't be visible
+ R_SetupView(true, rt->fbo, rt->depthtexture, rt->colortexture[0], 0, 0, r_fb.water.waterwidth, r_fb.water.waterheight);
+ if (R_ScissorForBBox(p->mins, p->maxs, myscissor))
+ {
+ p->rt_reflection = NULL;
+ p->rt_refraction = NULL;
+ p->rt_camera = NULL;
+ continue;
+ }
}
r_fb.water.hideplayer = ((r_water_hideplayer.integer >= 1) && !chase_active.integer);
PlaneClassify(&r_refdef.view.clipplane);
- R_ResetViewRendering3D(p->fbo_refraction, r_fb.water.depthtexture, p->texture_refraction);
+ R_ResetViewRendering3D(rt->fbo, rt->depthtexture, rt->colortexture[0], 0, 0, rt->texturewidth, rt->textureheight);
+ GL_ScissorTest(false);
R_ClearScreen(r_refdef.fogenabled);
+ GL_ScissorTest(true);
if(r_water_scissormode.integer & 2)
R_View_UpdateWithScissor(myscissor);
else
R_AnimCache_CacheVisibleEntities();
if(r_water_scissormode.integer & 1)
GL_Scissor(myscissor[0], myscissor[1], myscissor[2], myscissor[3]);
- R_RenderScene(p->fbo_refraction, r_fb.water.depthtexture, p->texture_refraction);
+ R_RenderScene(rt->fbo, rt->depthtexture, rt->colortexture[0], 0, 0, rt->texturewidth, rt->textureheight);
- if (!p->fbo_refraction)
- R_Mesh_CopyToTexture(p->texture_refraction, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
r_fb.water.hideplayer = false;
+ p->rt_refraction = rt;
}
else if (p->materialflags & MATERIALFLAG_CAMERA)
{
+ rt = R_RenderTarget_Get(r_fb.water.waterwidth, r_fb.water.waterheight, TEXTYPE_DEPTHBUFFER24STENCIL8, true, r_fb.rt_screen->colortextype[0], TEXTYPE_UNUSED, TEXTYPE_UNUSED, TEXTYPE_UNUSED);
+ if (rt->colortexture[0] == NULL || rt->depthtexture == NULL)
+ goto error;
r_refdef.view = myview;
r_refdef.view.clipplane = p->plane;
r_fb.water.hideplayer = false;
- R_ResetViewRendering3D(p->fbo_camera, r_fb.water.depthtexture, p->texture_camera);
+ R_ResetViewRendering3D(rt->fbo, rt->depthtexture, rt->colortexture[0], 0, 0, rt->texturewidth, rt->textureheight);
+ GL_ScissorTest(false);
R_ClearScreen(r_refdef.fogenabled);
+ GL_ScissorTest(true);
R_View_Update();
R_AnimCache_CacheVisibleEntities();
- R_RenderScene(p->fbo_camera, r_fb.water.depthtexture, p->texture_camera);
+ R_RenderScene(rt->fbo, rt->depthtexture, rt->colortexture[0], 0, 0, rt->texturewidth, rt->textureheight);
- if (!p->fbo_camera)
- R_Mesh_CopyToTexture(p->texture_camera, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
r_fb.water.hideplayer = false;
+ p->rt_camera = rt;
}
}
if(vid.renderpath==RENDERPATH_SOFT) DPSOFTRAST_ClipPlane(0, 0, 0, 1);
r_fb.water.renderingscene = false;
r_refdef.view = originalview;
- R_ResetViewRendering3D(fbo, depthtexture, colortexture);
- if (!r_fb.water.depthtexture)
- R_ClearScreen(r_refdef.fogenabled);
+ R_ResetViewRendering3D(fbo, depthtexture, colortexture, viewx, viewy, viewwidth, viewheight);
R_View_Update();
R_AnimCache_CacheVisibleEntities();
goto finish;
static void R_Bloom_StartFrame(void)
{
- int i;
int bloomtexturewidth, bloomtextureheight, screentexturewidth, screentextureheight;
int viewwidth, viewheight;
- qboolean useviewfbo = r_viewfbo.integer >= 1 && vid.support.ext_framebuffer_object && vid.support.arb_texture_non_power_of_two && vid.samples < 2;
textype_t textype = TEXTYPE_COLORBUFFER;
+ // clear the pointers to rendertargets from last frame as they're stale
+ r_fb.rt_screen = NULL;
+ r_fb.rt_bloom = NULL;
+
switch (vid.renderpath)
{
case RENDERPATH_GL20:
r_fb.usedepthtextures = r_usedepthtextures.integer != 0;
- if (vid.support.ext_framebuffer_object && vid.support.arb_texture_non_power_of_two)
- {
- if (r_viewfbo.integer == 2) textype = TEXTYPE_COLORBUFFER16F;
- if (r_viewfbo.integer == 3) textype = TEXTYPE_COLORBUFFER32F;
- }
+ if (r_viewfbo.integer == 2) textype = TEXTYPE_COLORBUFFER16F;
+ if (r_viewfbo.integer == 3) textype = TEXTYPE_COLORBUFFER32F;
+ // for simplicity, bloom requires FBO render to texture, which basically all video drivers support now
+ if (!vid.support.ext_framebuffer_object)
+ return;
break;
case RENDERPATH_GL11:
case RENDERPATH_GL13:
case RENDERPATH_GLES1:
+ return; // don't bother
case RENDERPATH_GLES2:
case RENDERPATH_D3D9:
case RENDERPATH_D3D10:
R_GetScaledViewSize(r_refdef.view.width, r_refdef.view.height, &viewwidth, &viewheight);
- switch(vid.renderpath)
- {
- case RENDERPATH_GL20:
- case RENDERPATH_D3D9:
- case RENDERPATH_D3D10:
- case RENDERPATH_D3D11:
- case RENDERPATH_SOFT:
- case RENDERPATH_GLES2:
- break;
- case RENDERPATH_GL11:
- case RENDERPATH_GL13:
- case RENDERPATH_GLES1:
- return;
- }
-
// set bloomwidth and bloomheight to the bloom resolution that will be
// used (often less than the screen resolution for faster rendering)
r_fb.bloomwidth = bound(1, r_bloom_resolution.integer, vid.width);
r_fb.bloomheight = bound(1, r_fb.bloomheight, (int)vid.maxtexturesize_2d);
// calculate desired texture sizes
- if (vid.support.arb_texture_non_power_of_two)
- {
- screentexturewidth = vid.width;
- screentextureheight = vid.height;
- bloomtexturewidth = r_fb.bloomwidth;
- bloomtextureheight = r_fb.bloomheight;
- }
- else
- {
- for (screentexturewidth = 1;screentexturewidth < vid.width ;screentexturewidth *= 2);
- for (screentextureheight = 1;screentextureheight < vid.height ;screentextureheight *= 2);
- for (bloomtexturewidth = 1;bloomtexturewidth < r_fb.bloomwidth ;bloomtexturewidth *= 2);
- for (bloomtextureheight = 1;bloomtextureheight < r_fb.bloomheight;bloomtextureheight *= 2);
- }
+ screentexturewidth = viewwidth;
+ screentextureheight = viewheight;
+ bloomtexturewidth = r_fb.bloomwidth;
+ bloomtextureheight = r_fb.bloomheight;
if ((r_bloom.integer || (!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0))) && ((r_bloom_resolution.integer < 4 || r_bloom_blur.value < 1 || r_bloom_blur.value >= 512) || r_refdef.view.width > (int)vid.maxtexturesize_2d || r_refdef.view.height > (int)vid.maxtexturesize_2d))
{
Cvar_SetValueQuick(&r_damageblur, 0);
}
- if (!((r_glsl_postprocess.integer || r_fxaa.integer) || (!R_Stereo_ColorMasking() && r_glsl_saturation.value != 1) || (v_glslgamma.integer && !vid_gammatables_trivial))
- && !r_bloom.integer
- && (R_Stereo_Active() || (r_motionblur.value <= 0 && r_damageblur.value <= 0))
- && !useviewfbo
- && r_viewscale.value == 1.0f
- && !r_viewscale_fpsscaling.integer)
- screentexturewidth = screentextureheight = 0;
- if (!r_bloom.integer)
- bloomtexturewidth = bloomtextureheight = 0;
-
- // allocate textures as needed
- if (r_fb.screentexturewidth != screentexturewidth
- || r_fb.screentextureheight != screentextureheight
- || r_fb.bloomtexturewidth != bloomtexturewidth
- || r_fb.bloomtextureheight != bloomtextureheight
- || r_fb.textype != textype
- || useviewfbo != (r_fb.fbo != 0))
+ // allocate motionblur ghost texture if needed - this is the only persistent texture and is only useful on the main view
+ if (r_refdef.view.ismain && (r_fb.screentexturewidth != screentexturewidth || r_fb.screentextureheight != screentextureheight || r_fb.textype != textype))
{
- for (i = 0;i < (int)(sizeof(r_fb.bloomtexture)/sizeof(r_fb.bloomtexture[i]));i++)
- {
- if (r_fb.bloomtexture[i])
- R_FreeTexture(r_fb.bloomtexture[i]);
- r_fb.bloomtexture[i] = NULL;
-
- if (r_fb.bloomfbo[i])
- R_Mesh_DestroyFramebufferObject(r_fb.bloomfbo[i]);
- r_fb.bloomfbo[i] = 0;
- }
-
- if (r_fb.fbo)
- R_Mesh_DestroyFramebufferObject(r_fb.fbo);
- r_fb.fbo = 0;
-
- if (r_fb.colortexture)
- R_FreeTexture(r_fb.colortexture);
- r_fb.colortexture = NULL;
-
- if (r_fb.depthtexture)
- R_FreeTexture(r_fb.depthtexture);
- r_fb.depthtexture = NULL;
-
if (r_fb.ghosttexture)
R_FreeTexture(r_fb.ghosttexture);
r_fb.ghosttexture = NULL;
r_fb.screentexturewidth = screentexturewidth;
r_fb.screentextureheight = screentextureheight;
- r_fb.bloomtexturewidth = bloomtexturewidth;
- r_fb.bloomtextureheight = bloomtextureheight;
r_fb.textype = textype;
if (r_fb.screentexturewidth && r_fb.screentextureheight)
if (r_motionblur.value > 0 || r_damageblur.value > 0)
r_fb.ghosttexture = R_LoadTexture2D(r_main_texturepool, "framebuffermotionblur", r_fb.screentexturewidth, r_fb.screentextureheight, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
r_fb.ghosttexture_valid = false;
- r_fb.colortexture = R_LoadTexture2D(r_main_texturepool, "framebuffercolor", r_fb.screentexturewidth, r_fb.screentextureheight, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
- if (useviewfbo)
- {
- r_fb.depthtexture = R_LoadTextureRenderBuffer(r_main_texturepool, "framebufferdepth", r_fb.screentexturewidth, r_fb.screentextureheight, TEXTYPE_DEPTHBUFFER24STENCIL8);
- r_fb.fbo = R_Mesh_CreateFramebufferObject(r_fb.depthtexture, r_fb.colortexture, NULL, NULL, NULL);
- R_Mesh_SetRenderTargets(r_fb.fbo, r_fb.depthtexture, r_fb.colortexture, NULL, NULL, NULL);
- }
- }
-
- if (r_fb.bloomtexturewidth && r_fb.bloomtextureheight)
- {
- for (i = 0;i < (int)(sizeof(r_fb.bloomtexture)/sizeof(r_fb.bloomtexture[i]));i++)
- {
- r_fb.bloomtexture[i] = R_LoadTexture2D(r_main_texturepool, "framebufferbloom", r_fb.bloomtexturewidth, r_fb.bloomtextureheight, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
- if (useviewfbo)
- r_fb.bloomfbo[i] = R_Mesh_CreateFramebufferObject(NULL, r_fb.bloomtexture[i], NULL, NULL, NULL);
- }
- }
- }
-
- // bloom texture is a different resolution
- r_fb.bloomwidth = bound(1, r_bloom_resolution.integer, r_refdef.view.width);
- r_fb.bloomheight = r_fb.bloomwidth * r_refdef.view.height / r_refdef.view.width;
- r_fb.bloomheight = bound(1, r_fb.bloomheight, r_refdef.view.height);
- r_fb.bloomwidth = bound(1, r_fb.bloomwidth, r_fb.bloomtexturewidth);
- r_fb.bloomheight = bound(1, r_fb.bloomheight, r_fb.bloomtextureheight);
-
- // set up a texcoord array for the full resolution screen image
- // (we have to keep this around to copy back during final render)
- r_fb.screentexcoord2f[0] = 0;
- r_fb.screentexcoord2f[1] = (float)viewheight / (float)r_fb.screentextureheight;
- r_fb.screentexcoord2f[2] = (float)viewwidth / (float)r_fb.screentexturewidth;
- r_fb.screentexcoord2f[3] = (float)viewheight / (float)r_fb.screentextureheight;
- r_fb.screentexcoord2f[4] = (float)viewwidth / (float)r_fb.screentexturewidth;
- r_fb.screentexcoord2f[5] = 0;
- r_fb.screentexcoord2f[6] = 0;
- r_fb.screentexcoord2f[7] = 0;
-
- if(r_fb.fbo)
- {
- for (i = 1;i < 8;i += 2)
- {
- r_fb.screentexcoord2f[i] += 1 - (float)(viewheight + r_refdef.view.y) / (float)r_fb.screentextureheight;
}
}
- // set up a texcoord array for the reduced resolution bloom image
- // (which will be additive blended over the screen image)
- r_fb.bloomtexcoord2f[0] = 0;
- r_fb.bloomtexcoord2f[1] = (float)r_fb.bloomheight / (float)r_fb.bloomtextureheight;
- r_fb.bloomtexcoord2f[2] = (float)r_fb.bloomwidth / (float)r_fb.bloomtexturewidth;
- r_fb.bloomtexcoord2f[3] = (float)r_fb.bloomheight / (float)r_fb.bloomtextureheight;
- r_fb.bloomtexcoord2f[4] = (float)r_fb.bloomwidth / (float)r_fb.bloomtexturewidth;
- r_fb.bloomtexcoord2f[5] = 0;
- r_fb.bloomtexcoord2f[6] = 0;
- r_fb.bloomtexcoord2f[7] = 0;
-
- switch(vid.renderpath)
+ if (r_bloom.integer)
{
- case RENDERPATH_GL11:
- case RENDERPATH_GL13:
- case RENDERPATH_GL20:
- case RENDERPATH_SOFT:
- case RENDERPATH_GLES1:
- case RENDERPATH_GLES2:
- break;
- case RENDERPATH_D3D9:
- case RENDERPATH_D3D10:
- case RENDERPATH_D3D11:
- for (i = 0;i < 4;i++)
- {
- r_fb.screentexcoord2f[i*2+0] += 0.5f / (float)r_fb.screentexturewidth;
- r_fb.screentexcoord2f[i*2+1] += 0.5f / (float)r_fb.screentextureheight;
- r_fb.bloomtexcoord2f[i*2+0] += 0.5f / (float)r_fb.bloomtexturewidth;
- r_fb.bloomtexcoord2f[i*2+1] += 0.5f / (float)r_fb.bloomtextureheight;
- }
- break;
+ // bloom texture is a different resolution
+ r_fb.bloomwidth = bound(1, r_bloom_resolution.integer, r_refdef.view.width);
+ r_fb.bloomheight = r_fb.bloomwidth * r_refdef.view.height / r_refdef.view.width;
+ r_fb.bloomheight = bound(1, r_fb.bloomheight, r_refdef.view.height);
}
+ else
+ r_fb.bloomwidth = r_fb.bloomheight = 0;
- R_Viewport_InitOrtho(&r_fb.bloomviewport, &identitymatrix, 0, 0, r_fb.bloomwidth, r_fb.bloomheight, 0, 0, 1, 1, -10, 100, NULL);
+ r_fb.rt_screen = R_RenderTarget_Get(screentexturewidth, screentextureheight, TEXTYPE_DEPTHBUFFER24STENCIL8, true, textype, TEXTYPE_UNUSED, TEXTYPE_UNUSED, TEXTYPE_UNUSED);
- if (r_fb.fbo)
- r_refdef.view.clear = true;
+ r_refdef.view.clear = true;
}
static void R_Bloom_MakeTexture(void)
{
int x, range, dir;
float xoffset, yoffset, r, brighten;
- rtexture_t *intex;
float colorscale = r_bloom_colorscale.value;
+ r_viewport_t bloomviewport;
+ r_rendertarget_t *prev, *cur;
+ textype_t textype = r_fb.rt_screen->colortextype[0];
r_refdef.stats[r_stat_bloom]++;
-
-#if 0
- // this copy is unnecessary since it happens in R_BlendView already
- if (!r_fb.fbo)
- {
- R_Mesh_CopyToTexture(r_fb.colortexture, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
- r_refdef.stats[r_stat_bloom_copypixels] += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
- }
-#endif
+
+ R_Viewport_InitOrtho(&bloomviewport, &identitymatrix, 0, 0, r_fb.bloomwidth, r_fb.bloomheight, 0, 0, 1, 1, -10, 100, NULL);
// scale down screen texture to the bloom texture size
CHECKGLERROR
- r_fb.bloomindex = 0;
- R_Mesh_SetRenderTargets(r_fb.bloomfbo[r_fb.bloomindex], NULL, r_fb.bloomtexture[r_fb.bloomindex], NULL, NULL, NULL);
- R_SetViewport(&r_fb.bloomviewport);
+ prev = r_fb.rt_screen;
+ cur = R_RenderTarget_Get(r_fb.bloomwidth, r_fb.bloomheight, TEXTYPE_UNUSED, false, textype, TEXTYPE_UNUSED, TEXTYPE_UNUSED, TEXTYPE_UNUSED);
+ R_Mesh_SetRenderTargets(cur->fbo, NULL, cur->colortexture[0], NULL, NULL, NULL);
+ R_SetViewport(&bloomviewport);
GL_CullFace(GL_NONE);
GL_DepthTest(false);
GL_BlendFunc(GL_ONE, GL_ZERO);
GL_Color(colorscale, colorscale, colorscale, 1);
- // D3D has upside down Y coords, the easiest way to flip this is to flip the screen vertices rather than the texcoords, so we just use a different array for that...
- switch(vid.renderpath)
- {
- case RENDERPATH_GL11:
- case RENDERPATH_GL13:
- case RENDERPATH_GL20:
- case RENDERPATH_GLES1:
- case RENDERPATH_GLES2:
- case RENDERPATH_SOFT:
- R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_fb.screentexcoord2f);
- break;
- case RENDERPATH_D3D9:
- case RENDERPATH_D3D10:
- case RENDERPATH_D3D11:
- R_Mesh_PrepareVertices_Generic_Arrays(4, r_d3dscreenvertex3f, NULL, r_fb.screentexcoord2f);
- break;
- }
+ R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, prev->texcoord2f);
// TODO: do boxfilter scale-down in shader?
- R_SetupShader_Generic(r_fb.colortexture, NULL, GL_MODULATE, 1, false, true, true);
+ R_SetupShader_Generic(prev->colortexture[0], NULL, GL_MODULATE, 1, false, true, true);
R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
r_refdef.stats[r_stat_bloom_drawpixels] += r_fb.bloomwidth * r_fb.bloomheight;
-
// we now have a properly scaled bloom image
- if (!r_fb.bloomfbo[r_fb.bloomindex])
- {
- // copy it into the bloom texture
- R_Mesh_CopyToTexture(r_fb.bloomtexture[r_fb.bloomindex], 0, 0, r_fb.bloomviewport.x, r_fb.bloomviewport.y, r_fb.bloomviewport.width, r_fb.bloomviewport.height);
- r_refdef.stats[r_stat_bloom_copypixels] += r_fb.bloomviewport.width * r_fb.bloomviewport.height;
- }
- // multiply bloom image by itself as many times as desired
+ // multiply bloom image by itself as many times as desired to darken it
+ // TODO: if people actually use this it could be done more quickly in the previous shader pass
for (x = 1;x < min(r_bloom_colorexponent.value, 32);)
{
- intex = r_fb.bloomtexture[r_fb.bloomindex];
- r_fb.bloomindex ^= 1;
- R_Mesh_SetRenderTargets(r_fb.bloomfbo[r_fb.bloomindex], NULL, r_fb.bloomtexture[r_fb.bloomindex], NULL, NULL, NULL);
+ prev = cur;
+ cur = R_RenderTarget_Get(r_fb.bloomwidth, r_fb.bloomheight, TEXTYPE_UNUSED, false, textype, TEXTYPE_UNUSED, TEXTYPE_UNUSED, TEXTYPE_UNUSED);
+ R_Mesh_SetRenderTargets(cur->fbo, NULL, cur->colortexture[0], NULL, NULL, NULL);
x *= 2;
r = bound(0, r_bloom_colorexponent.value / x, 1); // always 0.5 to 1
- if (!r_fb.bloomfbo[r_fb.bloomindex])
- {
- GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR); // square it and multiply by two
- GL_Color(r,r,r,1); // apply fix factor
- }
- else
- {
- if(x <= 2)
- GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 128);
- GL_BlendFunc(GL_SRC_COLOR, GL_ZERO); // square it
- GL_Color(1,1,1,1); // no fix factor supported here
- }
- R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_fb.bloomtexcoord2f);
- R_SetupShader_Generic(intex, NULL, GL_MODULATE, 1, false, true, false);
+ if(x <= 2)
+ GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 128);
+ GL_BlendFunc(GL_SRC_COLOR, GL_ZERO); // square it
+ GL_Color(1,1,1,1); // no fix factor supported here
+ R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, prev->texcoord2f);
+ R_SetupShader_Generic(prev->colortexture[0], NULL, GL_MODULATE, 1, false, true, false);
R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
r_refdef.stats[r_stat_bloom_drawpixels] += r_fb.bloomwidth * r_fb.bloomheight;
-
- if (!r_fb.bloomfbo[r_fb.bloomindex])
- {
- // copy the darkened image to a texture
- R_Mesh_CopyToTexture(r_fb.bloomtexture[r_fb.bloomindex], 0, 0, r_fb.bloomviewport.x, r_fb.bloomviewport.y, r_fb.bloomviewport.width, r_fb.bloomviewport.height);
- r_refdef.stats[r_stat_bloom_copypixels] += r_fb.bloomviewport.width * r_fb.bloomviewport.height;
- }
}
range = r_bloom_blur.integer * r_fb.bloomwidth / 320;
for (dir = 0;dir < 2;dir++)
{
- intex = r_fb.bloomtexture[r_fb.bloomindex];
- r_fb.bloomindex ^= 1;
- R_Mesh_SetRenderTargets(r_fb.bloomfbo[r_fb.bloomindex], NULL, r_fb.bloomtexture[r_fb.bloomindex], NULL, NULL, NULL);
+ prev = cur;
+ cur = R_RenderTarget_Get(r_fb.bloomwidth, r_fb.bloomheight, TEXTYPE_UNUSED, false, textype, TEXTYPE_UNUSED, TEXTYPE_UNUSED, TEXTYPE_UNUSED);
+ R_Mesh_SetRenderTargets(cur->fbo, NULL, cur->colortexture[0], NULL, NULL, NULL);
// blend on at multiple vertical offsets to achieve a vertical blur
// TODO: do offset blends using GLSL
// TODO instead of changing the texcoords, change the target positions to prevent artifacts at edges
GL_BlendFunc(GL_ONE, GL_ZERO);
- R_SetupShader_Generic(intex, NULL, GL_MODULATE, 1, false, true, false);
+ R_SetupShader_Generic(prev->colortexture[0], NULL, GL_MODULATE, 1, false, true, false);
for (x = -range;x <= range;x++)
{
if (!dir){xoffset = 0;yoffset = x;}
else {xoffset = x;yoffset = 0;}
- xoffset /= (float)r_fb.bloomtexturewidth;
- yoffset /= (float)r_fb.bloomtextureheight;
+ xoffset /= (float)prev->texturewidth;
+ yoffset /= (float)prev->textureheight;
// compute a texcoord array with the specified x and y offset
- r_fb.offsettexcoord2f[0] = xoffset+r_fb.bloomtexcoord2f[0];
- r_fb.offsettexcoord2f[1] = yoffset+r_fb.bloomtexcoord2f[1];
- r_fb.offsettexcoord2f[2] = xoffset+r_fb.bloomtexcoord2f[2];
- r_fb.offsettexcoord2f[3] = yoffset+r_fb.bloomtexcoord2f[3];
- r_fb.offsettexcoord2f[4] = xoffset+r_fb.bloomtexcoord2f[4];
- r_fb.offsettexcoord2f[5] = yoffset+r_fb.bloomtexcoord2f[5];
- r_fb.offsettexcoord2f[6] = xoffset+r_fb.bloomtexcoord2f[6];
- r_fb.offsettexcoord2f[7] = yoffset+r_fb.bloomtexcoord2f[7];
+ r_fb.offsettexcoord2f[0] = xoffset+prev->texcoord2f[0];
+ r_fb.offsettexcoord2f[1] = yoffset+prev->texcoord2f[1];
+ r_fb.offsettexcoord2f[2] = xoffset+prev->texcoord2f[2];
+ r_fb.offsettexcoord2f[3] = yoffset+prev->texcoord2f[3];
+ r_fb.offsettexcoord2f[4] = xoffset+prev->texcoord2f[4];
+ r_fb.offsettexcoord2f[5] = yoffset+prev->texcoord2f[5];
+ r_fb.offsettexcoord2f[6] = xoffset+prev->texcoord2f[6];
+ r_fb.offsettexcoord2f[7] = yoffset+prev->texcoord2f[7];
// this r value looks like a 'dot' particle, fading sharply to
// black at the edges
// (probably not realistic but looks good enough)
//r = brighten/(range*2+1);
r = brighten / (range * 2 + 1);
if(range >= 1)
- r *= (1 - x*x/(float)(range*range));
+ r *= (1 - x*x/(float)((range+1)*(range+1)));
+ if (r <= 0)
+ continue;
GL_Color(r, r, r, 1);
R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_fb.offsettexcoord2f);
R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
r_refdef.stats[r_stat_bloom_drawpixels] += r_fb.bloomwidth * r_fb.bloomheight;
GL_BlendFunc(GL_ONE, GL_ONE);
}
-
- if (!r_fb.bloomfbo[r_fb.bloomindex])
- {
- // copy the vertically or horizontally blurred bloom view to a texture
- R_Mesh_CopyToTexture(r_fb.bloomtexture[r_fb.bloomindex], 0, 0, r_fb.bloomviewport.x, r_fb.bloomviewport.y, r_fb.bloomviewport.width, r_fb.bloomviewport.height);
- r_refdef.stats[r_stat_bloom_copypixels] += r_fb.bloomviewport.width * r_fb.bloomviewport.height;
- }
}
+
+ // now we have the bloom image, so keep track of it
+ r_fb.rt_bloom = cur;
}
-static void R_BlendView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
+static void R_BlendView(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight)
{
dpuint64 permutation;
float uservecs[4][4];
+ rtexture_t *viewtexture;
+ rtexture_t *bloomtexture;
R_EntityMatrix(&identitymatrix);
case RENDERPATH_SOFT:
case RENDERPATH_GLES2:
permutation =
- (r_fb.bloomtexture[r_fb.bloomindex] ? SHADERPERMUTATION_BLOOM : 0)
+ (r_fb.bloomwidth ? SHADERPERMUTATION_BLOOM : 0)
| (r_refdef.viewblend[3] > 0 ? SHADERPERMUTATION_VIEWTINT : 0)
- | ((v_glslgamma.value && !vid_gammatables_trivial) ? SHADERPERMUTATION_GAMMARAMPS : 0)
+ | (!vid_gammatables_trivial ? SHADERPERMUTATION_GAMMARAMPS : 0)
| (r_glsl_postprocess.integer ? SHADERPERMUTATION_POSTPROCESSING : 0)
| ((!R_Stereo_ColorMasking() && r_glsl_saturation.value != 1) ? SHADERPERMUTATION_SATURATION : 0);
- if (r_fb.colortexture)
- {
- if (!r_fb.fbo)
- {
- R_Mesh_CopyToTexture(r_fb.colortexture, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
- r_refdef.stats[r_stat_bloom_copypixels] += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
- }
-
- if(!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0) && r_fb.ghosttexture)
- {
- // declare variables
- float blur_factor, blur_mouseaccel, blur_velocity;
- static float blur_average;
- static vec3_t blur_oldangles; // used to see how quickly the mouse is moving
-
- // set a goal for the factoring
- blur_velocity = bound(0, (VectorLength(cl.movement_velocity) - r_motionblur_velocityfactor_minspeed.value)
- / max(1, r_motionblur_velocityfactor_maxspeed.value - r_motionblur_velocityfactor_minspeed.value), 1);
- blur_mouseaccel = bound(0, ((fabs(VectorLength(cl.viewangles) - VectorLength(blur_oldangles)) * 10) - r_motionblur_mousefactor_minspeed.value)
- / max(1, r_motionblur_mousefactor_maxspeed.value - r_motionblur_mousefactor_minspeed.value), 1);
- blur_factor = ((blur_velocity * r_motionblur_velocityfactor.value)
- + (blur_mouseaccel * r_motionblur_mousefactor.value));
-
- // from the goal, pick an averaged value between goal and last value
- cl.motionbluralpha = bound(0, (cl.time - cl.oldtime) / max(0.001, r_motionblur_averaging.value), 1);
- blur_average = blur_average * (1 - cl.motionbluralpha) + blur_factor * cl.motionbluralpha;
-
- // enforce minimum amount of blur
- blur_factor = blur_average * (1 - r_motionblur_minblur.value) + r_motionblur_minblur.value;
-
- //Con_Printf("motionblur: direct factor: %f, averaged factor: %f, velocity: %f, mouse accel: %f \n", blur_factor, blur_average, blur_velocity, blur_mouseaccel);
-
- // calculate values into a standard alpha
- cl.motionbluralpha = 1 - exp(-
- (
- (r_motionblur.value * blur_factor / 80)
- +
- (r_damageblur.value * (cl.cshifts[CSHIFT_DAMAGE].percent / 1600))
- )
- /
- max(0.0001, cl.time - cl.oldtime) // fps independent
- );
-
- // randomization for the blur value to combat persistent ghosting
- cl.motionbluralpha *= lhrandom(1 - r_motionblur_randomize.value, 1 + r_motionblur_randomize.value);
- cl.motionbluralpha = bound(0, cl.motionbluralpha, r_motionblur_maxblur.value);
-
- // apply the blur
- R_ResetViewRendering2D(fbo, depthtexture, colortexture);
- if (cl.motionbluralpha > 0 && !r_refdef.envmap && r_fb.ghosttexture_valid)
- {
- GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- GL_Color(1, 1, 1, cl.motionbluralpha);
- switch(vid.renderpath)
- {
- case RENDERPATH_GL11:
- case RENDERPATH_GL13:
- case RENDERPATH_GL20:
- case RENDERPATH_GLES1:
- case RENDERPATH_GLES2:
- case RENDERPATH_SOFT:
- R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_fb.screentexcoord2f);
- break;
- case RENDERPATH_D3D9:
- case RENDERPATH_D3D10:
- case RENDERPATH_D3D11:
- R_Mesh_PrepareVertices_Generic_Arrays(4, r_d3dscreenvertex3f, NULL, r_fb.screentexcoord2f);
- break;
- }
- R_SetupShader_Generic(r_fb.ghosttexture, NULL, GL_MODULATE, 1, false, true, true);
- R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
- r_refdef.stats[r_stat_bloom_drawpixels] += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
- }
-
- // updates old view angles for next pass
- VectorCopy(cl.viewangles, blur_oldangles);
-
- // copy view into the ghost texture
- R_Mesh_CopyToTexture(r_fb.ghosttexture, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
- r_refdef.stats[r_stat_bloom_copypixels] += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
- r_fb.ghosttexture_valid = true;
- }
- }
- else
- {
- // no r_fb.colortexture means we're rendering to the real fb
- // we may still have to do view tint...
- if (r_refdef.viewblend[3] >= (1.0f / 256.0f))
+ if(r_refdef.view.ismain && !R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0) && r_fb.ghosttexture)
+ {
+ // declare variables
+ float blur_factor, blur_mouseaccel, blur_velocity;
+ static float blur_average;
+ static vec3_t blur_oldangles; // used to see how quickly the mouse is moving
+
+ // set a goal for the factoring
+ blur_velocity = bound(0, (VectorLength(cl.movement_velocity) - r_motionblur_velocityfactor_minspeed.value)
+ / max(1, r_motionblur_velocityfactor_maxspeed.value - r_motionblur_velocityfactor_minspeed.value), 1);
+ blur_mouseaccel = bound(0, ((fabs(VectorLength(cl.viewangles) - VectorLength(blur_oldangles)) * 10) - r_motionblur_mousefactor_minspeed.value)
+ / max(1, r_motionblur_mousefactor_maxspeed.value - r_motionblur_mousefactor_minspeed.value), 1);
+ blur_factor = ((blur_velocity * r_motionblur_velocityfactor.value)
+ + (blur_mouseaccel * r_motionblur_mousefactor.value));
+
+ // from the goal, pick an averaged value between goal and last value
+ cl.motionbluralpha = bound(0, (cl.time - cl.oldtime) / max(0.001, r_motionblur_averaging.value), 1);
+ blur_average = blur_average * (1 - cl.motionbluralpha) + blur_factor * cl.motionbluralpha;
+
+ // enforce minimum amount of blur
+ blur_factor = blur_average * (1 - r_motionblur_minblur.value) + r_motionblur_minblur.value;
+
+ //Con_Printf("motionblur: direct factor: %f, averaged factor: %f, velocity: %f, mouse accel: %f \n", blur_factor, blur_average, blur_velocity, blur_mouseaccel);
+
+ // calculate values into a standard alpha
+ cl.motionbluralpha = 1 - exp(-
+ (
+ (r_motionblur.value * blur_factor / 80)
+ +
+ (r_damageblur.value * (cl.cshifts[CSHIFT_DAMAGE].percent / 1600))
+ )
+ /
+ max(0.0001, cl.time - cl.oldtime) // fps independent
+ );
+
+ // randomization for the blur value to combat persistent ghosting
+ cl.motionbluralpha *= lhrandom(1 - r_motionblur_randomize.value, 1 + r_motionblur_randomize.value);
+ cl.motionbluralpha = bound(0, cl.motionbluralpha, r_motionblur_maxblur.value);
+
+ // apply the blur
+ R_ResetViewRendering2D(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
+ if (cl.motionbluralpha > 0 && !r_refdef.envmap && r_fb.ghosttexture_valid)
{
- // apply a color tint to the whole view
- R_ResetViewRendering2D(0, NULL, NULL);
- GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
- R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, NULL);
- R_SetupShader_Generic_NoTexture(false, true);
GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ GL_Color(1, 1, 1, cl.motionbluralpha);
+ R_CalcTexCoordsForView(0, 0, viewwidth, viewheight, viewwidth, viewheight, r_fb.ghosttexcoord2f);
+ R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_fb.ghosttexcoord2f);
+ R_SetupShader_Generic(r_fb.ghosttexture, NULL, GL_MODULATE, 1, false, true, true);
R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
+ r_refdef.stats[r_stat_bloom_drawpixels] += viewwidth * viewheight;
}
- break; // no screen processing, no bloom, skip it
+
+ // updates old view angles for next pass
+ VectorCopy(cl.viewangles, blur_oldangles);
+
+ // copy view into the ghost texture
+ R_Mesh_CopyToTexture(r_fb.ghosttexture, 0, 0, viewx, viewy, viewwidth, viewheight);
+ r_refdef.stats[r_stat_bloom_copypixels] += viewwidth * viewheight;
+ r_fb.ghosttexture_valid = true;
}
- if (r_fb.bloomtexture[0])
+ if (r_fb.bloomwidth)
{
// make the bloom texture
R_Bloom_MakeTexture();
if (r_glsl_postprocess_uservec4_enable.integer)
sscanf(r_glsl_postprocess_uservec4.string, "%f %f %f %f", &uservecs[3][0], &uservecs[3][1], &uservecs[3][2], &uservecs[3][3]);
- R_ResetViewRendering2D(0, NULL, NULL); // here we render to the real framebuffer!
+ // render to the screen fbo
+ R_ResetViewRendering2D(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
GL_Color(1, 1, 1, 1);
GL_BlendFunc(GL_ONE, GL_ZERO);
+ viewtexture = r_fb.rt_screen->colortexture[0];
+ bloomtexture = r_fb.rt_bloom ? r_fb.rt_bloom->colortexture[0] : NULL;
+
+ if (r_rendertarget_debug.integer >= 0)
+ {
+ r_rendertarget_t *rt = (r_rendertarget_t *)Mem_ExpandableArray_RecordAtIndex(&r_fb.rendertargets, r_rendertarget_debug.integer);
+ if (rt && rt->colortexture[0])
+ {
+ viewtexture = rt->colortexture[0];
+ bloomtexture = NULL;
+ }
+ }
+
+ R_Mesh_PrepareVertices_Mesh_Arrays(4, r_screenvertex3f, NULL, NULL, NULL, NULL, r_fb.rt_screen->texcoord2f, bloomtexture ? r_fb.rt_bloom->texcoord2f : NULL);
switch(vid.renderpath)
{
case RENDERPATH_GL20:
case RENDERPATH_GLES2:
- R_Mesh_PrepareVertices_Mesh_Arrays(4, r_screenvertex3f, NULL, NULL, NULL, NULL, r_fb.screentexcoord2f, r_fb.bloomtexcoord2f);
R_SetupShader_SetPermutationGLSL(SHADERMODE_POSTPROCESS, permutation);
- if (r_glsl_permutation->tex_Texture_First >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , r_fb.colortexture);
- if (r_glsl_permutation->tex_Texture_Second >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second , r_fb.bloomtexture[r_fb.bloomindex]);
+ if (r_glsl_permutation->tex_Texture_First >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , viewtexture);
+ if (r_glsl_permutation->tex_Texture_Second >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second , bloomtexture);
if (r_glsl_permutation->tex_Texture_GammaRamps >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_GammaRamps, r_texture_gammaramps );
if (r_glsl_permutation->loc_ViewTintColor >= 0) qglUniform4f(r_glsl_permutation->loc_ViewTintColor , r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
if (r_glsl_permutation->loc_PixelSize >= 0) qglUniform2f(r_glsl_permutation->loc_PixelSize , 1.0/r_fb.screentexturewidth, 1.0/r_fb.screentextureheight);
break;
case RENDERPATH_D3D9:
#ifdef SUPPORTD3D
- // D3D has upside down Y coords, the easiest way to flip this is to flip the screen vertices rather than the texcoords, so we just use a different array for that...
- R_Mesh_PrepareVertices_Mesh_Arrays(4, r_d3dscreenvertex3f, NULL, NULL, NULL, NULL, r_fb.screentexcoord2f, r_fb.bloomtexcoord2f);
R_SetupShader_SetPermutationHLSL(SHADERMODE_POSTPROCESS, permutation);
- R_Mesh_TexBind(GL20TU_FIRST , r_fb.colortexture);
- R_Mesh_TexBind(GL20TU_SECOND , r_fb.bloomtexture[r_fb.bloomindex]);
+ R_Mesh_TexBind(GL20TU_FIRST , viewtexture);
+ R_Mesh_TexBind(GL20TU_SECOND , bloomtexture);
R_Mesh_TexBind(GL20TU_GAMMARAMPS, r_texture_gammaramps );
hlslPSSetParameter4f(D3DPSREGISTER_ViewTintColor , r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
hlslPSSetParameter2f(D3DPSREGISTER_PixelSize , 1.0/r_fb.screentexturewidth, 1.0/r_fb.screentextureheight);
Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
break;
case RENDERPATH_SOFT:
- R_Mesh_PrepareVertices_Mesh_Arrays(4, r_screenvertex3f, NULL, NULL, NULL, NULL, r_fb.screentexcoord2f, r_fb.bloomtexcoord2f);
R_SetupShader_SetPermutationSoft(SHADERMODE_POSTPROCESS, permutation);
- R_Mesh_TexBind(GL20TU_FIRST , r_fb.colortexture);
- R_Mesh_TexBind(GL20TU_SECOND , r_fb.bloomtexture[r_fb.bloomindex]);
+ R_Mesh_TexBind(GL20TU_FIRST , viewtexture);
+ R_Mesh_TexBind(GL20TU_SECOND , bloomtexture);
R_Mesh_TexBind(GL20TU_GAMMARAMPS, r_texture_gammaramps );
DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ViewTintColor , r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_PixelSize , 1.0/r_fb.screentexturewidth, 1.0/r_fb.screentextureheight);
if (r_refdef.viewblend[3] >= (1.0f / 256.0f))
{
// apply a color tint to the whole view
- R_ResetViewRendering2D(0, NULL, NULL);
+ R_ResetViewRendering2D(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, NULL);
R_SetupShader_Generic_NoTexture(false, true);
case RENDERPATH_D3D11:
case RENDERPATH_SOFT:
case RENDERPATH_GLES2:
- if(v_glslgamma.integer && !vid_gammatables_trivial)
+ if(!vid_gammatables_trivial)
{
if(!r_texture_gammaramps || vid_gammatables_serial != r_texture_gammaramps_serial)
{
*/
int dpsoftrast_test;
extern cvar_t r_shadow_bouncegrid;
-void R_RenderView(void)
+void R_RenderView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, int x, int y, int width, int height)
{
matrix4x4_t originalmatrix = r_refdef.view.matrix, offsetmatrix;
- int fbo;
- rtexture_t *depthtexture;
- rtexture_t *colortexture;
+ int viewfbo = 0;
+ rtexture_t *viewdepthtexture = NULL;
+ rtexture_t *viewcolortexture = NULL;
+ int viewx = r_refdef.view.x, viewy = r_refdef.view.y, viewwidth = r_refdef.view.width, viewheight = r_refdef.view.height;
dpsoftrast_test = r_test.integer;
r_fb.water.enabled = false;
r_fb.water.numwaterplanes = 0;
- R_RenderScene(0, NULL, NULL);
+ R_RenderScene(0, NULL, NULL, r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
r_refdef.view.matrix = originalmatrix;
R_Shadow_UpdateWorldLightSelection();
+ // this will set up r_fb.rt_screen
R_Bloom_StartFrame();
// apply bloom brightness offset
- if(r_fb.bloomtexture[0])
+ if(r_fb.rt_bloom)
r_refdef.view.colorscale *= r_bloom_scenebrightness.value;
- R_Water_StartFrame();
+ // R_Bloom_StartFrame probably set up an fbo for us to render into, it will be rendered to the window later in R_BlendView
+ if (r_fb.rt_screen)
+ {
+ viewfbo = r_fb.rt_screen->fbo;
+ viewdepthtexture = r_fb.rt_screen->depthtexture;
+ viewcolortexture = r_fb.rt_screen->colortexture[0];
+ viewx = 0;
+ viewy = 0;
+ viewwidth = width;
+ viewheight = height;
+ }
- // now we probably have an fbo to render into
- fbo = r_fb.fbo;
- depthtexture = r_fb.depthtexture;
- colortexture = r_fb.colortexture;
+ R_Water_StartFrame();
CHECKGLERROR
if (r_timereport_active)
R_TimeReport("viewsetup");
- R_ResetViewRendering3D(fbo, depthtexture, colortexture);
+ R_ResetViewRendering3D(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
+
+ // clear the whole fbo every frame - otherwise the driver will consider
+ // it to be an inter-frame texture and stall in multi-gpu configurations
+ if (r_fb.rt_screen)
+ GL_ScissorTest(false);
+ R_ClearScreen(r_refdef.fogenabled);
+ if (r_timereport_active)
+ R_TimeReport("viewclear");
- if (r_refdef.view.clear || r_refdef.fogenabled || fbo)
- {
- R_ClearScreen(r_refdef.fogenabled);
- if (r_timereport_active)
- R_TimeReport("viewclear");
- }
r_refdef.view.clear = true;
r_refdef.view.showdebug = true;
r_fb.water.numwaterplanes = 0;
if (r_fb.water.enabled)
- R_RenderWaterPlanes(fbo, depthtexture, colortexture);
-
- R_RenderScene(fbo, depthtexture, colortexture);
+ R_RenderWaterPlanes(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
+
+ // for the actual view render we use scissoring a fair amount, so scissor
+ // test needs to be on
+ if (r_fb.rt_screen)
+ GL_ScissorTest(true);
+ GL_Scissor(viewx, viewy, viewwidth, viewheight);
+ R_RenderScene(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
r_fb.water.numwaterplanes = 0;
- R_BlendView(fbo, depthtexture, colortexture);
+ // postprocess uses textures that are not aligned with the viewport we're rendering, so no scissoring
+ GL_ScissorTest(false);
+
+ R_BlendView(fbo, depthtexture, colortexture, x, y, width, height);
if (r_timereport_active)
R_TimeReport("blendview");
- GL_Scissor(0, 0, vid.width, vid.height);
- GL_ScissorTest(false);
-
r_refdef.view.matrix = originalmatrix;
CHECKGLERROR
}
-void R_RenderWaterPlanes(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
+void R_RenderWaterPlanes(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight)
{
if (cl.csqc_vidvars.drawworld && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->DrawAddWaterPlanes)
{
if (r_fb.water.numwaterplanes)
{
- R_Water_ProcessPlanes(fbo, depthtexture, colortexture);
+ R_Water_ProcessPlanes(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
if (r_timereport_active)
R_TimeReport("waterscenes");
}
extern cvar_t cl_decals_newsystem;
extern qboolean r_shadow_usingdeferredprepass;
extern int r_shadow_shadowmapatlas_modelshadows_size;
-void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
+void R_RenderScene(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight)
{
qboolean shadowmapping = false;
if (skyrendermasked && skyrenderlater)
{
// we have to force off the water clipping plane while rendering sky
- R_SetupView(false, fbo, depthtexture, colortexture);
+ R_SetupView(false, viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
R_Sky();
- R_SetupView(true, fbo, depthtexture, colortexture);
+ R_SetupView(true, viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
if (r_timereport_active)
R_TimeReport("sky");
}
}
+ // save the framebuffer info for R_Shadow_RenderMode_Reset during this view render
+ r_shadow_viewfbo = viewfbo;
+ r_shadow_viewdepthtexture = viewdepthtexture;
+ r_shadow_viewcolortexture = viewcolortexture;
+ r_shadow_viewx = viewx;
+ r_shadow_viewy = viewy;
+ r_shadow_viewwidth = viewwidth;
+ r_shadow_viewheight = viewheight;
+
R_Shadow_PrepareModelShadows();
- R_Shadow_PrepareLights(fbo, depthtexture, colortexture);
+ R_Shadow_PrepareLights();
if (r_timereport_active)
R_TimeReport("preparelights");
if ((r_shadows.integer == 1 || (r_shadows.integer > 0 && !shadowmapping)) && !r_shadows_drawafterrtlighting.integer && r_refdef.scene.lightmapintensity > 0)
{
- R_ResetViewRendering3D(fbo, depthtexture, colortexture);
+ R_ResetViewRendering3D(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
R_Shadow_DrawModelShadows();
- R_ResetViewRendering3D(fbo, depthtexture, colortexture);
+ R_ResetViewRendering3D(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
// don't let sound skip if going slow
if (r_refdef.scene.extraupdate)
S_ExtraUpdate ();
if ((r_shadows.integer == 1 || (r_shadows.integer > 0 && !shadowmapping)) && r_shadows_drawafterrtlighting.integer && r_refdef.scene.lightmapintensity > 0)
{
- R_ResetViewRendering3D(fbo, depthtexture, colortexture);
+ R_ResetViewRendering3D(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
R_Shadow_DrawModelShadows();
- R_ResetViewRendering3D(fbo, depthtexture, colortexture);
+ R_ResetViewRendering3D(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
// don't let sound skip if going slow
if (r_refdef.scene.extraupdate)
S_ExtraUpdate ();
R_TimeReport("explosions");
}
- if (cl.csqc_loaded)
- VM_CL_AddPolygonsToMeshQueue(CLVM_prog);
-
if (r_refdef.view.showdebug)
{
if (cl_locs_show.integer)
t->currentmaterialflags |= MATERIALFLAG_NORTLIGHT;
if (t->currentmaterialflags & MATERIALFLAG_CUSTOMBLEND && !(R_BlendFuncFlags(t->customblendfunc[0], t->customblendfunc[1]) & BLENDFUNC_ALLOWS_COLORMOD))
{
- // some CUSTOMBLEND blendfuncs are too weird for anything but fullbright rendering, and even then we have to ignore colormod and view colorscale
- t->currentmaterialflags = t->currentmaterialflags | MATERIALFLAG_MODELLIGHT | MATERIALFLAG_NORTLIGHT;
+ // some CUSTOMBLEND blendfuncs are too weird, we have to ignore colormod and view colorscale
+ t->currentmaterialflags = t->currentmaterialflags | MATERIALFLAG_NORTLIGHT;
for (q = 0; q < 3; q++)
{
t->render_glowmod[q] = rsurface.entity->glowmod[q];
for (q = 0; q < 3; q++)
{
t->render_glowmod[q] = rsurface.entity->render_glowmod[q] * r_refdef.view.colorscale;
- t->render_modellight_lightdir[q] = rsurface.entity->render_modellight_lightdir[q] * r_refdef.view.colorscale;
- t->render_modellight_ambient[q] = rsurface.entity->render_modellight_ambient[q] * r_refdef.view.colorscale;
+ t->render_modellight_lightdir[q] = q == 2;
+ t->render_modellight_ambient[q] = 0;
t->render_modellight_diffuse[q] = 0;
t->render_modellight_specular[q] = 0;
t->render_lightmap_ambient[q] = rsurface.entity->render_lightmap_ambient[q] * r_refdef.view.colorscale;
}
}
+ if (t->currentmaterialflags & MATERIALFLAG_VERTEXCOLOR)
+ {
+ // since MATERIALFLAG_VERTEXCOLOR uses the lightmapcolor4f vertex
+ // attribute, we punt it to the lightmap path and hope for the best,
+ // but lighting doesn't work.
+ //
+ // FIXME: this is fine for effects but CSQC polygons should be subject
+ // to lighting.
+ t->currentmaterialflags &= ~MATERIALFLAG_MODELLIGHT;
+ for (q = 0; q < 3; q++)
+ {
+ t->render_glowmod[q] = rsurface.entity->render_glowmod[q] * r_refdef.view.colorscale;
+ t->render_modellight_lightdir[q] = q == 2;
+ t->render_modellight_ambient[q] = 0;
+ t->render_modellight_diffuse[q] = 0;
+ t->render_modellight_specular[q] = 0;
+ t->render_lightmap_ambient[q] = 0;
+ t->render_lightmap_diffuse[q] = rsurface.entity->render_fullbright[q] * r_refdef.view.colorscale;
+ t->render_lightmap_specular[q] = 0;
+ t->render_rtlight_diffuse[q] = 0;
+ t->render_rtlight_specular[q] = 0;
+ }
+ }
+
for (q = 0; q < 3; q++)
{
t->render_colormap_pants[q] = rsurface.entity->colormap_pantscolor[q];
static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_t **texturesurfacelist)
{
+ int i, j;
// transparent sky would be ridiculous
if (rsurface.texture->currentmaterialflags & MATERIALFLAGMASK_DEPTHSORTED)
return;
skyrenderlater = true;
RSurf_SetupDepthAndCulling();
GL_DepthMask(true);
- // LordHavoc: HalfLife maps have freaky skypolys so don't use
+
+ // add the vertices of the surfaces to a world bounding box so we can scissor the sky render later
+ if (r_sky_scissor.integer)
+ {
+ RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist);
+ for (i = 0; i < texturenumsurfaces; i++)
+ {
+ const msurface_t *surf = texturesurfacelist[i];
+ const float *v;
+ float p[3];
+ float mins[3], maxs[3];
+ int scissor[4];
+ for (j = 0, v = rsurface.batchvertex3f + 3 * surf->num_firstvertex; j < surf->num_vertices; j++, v += 3)
+ {
+ Matrix4x4_Transform(&rsurface.matrix, v, p);
+ if (j > 0)
+ {
+ if (mins[0] > p[0]) mins[0] = p[0];
+ if (mins[1] > p[1]) mins[1] = p[1];
+ if (mins[2] > p[2]) mins[2] = p[2];
+ if (maxs[0] < p[0]) maxs[0] = p[0];
+ if (maxs[1] < p[1]) maxs[1] = p[1];
+ if (maxs[2] < p[2]) maxs[2] = p[2];
+ }
+ else
+ {
+ VectorCopy(p, mins);
+ VectorCopy(p, maxs);
+ }
+ }
+ if (!R_ScissorForBBox(mins, maxs, scissor))
+ {
+ if (skyscissor[2])
+ {
+ if (skyscissor[0] > scissor[0])
+ {
+ skyscissor[2] += skyscissor[0] - scissor[0];
+ skyscissor[0] = scissor[0];
+ }
+ if (skyscissor[1] > scissor[1])
+ {
+ skyscissor[3] += skyscissor[1] - scissor[1];
+ skyscissor[1] = scissor[1];
+ }
+ if (skyscissor[0] + skyscissor[2] < scissor[0] + scissor[2])
+ skyscissor[2] = scissor[0] + scissor[2] - skyscissor[0];
+ if (skyscissor[1] + skyscissor[3] < scissor[1] + scissor[3])
+ skyscissor[3] = scissor[1] + scissor[3] - skyscissor[1];
+ }
+ else
+ Vector4Copy(scissor, skyscissor);
+ }
+ }
+ }
+
+ // LadyHavoc: HalfLife maps have freaky skypolys so don't use
// skymasking on them, and Quake3 never did sky masking (unlike
// software Quake and software Quake2), so disable the sky masking
// in Quake3 maps as it causes problems with q3map2 sky tricks,
// and skymasking also looks very bad when noclipping outside the
// level, so don't use it then either.
- if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.skymasking && r_q1bsp_skymasking.integer && !r_refdef.viewcache.world_novis && !r_trippy.integer)
+ if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.skymasking && (r_refdef.scene.worldmodel->brush.isq3bsp ? r_q3bsp_renderskydepth.integer : r_q1bsp_skymasking.integer) && !r_refdef.viewcache.world_novis && !r_trippy.integer)
{
R_Mesh_ResetTextureState();
if (skyrendermasked)
{
R_SetupShader_DepthOrShadow(false, false, false);
// depth-only (masking)
- GL_ColorMask(0,0,0,0);
+ GL_ColorMask(0, 0, 0, 0);
// just to make sure that braindead drivers don't draw
// anything despite that colormask...
GL_BlendFunc(GL_ZERO, GL_ONE);