]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
Made RENDERPATH_GL20 require fbo support and always use it.
[xonotic/darkplaces.git] / gl_rmain.c
index cd3d41ee4c64281e5111ec0092f396c51b1921f2..fa55fd47fe2507b0b770dcc0df541c30536c297b 100644 (file)
@@ -20,7 +20,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 // r_main.c
 
 #include "quakedef.h"
-#include "cl_dyntexture.h"
 #include "r_shadow.h"
 #include "polygon.h"
 #include "image.h"
@@ -176,6 +175,7 @@ static cvar_t r_glsl = {CVAR_READONLY, "r_glsl", "1", "indicates whether the Ope
 
 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"};
@@ -213,7 +213,6 @@ cvar_t r_water_reflectdistort = {CVAR_SAVE, "r_water_reflectdistort", "0.01", "h
 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"};
@@ -268,7 +267,6 @@ cvar_t r_buffermegs[R_BUFFERDATA_COUNT] =
        {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;
@@ -321,7 +319,6 @@ static int r_qwskincache_size;
 
 /// 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,
@@ -329,13 +326,6 @@ const float r_screenvertex3f[12] =
        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)
 {
@@ -2034,7 +2024,7 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod
                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;
@@ -2050,7 +2040,7 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod
                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:
@@ -2145,28 +2135,6 @@ void R_SetupShader_DepthOrShadow(qboolean notrippy, qboolean depthrgb, qboolean
        }
 }
 
-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
@@ -2632,13 +2600,13 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif
                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              );
@@ -2815,13 +2783,13 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif
                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              );
@@ -2962,13 +2930,13 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif
                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              );
@@ -3129,6 +3097,23 @@ void R_SkinFrame_MarkUsed(skinframe_t *skinframe)
        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;
@@ -3138,22 +3123,7 @@ void R_SkinFrame_Purge(void)
                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);
                }
        }
 }
@@ -3194,16 +3164,13 @@ skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewid
                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;
@@ -3213,33 +3180,9 @@ skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewid
        }
        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);
@@ -3282,7 +3225,7 @@ skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewid
        }
 
 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;
@@ -3316,6 +3259,8 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        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;
        }
@@ -3741,7 +3686,7 @@ skinframe_t *R_SkinFrame_LoadInternal8bit(const char *name, int textureflags, co
                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++)
                {
@@ -3789,6 +3734,73 @@ skinframe_t *R_SkinFrame_LoadMissing(void)
        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
 {
@@ -4048,6 +4060,7 @@ static void gl_main_start(void)
        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);
@@ -4087,6 +4100,8 @@ static void gl_main_start(void)
 
 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();
@@ -4295,6 +4310,7 @@ void GL_Main_Init(void)
        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);
@@ -4333,7 +4349,6 @@ void GL_Main_Init(void)
        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);
@@ -5525,14 +5540,14 @@ static void R_GetScaledViewSize(int width, int height, int *outwidth, int *outhe
        *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)
@@ -5544,17 +5559,16 @@ void R_SetupView(qboolean allowwaterclippingplane, int fbo, rtexture_t *depthtex
                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)
        {
@@ -5613,15 +5627,15 @@ void R_EntityMatrix(const matrix4x4_t *matrix)
        }
 }
 
-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);
@@ -5656,18 +5670,18 @@ void R_ResetViewRendering2D_Common(int fbo, rtexture_t *depthtexture, rtexture_t
        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);
@@ -5715,15 +5729,105 @@ void R_RenderView_UpdateViewVectors(void)
        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;
@@ -5745,76 +5849,25 @@ static void R_Water_StartFrame(void)
 
        // 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;
@@ -5937,7 +5990,7 @@ void R_Water_AddWaterPlane(msurface_t *surface, int entno)
 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;
@@ -5945,8 +5998,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
        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;
 
@@ -5974,54 +6026,11 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
                }
        }
 
-       // 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
@@ -6036,20 +6045,27 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
        {
                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;
@@ -6064,8 +6080,10 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
                        }
 
                        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
@@ -6073,23 +6091,30 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
                        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);
@@ -6113,8 +6138,10 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
 
                        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
@@ -6122,14 +6149,16 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
                        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;
@@ -6171,24 +6200,23 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
 
                        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;
@@ -6218,25 +6246,28 @@ 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:
@@ -6267,21 +6298,6 @@ static void R_Bloom_StartFrame(void)
 
        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);
@@ -6291,20 +6307,10 @@ static void R_Bloom_StartFrame(void)
        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))
        {
@@ -6313,55 +6319,15 @@ static void R_Bloom_StartFrame(void)
                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)
@@ -6369,179 +6335,71 @@ static void R_Bloom_StartFrame(void)
                        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;
@@ -6552,29 +6410,29 @@ static void R_Bloom_MakeTexture(void)
 
        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)
@@ -6582,27 +6440,27 @@ static void R_Bloom_MakeTexture(void)
                        //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);
 
@@ -6615,113 +6473,74 @@ static void R_BlendView(int fbo, rtexture_t *depthtexture, rtexture_t *colortext
        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();
@@ -6740,18 +6559,32 @@ static void R_BlendView(int fbo, rtexture_t *depthtexture, rtexture_t *colortext
                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);
@@ -6765,11 +6598,9 @@ static void R_BlendView(int fbo, rtexture_t *depthtexture, rtexture_t *colortext
                        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);
@@ -6789,10 +6620,9 @@ static void R_BlendView(int fbo, rtexture_t *depthtexture, rtexture_t *colortext
                        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);
@@ -6816,7 +6646,7 @@ static void R_BlendView(int fbo, rtexture_t *depthtexture, rtexture_t *colortext
                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);
@@ -6966,7 +6796,7 @@ void R_UpdateVariables(void)
        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)
                        {
@@ -7078,12 +6908,13 @@ R_RenderView
 */
 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;
 
@@ -7121,7 +6952,7 @@ void R_RenderView(void)
                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;
 
@@ -7146,31 +6977,41 @@ void R_RenderView(void)
 
        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;
@@ -7189,24 +7030,29 @@ void R_RenderView(void)
 
        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)
        {
@@ -7225,7 +7071,7 @@ void R_RenderWaterPlanes(int fbo, rtexture_t *depthtexture, rtexture_t *colortex
 
        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");
        }
@@ -7238,7 +7084,7 @@ static void R_DrawModelDecals(void);
 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;
 
@@ -7281,16 +7127,25 @@ void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
                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");
 
@@ -7342,9 +7197,9 @@ void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
 
        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 ();
@@ -7363,9 +7218,9 @@ void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
 
        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 ();
@@ -7395,9 +7250,6 @@ void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
                        R_TimeReport("explosions");
        }
 
-       if (cl.csqc_loaded)
-               VM_CL_AddPolygonsToMeshQueue(CLVM_prog);
-
        if (r_refdef.view.showdebug)
        {
                if (cl_locs_show.integer)
@@ -8126,8 +7978,8 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                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];
@@ -8202,8 +8054,8 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                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;
@@ -8214,6 +8066,30 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                }
        }
 
+       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];
@@ -10522,6 +10398,7 @@ void RSurf_SetupDepthAndCulling(void)
 
 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;
@@ -10529,20 +10406,75 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_
        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);