]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
fix image loading sRGB conversion
[xonotic/darkplaces.git] / gl_rmain.c
index 8574aeb2db3c3dc43ed43ee2915348e17d72a800..20aff5a24551d832f97d6047d46740904cbeb10e 100644 (file)
@@ -50,14 +50,18 @@ static qboolean r_savedds;
 //
 r_refdef_t r_refdef;
 
-cvar_t r_motionblur = {CVAR_SAVE, "r_motionblur", "0", "motionblur value scale - 0.5 recommended"};
-cvar_t r_damageblur = {CVAR_SAVE, "r_damageblur", "0", "motionblur based on damage"};
-cvar_t r_motionblur_vmin = {CVAR_SAVE, "r_motionblur_vmin", "300", "minimum influence from velocity"};
-cvar_t r_motionblur_vmax = {CVAR_SAVE, "r_motionblur_vmax", "600", "maximum influence from velocity"};
-cvar_t r_motionblur_bmin = {CVAR_SAVE, "r_motionblur_bmin", "0.5", "velocity at which there is no blur yet (may be negative to always have some blur)"};
-cvar_t r_motionblur_vcoeff = {CVAR_SAVE, "r_motionblur_vcoeff", "0.05", "sliding average reaction time for velocity"};
-cvar_t r_motionblur_maxblur = {CVAR_SAVE, "r_motionblur_maxblur", "0.88", "cap for motionblur alpha value"};
+cvar_t r_motionblur = {CVAR_SAVE, "r_motionblur", "0", "screen motionblur - value represents intensity, somewhere around 0.5 recommended"};
+cvar_t r_damageblur = {CVAR_SAVE, "r_damageblur", "0", "screen motionblur based on damage - value represents intensity, somewhere around 0.5 recommended"};
+cvar_t r_motionblur_averaging = {CVAR_SAVE, "r_motionblur_averaging", "0.1", "sliding average reaction time for velocity (higher = slower adaption to change)"};
 cvar_t r_motionblur_randomize = {CVAR_SAVE, "r_motionblur_randomize", "0.1", "randomizing coefficient to workaround ghosting"};
+cvar_t r_motionblur_minblur = {CVAR_SAVE, "r_motionblur_minblur", "0.5", "factor of blur to apply at all times (always have this amount of blur no matter what the other factors are)"};
+cvar_t r_motionblur_maxblur = {CVAR_SAVE, "r_motionblur_maxblur", "0.9", "maxmimum amount of blur"};
+cvar_t r_motionblur_velocityfactor = {CVAR_SAVE, "r_motionblur_velocityfactor", "1", "factoring in of player velocity to the blur equation - the faster the player moves around the map, the more blur they get"};
+cvar_t r_motionblur_velocityfactor_minspeed = {CVAR_SAVE, "r_motionblur_velocityfactor_minspeed", "400", "lower value of velocity when it starts to factor into blur equation"};
+cvar_t r_motionblur_velocityfactor_maxspeed = {CVAR_SAVE, "r_motionblur_velocityfactor_maxspeed", "800", "upper value of velocity when it reaches the peak factor into blur equation"};
+cvar_t r_motionblur_mousefactor = {CVAR_SAVE, "r_motionblur_mousefactor", "2", "factoring in of mouse acceleration to the blur equation - the faster the player turns their mouse, the more blur they get"};
+cvar_t r_motionblur_mousefactor_minspeed = {CVAR_SAVE, "r_motionblur_mousefactor_minspeed", "0", "lower value of mouse acceleration when it starts to factor into blur equation"};
+cvar_t r_motionblur_mousefactor_maxspeed = {CVAR_SAVE, "r_motionblur_mousefactor_maxspeed", "50", "upper value of mouse acceleration when it reaches the peak factor into blur equation"};
 
 // TODO do we want a r_equalize_entities cvar that works on all ents, or would that be a cheat?
 cvar_t r_equalize_entities_fullbright = {CVAR_SAVE, "r_equalize_entities_fullbright", "0", "render fullbright entities by equalizing their lightness, not by not rendering light"};
@@ -95,6 +99,7 @@ cvar_t r_cullentities_trace_samples = {0, "r_cullentities_trace_samples", "2", "
 cvar_t r_cullentities_trace_tempentitysamples = {0, "r_cullentities_trace_tempentitysamples", "-1", "number of samples to test for entity culling of temp entities (including all CSQC entities), -1 disables trace culling on these entities to prevent flicker (pvs still applies)"};
 cvar_t r_cullentities_trace_enlarge = {0, "r_cullentities_trace_enlarge", "0", "box enlargement for entity culling"};
 cvar_t r_cullentities_trace_delay = {0, "r_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"};
+cvar_t r_sortentities = {0, "r_sortentities", "0", "sort entities before drawing (might be faster)"};
 cvar_t r_speeds = {0, "r_speeds","0", "displays rendering statistics and per-subsystem timings"};
 cvar_t r_fullbright = {0, "r_fullbright","0", "makes map very bright and renders faster"};
 
@@ -122,6 +127,7 @@ cvar_t r_fog_exp2 = {0, "r_fog_exp2", "0", "uses GL_EXP2 fog (as in Nehahra) rat
 cvar_t r_fog_clear = {0, "r_fog_clear", "1", "clears renderbuffer with fog color before render starts"};
 cvar_t r_drawfog = {CVAR_SAVE, "r_drawfog", "1", "allows one to disable fog rendering"};
 cvar_t r_transparentdepthmasking = {CVAR_SAVE, "r_transparentdepthmasking", "0", "enables depth writes on transparent meshes whose materially is normally opaque, this prevents seeing the inside of a transparent mesh"};
+cvar_t r_transparent_sortmindist = {CVAR_SAVE, "r_transparent_sortmindist", "0", "lower distance limit for transparent sorting"};
 cvar_t r_transparent_sortmaxdist = {CVAR_SAVE, "r_transparent_sortmaxdist", "32768", "upper distance limit for transparent sorting"};
 cvar_t r_transparent_sortarraysize = {CVAR_SAVE, "r_transparent_sortarraysize", "4096", "number of distance-sorting layers"};
 
@@ -157,6 +163,8 @@ cvar_t r_glsl_offsetmapping_reliefmapping = {CVAR_SAVE, "r_glsl_offsetmapping_re
 cvar_t r_glsl_offsetmapping_reliefmapping_steps = {CVAR_SAVE, "r_glsl_offsetmapping_reliefmapping_steps", "10", "relief mapping steps (note: too high values may be not supported by your GPU)"};
 cvar_t r_glsl_offsetmapping_reliefmapping_refinesteps = {CVAR_SAVE, "r_glsl_offsetmapping_reliefmapping_refinesteps", "5", "relief mapping refine steps (these are a binary search executed as the last step as given by r_glsl_offsetmapping_reliefmapping_steps)"};
 cvar_t r_glsl_offsetmapping_scale = {CVAR_SAVE, "r_glsl_offsetmapping_scale", "0.04", "how deep the offset mapping effect is"};
+cvar_t r_glsl_offsetmapping_lod = {CVAR_SAVE, "r_glsl_offsetmapping_lod", "0", "apply distance-based level-of-detail correction to number of offsetmappig steps, effectively making it render faster on large open-area maps"};
+cvar_t r_glsl_offsetmapping_lod_distance = {CVAR_SAVE, "r_glsl_offsetmapping_lod_distance", "32", "first LOD level distance, second level (-50% steps) is 2x of this, third (33%) - 3x etc."};
 cvar_t r_glsl_postprocess = {CVAR_SAVE, "r_glsl_postprocess", "0", "use a GLSL postprocessing shader"};
 cvar_t r_glsl_postprocess_uservec1 = {CVAR_SAVE, "r_glsl_postprocess_uservec1", "0 0 0 0", "a 4-component vector to pass as uservec1 to the postprocessing shader (only useful if default.glsl has been customized)"};
 cvar_t r_glsl_postprocess_uservec2 = {CVAR_SAVE, "r_glsl_postprocess_uservec2", "0 0 0 0", "a 4-component vector to pass as uservec2 to the postprocessing shader (only useful if default.glsl has been customized)"};
@@ -174,6 +182,8 @@ cvar_t r_water_refractdistort = {CVAR_SAVE, "r_water_refractdistort", "0.01", "h
 cvar_t r_water_reflectdistort = {CVAR_SAVE, "r_water_reflectdistort", "0.01", "how much water reflections shimmer"};
 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"};
@@ -182,22 +192,23 @@ cvar_t r_waterscroll = {CVAR_SAVE, "r_waterscroll", "1", "makes water scroll aro
 
 cvar_t r_bloom = {CVAR_SAVE, "r_bloom", "0", "enables bloom effect (makes bright pixels affect neighboring pixels)"};
 cvar_t r_bloom_colorscale = {CVAR_SAVE, "r_bloom_colorscale", "1", "how bright the glow is"};
+
 cvar_t r_bloom_brighten = {CVAR_SAVE, "r_bloom_brighten", "2", "how bright the glow is, after subtract/power"};
 cvar_t r_bloom_blur = {CVAR_SAVE, "r_bloom_blur", "4", "how large the glow is"};
 cvar_t r_bloom_resolution = {CVAR_SAVE, "r_bloom_resolution", "320", "what resolution to perform the bloom effect at (independent of screen resolution)"};
 cvar_t r_bloom_colorexponent = {CVAR_SAVE, "r_bloom_colorexponent", "1", "how exaggerated the glow is"};
 cvar_t r_bloom_colorsubtract = {CVAR_SAVE, "r_bloom_colorsubtract", "0.125", "reduces bloom colors by a certain amount"};
 
-cvar_t r_hdr = {CVAR_SAVE, "r_hdr", "0", "enables High Dynamic Range bloom effect (higher quality version of r_bloom)"};
 cvar_t r_hdr_scenebrightness = {CVAR_SAVE, "r_hdr_scenebrightness", "1", "global rendering brightness"};
 cvar_t r_hdr_glowintensity = {CVAR_SAVE, "r_hdr_glowintensity", "1", "how bright light emitting textures should appear"};
-cvar_t r_hdr_range = {CVAR_SAVE, "r_hdr_range", "4", "how much dynamic range to render bloom with (equivalent to multiplying r_bloom_brighten by this value and dividing r_bloom_colorscale by this value)"};
 cvar_t r_hdr_irisadaptation = {CVAR_SAVE, "r_hdr_irisadaptation", "0", "adjust scene brightness according to light intensity at player location"};
 cvar_t r_hdr_irisadaptation_multiplier = {CVAR_SAVE, "r_hdr_irisadaptation_multiplier", "2", "brightness at which value will be 1.0"};
 cvar_t r_hdr_irisadaptation_minvalue = {CVAR_SAVE, "r_hdr_irisadaptation_minvalue", "0.5", "minimum value that can result from multiplier / brightness"};
 cvar_t r_hdr_irisadaptation_maxvalue = {CVAR_SAVE, "r_hdr_irisadaptation_maxvalue", "4", "maximum value that can result from multiplier / brightness"};
 cvar_t r_hdr_irisadaptation_value = {0, "r_hdr_irisadaptation_value", "1", "current value as scenebrightness multiplier, changes continuously when irisadaptation is active"};
-cvar_t r_hdr_irisadaptation_fade = {CVAR_SAVE, "r_hdr_irisadaptation_fade", "1", "fade rate at which value adjusts"};
+cvar_t r_hdr_irisadaptation_fade_up = {CVAR_SAVE, "r_hdr_irisadaptation_fade_up", "0.1", "fade rate at which value adjusts to darkness"};
+cvar_t r_hdr_irisadaptation_fade_down = {CVAR_SAVE, "r_hdr_irisadaptation_fade_down", "0.5", "fade rate at which value adjusts to brightness"};
+cvar_t r_hdr_irisadaptation_radius = {CVAR_SAVE, "r_hdr_irisadaptation_radius", "15", "lighting within this many units of the eye is averaged"};
 
 cvar_t r_smoothnormals_areaweighting = {0, "r_smoothnormals_areaweighting", "1", "uses significantly faster (and supposedly higher quality) area-weighted vertex normals and tangent vectors rather than summing normalized triangle normals and tangents"};
 
@@ -215,39 +226,11 @@ cvar_t r_glsl_vertextextureblend_usebothalphas = {CVAR_SAVE, "r_glsl_vertextextu
 cvar_t r_framedatasize = {CVAR_SAVE, "r_framedatasize", "0.5", "size of renderer data cache used during one frame (for skeletal animation caching, light processing, etc)"};
 
 extern cvar_t v_glslgamma;
+extern cvar_t v_glslgamma_2d;
 
 extern qboolean v_flipped_state;
 
-static struct r_bloomstate_s
-{
-       qboolean enabled;
-       qboolean hdr;
-
-       int bloomwidth, bloomheight;
-
-       textype_t texturetype;
-       int viewfbo; // used to check if r_viewfbo cvar has changed
-
-       int fbo_framebuffer; // non-zero if r_viewfbo is enabled and working
-       rtexture_t *texture_framebuffercolor; // non-NULL if fbo_screen is non-zero
-       rtexture_t *texture_framebufferdepth; // non-NULL if fbo_screen is non-zero
-
-       int screentexturewidth, screentextureheight;
-       rtexture_t *texture_screen; /// \note also used for motion blur if enabled!
-
-       int bloomtexturewidth, bloomtextureheight;
-       rtexture_t *texture_bloom;
-
-       // arrays for rendering the screen passes
-       float screentexcoord2f[8];
-       float bloomtexcoord2f[8];
-       float offsettexcoord2f[8];
-
-       r_viewport_t viewport;
-}
-r_bloomstate;
-
-r_waterstate_t r_waterstate;
+r_framebufferstate_t r_fb;
 
 /// shadow volume bsp struct with automatically growing nodes buffer
 svbsp_t r_svbsp;
@@ -365,7 +348,7 @@ static void R_BuildBlankTextures(void)
        data[2] = 128; // normal X
        data[1] = 128; // normal Y
        data[0] = 255; // normal Z
-       data[3] = 128; // height
+       data[3] = 255; // height
        r_texture_blanknormalmap = R_LoadTexture2D(r_main_texturepool, "blankbump", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, -1, NULL);
        data[0] = 255;
        data[1] = 255;
@@ -678,13 +661,15 @@ shadermodeinfo_t glslshadermodeinfo[SHADERMODE_COUNT] =
 {
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_GENERIC\n", " generic"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_POSTPROCESS\n", " postprocess"},
-       {"glsl/default.glsl", NULL, NULL               , "#define MODE_DEPTH_OR_SHADOW\n", " depth/shadow"},
+       {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_DEPTH_OR_SHADOW\n", " depth/shadow"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_FLATCOLOR\n", " flatcolor"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_VERTEXCOLOR\n", " vertexcolor"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTMAP\n", " lightmap"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_FAKELIGHT\n", " fakelight"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n", " lightdirectionmap_modelspace"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n", " lightdirectionmap_tangentspace"},
+       {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTDIRECTIONMAP_FORCED_LIGHTMAP\n", " lightdirectionmap_forced_lightmap"},
+       {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTDIRECTIONMAP_FORCED_VERTEXCOLOR\n", " lightdirectionmap_forced_vertexcolor"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTDIRECTION\n", " lightdirection"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTSOURCE\n", " lightsource"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_REFRACTION\n", " refraction"},
@@ -705,6 +690,8 @@ shadermodeinfo_t hlslshadermodeinfo[SHADERMODE_COUNT] =
        {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_FAKELIGHT\n", " fakelight"},
        {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n", " lightdirectionmap_modelspace"},
        {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n", " lightdirectionmap_tangentspace"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTDIRECTIONMAP_FORCED_LIGHTMAP\n", " lightdirectionmap_forced_lightmap"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTDIRECTIONMAP_FORCED_VERTEXCOLOR\n", " lightdirectionmap_forced_vertexcolor"},
        {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTDIRECTION\n", " lightdirection"},
        {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTSOURCE\n", " lightsource"},
        {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_REFRACTION\n", " refraction"},
@@ -813,6 +800,8 @@ typedef struct r_glsl_permutation_s
        int loc_LightDir;
        int loc_LightPosition;
        int loc_OffsetMapping_ScaleSteps;
+       int loc_OffsetMapping_LodDistance;
+       int loc_OffsetMapping_Bias;
        int loc_PixelSize;
        int loc_ReflectColor;
        int loc_ReflectFactor;
@@ -859,9 +848,10 @@ enum
        SHADERSTATICPARM_POSTPROCESS_USERVEC2 = 3, ///< postprocess uservec2 is enabled
        SHADERSTATICPARM_POSTPROCESS_USERVEC3 = 4, ///< postprocess uservec3 is enabled
        SHADERSTATICPARM_POSTPROCESS_USERVEC4 = 5,  ///< postprocess uservec4 is enabled
-       SHADERSTATICPARM_VERTEXTEXTUREBLEND_USEBOTHALPHAS = 6 // use both alpha layers while blending materials, allows more advanced microblending
+       SHADERSTATICPARM_VERTEXTEXTUREBLEND_USEBOTHALPHAS = 6, // use both alpha layers while blending materials, allows more advanced microblending
+       SHADERSTATICPARM_OFFSETMAPPING_USELOD = 7,  ///< LOD for offsetmapping
 };
-#define SHADERSTATICPARMS_COUNT 7
+#define SHADERSTATICPARMS_COUNT 8
 
 static const char *shaderstaticparmstrings_list[SHADERSTATICPARMS_COUNT];
 static int shaderstaticparms_count = 0;
@@ -892,6 +882,8 @@ qboolean R_CompileShader_CheckStaticParms(void)
                if (r_glsl_postprocess_uservec4_enable.integer)
                        R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_POSTPROCESS_USERVEC4);
        }
+       if (r_glsl_offsetmapping_lod.integer && r_glsl_offsetmapping_lod_distance.integer > 0)
+               R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_OFFSETMAPPING_USELOD);
        return memcmp(r_compileshader_staticparms, r_compileshader_staticparms_save, sizeof(r_compileshader_staticparms)) != 0;
 }
 
@@ -912,6 +904,7 @@ void R_CompileShader_AddStaticParms(unsigned int mode, unsigned int permutation)
        R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_POSTPROCESS_USERVEC3, "USERVEC3");
        R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_POSTPROCESS_USERVEC4, "USERVEC4");
        R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_VERTEXTEXTUREBLEND_USEBOTHALPHAS, "USEBOTHALPHAS");
+       R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_OFFSETMAPPING_USELOD, "USEOFFSETMAPPING_LOD");
 }
 
 /// information about each possible shader permutation
@@ -1130,6 +1123,8 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
                p->loc_LightDir                   = qglGetUniformLocation(p->program, "LightDir");
                p->loc_LightPosition              = qglGetUniformLocation(p->program, "LightPosition");
                p->loc_OffsetMapping_ScaleSteps   = qglGetUniformLocation(p->program, "OffsetMapping_ScaleSteps");
+               p->loc_OffsetMapping_LodDistance  = qglGetUniformLocation(p->program, "OffsetMapping_LodDistance");
+               p->loc_OffsetMapping_Bias         = qglGetUniformLocation(p->program, "OffsetMapping_Bias");
                p->loc_PixelSize                  = qglGetUniformLocation(p->program, "PixelSize");
                p->loc_ReflectColor               = qglGetUniformLocation(p->program, "ReflectColor");
                p->loc_ReflectFactor              = qglGetUniformLocation(p->program, "ReflectFactor");
@@ -1369,7 +1364,9 @@ typedef enum D3DPSREGISTER_e
        D3DPSREGISTER_ViewToLight = 44, // float4x4
        D3DPSREGISTER_ModelToReflectCube = 48, // float4x4
        D3DPSREGISTER_NormalmapScrollBlend = 52,
-       // next at 53
+       D3DPSREGISTER_OffsetMapping_LodDistance = 53,
+       D3DPSREGISTER_OffsetMapping_Bias = 54,
+       // next at 54
 }
 D3DPSREGISTER_t;
 
@@ -1529,7 +1526,7 @@ static void R_HLSL_CacheShader(r_hlsl_permutation_t *p, const char *cachename, c
                                if (vslog)
                                {
                                        strlcpy(temp, (const char *)vslog->GetBufferPointer(), min(sizeof(temp), vslog->GetBufferSize()));
-                                       Con_Printf("HLSL vertex shader compile output for %s follows:\n%s\n", cachename, temp);
+                                       Con_DPrintf("HLSL vertex shader compile output for %s follows:\n%s\n", cachename, temp);
                                        vslog->Release();
                                }
                        }
@@ -1554,23 +1551,23 @@ static void R_HLSL_CacheShader(r_hlsl_permutation_t *p, const char *cachename, c
                                if (pslog)
                                {
                                        strlcpy(temp, (const char *)pslog->GetBufferPointer(), min(sizeof(temp), pslog->GetBufferSize()));
-                                       Con_Printf("HLSL pixel shader compile output for %s follows:\n%s\n", cachename, temp);
+                                       Con_DPrintf("HLSL pixel shader compile output for %s follows:\n%s\n", cachename, temp);
                                        pslog->Release();
                                }
                        }
                        Sys_UnloadLibrary(&d3dx9_dll);
                }
                else
-                       Con_Printf("Unable to compile shader - D3DXCompileShader function not found\n");
+                       Con_DPrintf("Unable to compile shader - D3DXCompileShader function not found\n");
        }
        if (vsbin && psbin)
        {
                vsresult = IDirect3DDevice9_CreateVertexShader(vid_d3d9dev, vsbin, &p->vertexshader);
                if (FAILED(vsresult))
-                       Con_Printf("HLSL CreateVertexShader failed for %s (hresult = %8x)\n", cachename, vsresult);
+                       Con_DPrintf("HLSL CreateVertexShader failed for %s (hresult = %8x)\n", cachename, vsresult);
                psresult = IDirect3DDevice9_CreatePixelShader(vid_d3d9dev, psbin, &p->pixelshader);
                if (FAILED(psresult))
-                       Con_Printf("HLSL CreatePixelShader failed for %s (hresult = %8x)\n", cachename, psresult);
+                       Con_DPrintf("HLSL CreatePixelShader failed for %s (hresult = %8x)\n", cachename, psresult);
        }
        // free the shader data
        vsbin = (DWORD *)Mem_Realloc(tempmempool, vsbin, 0);
@@ -1893,7 +1890,7 @@ void R_GLSL_DumpShader_f(void)
                Con_Printf("failed to write to hlsl/default.hlsl\n");
 }
 
-void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemode, int rgbscale, qboolean notrippy)
+void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemode, int rgbscale, qboolean usegamma, qboolean notrippy, qboolean suppresstexalpha)
 {
        unsigned int permutation = 0;
        if (r_trippy.integer && !notrippy)
@@ -1909,6 +1906,10 @@ 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)
+               permutation |= SHADERPERMUTATION_GAMMARAMPS;
+       if (suppresstexalpha)
+               permutation |= SHADERPERMUTATION_REFLECTCUBE;
        if (!second)
                texturemode = GL_MODULATE;
        if (vid.allowalphatocoverage)
@@ -1920,6 +1921,8 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod
                R_SetupShader_SetPermutationHLSL(SHADERMODE_GENERIC, permutation);
                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);
 #endif
                break;
        case RENDERPATH_D3D10:
@@ -1933,6 +1936,8 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod
                R_SetupShader_SetPermutationGLSL(SHADERMODE_GENERIC, permutation);
                R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , first );
                R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second, second);
+               if (r_glsl_permutation->tex_Texture_GammaRamps >= 0)
+                       R_Mesh_TexBind(r_glsl_permutation->tex_Texture_GammaRamps, r_texture_gammaramps);
                break;
        case RENDERPATH_GL13:
        case RENDERPATH_GLES1:
@@ -1953,6 +1958,11 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod
        }
 }
 
+void R_SetupShader_Generic_NoTexture(qboolean usegamma, qboolean notrippy)
+{
+       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, usegamma, notrippy, false);
+}
+
 void R_SetupShader_DepthOrShadow(qboolean notrippy)
 {
        unsigned int permutation = 0;
@@ -1996,8 +2006,6 @@ void R_SetupShader_ShowDepth(qboolean notrippy)
        int permutation = 0;
        if (r_trippy.integer && !notrippy)
                permutation |= SHADERPERMUTATION_TRIPPY;
-       if (r_trippy.integer)
-               permutation |= SHADERPERMUTATION_TRIPPY;
        if (vid.allowalphatocoverage)
                GL_AlphaToCoverage(false);
        switch (vid.renderpath)
@@ -2156,7 +2164,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        }
        else if (rsurfacepass == RSURFPASS_DEFERREDGEOMETRY)
        {
-               if (r_glsl_offsetmapping.integer)
+               if (r_glsl_offsetmapping.integer && ((R_TextureFlags(rsurface.texture->nmaptexture) & TEXF_ALPHA) || rsurface.texture->offsetbias != 0.0f))
                {
                        switch(rsurface.texture->offsetmapping)
                        {
@@ -2177,7 +2185,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        }
        else if (rsurfacepass == RSURFPASS_RTLIGHT)
        {
-               if (r_glsl_offsetmapping.integer)
+               if (r_glsl_offsetmapping.integer && ((R_TextureFlags(rsurface.texture->nmaptexture) & TEXF_ALPHA) || rsurface.texture->offsetbias != 0.0f))
                {
                        switch(rsurface.texture->offsetmapping)
                        {
@@ -2223,7 +2231,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
        {
-               if (r_glsl_offsetmapping.integer)
+               if (r_glsl_offsetmapping.integer && ((R_TextureFlags(rsurface.texture->nmaptexture) & TEXF_ALPHA) || rsurface.texture->offsetbias != 0.0f))
                {
                        switch(rsurface.texture->offsetmapping)
                        {
@@ -2276,7 +2284,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT_DIRECTIONAL)
        {
-               if (r_glsl_offsetmapping.integer)
+               if (r_glsl_offsetmapping.integer && ((R_TextureFlags(rsurface.texture->nmaptexture) & TEXF_ALPHA) || rsurface.texture->offsetbias != 0.0f))
                {
                        switch(rsurface.texture->offsetmapping)
                        {
@@ -2317,7 +2325,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        permutation |= SHADERPERMUTATION_DEFERREDLIGHTMAP;
                if (rsurface.texture->reflectmasktexture)
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
-               if (r_shadow_bouncegridtexture)
+               if (r_shadow_bouncegridtexture && cl.csqc_vidvars.drawworld)
                {
                        permutation |= SHADERPERMUTATION_BOUNCEGRID;
                        if (r_shadow_bouncegriddirectional)
@@ -2339,7 +2347,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
        {
-               if (r_glsl_offsetmapping.integer)
+               if (r_glsl_offsetmapping.integer && ((R_TextureFlags(rsurface.texture->nmaptexture) & TEXF_ALPHA) || rsurface.texture->offsetbias != 0.0f))
                {
                        switch(rsurface.texture->offsetmapping)
                        {
@@ -2377,7 +2385,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        permutation |= SHADERPERMUTATION_DEFERREDLIGHTMAP;
                if (rsurface.texture->reflectmasktexture)
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
-               if (r_shadow_bouncegridtexture)
+               if (r_shadow_bouncegridtexture && cl.csqc_vidvars.drawworld)
                {
                        permutation |= SHADERPERMUTATION_BOUNCEGRID;
                        if (r_shadow_bouncegriddirectional)
@@ -2399,7 +2407,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        }
        else
        {
-               if (r_glsl_offsetmapping.integer)
+               if (r_glsl_offsetmapping.integer && ((R_TextureFlags(rsurface.texture->nmaptexture) & TEXF_ALPHA) || rsurface.texture->offsetbias != 0.0f))
                {
                        switch(rsurface.texture->offsetmapping)
                        {
@@ -2455,10 +2463,13 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        if (specularscale > 0)
                                permutation |= SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_DIFFUSE;
                }
-               else if (r_glsl_deluxemapping.integer >= 2 && rsurface.uselightmaptexture)
+               else if (r_glsl_deluxemapping.integer >= 2)
                {
                        // fake deluxemapping (uniform light direction in tangentspace)
-                       mode = SHADERMODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
+                       if (rsurface.uselightmaptexture)
+                               mode = SHADERMODE_LIGHTDIRECTIONMAP_FORCED_LIGHTMAP;
+                       else
+                               mode = SHADERMODE_LIGHTDIRECTIONMAP_FORCED_VERTEXCOLOR;
                        permutation |= SHADERPERMUTATION_DIFFUSE;
                        if (specularscale > 0)
                                permutation |= SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_DIFFUSE;
@@ -2473,7 +2484,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        // ordinary vertex coloring (q3bsp)
                        mode = SHADERMODE_VERTEXCOLOR;
                }
-               if (r_shadow_bouncegridtexture)
+               if (r_shadow_bouncegridtexture && cl.csqc_vidvars.drawworld)
                {
                        permutation |= SHADERPERMUTATION_BOUNCEGRID;
                        if (r_shadow_bouncegriddirectional)
@@ -2535,7 +2546,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
 
                        // additive passes are only darkened by fog, not tinted
                        hlslPSSetParameter3f(D3DPSREGISTER_FogColor, 0, 0, 0);
-                       hlslPSSetParameter1f(D3DPSREGISTER_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+                       hlslPSSetParameter1f(D3DPSREGISTER_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f) - 1.0f);
                }
                else
                {
@@ -2567,20 +2578,20 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        else
                                hlslPSSetParameter3f(D3DPSREGISTER_FogColor, r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2]);
                        hlslPSSetParameter4f(D3DPSREGISTER_DistortScaleRefractReflect, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor);
-                       hlslPSSetParameter4f(D3DPSREGISTER_ScreenScaleRefractReflect, r_waterstate.screenscale[0], r_waterstate.screenscale[1], r_waterstate.screenscale[0], r_waterstate.screenscale[1]);
-                       hlslPSSetParameter4f(D3DPSREGISTER_ScreenCenterRefractReflect, r_waterstate.screencenter[0], r_waterstate.screencenter[1], r_waterstate.screencenter[0], r_waterstate.screencenter[1]);
+                       hlslPSSetParameter4f(D3DPSREGISTER_ScreenScaleRefractReflect, r_fb.water.screenscale[0], r_fb.water.screenscale[1], r_fb.water.screenscale[0], r_fb.water.screenscale[1]);
+                       hlslPSSetParameter4f(D3DPSREGISTER_ScreenCenterRefractReflect, r_fb.water.screencenter[0], r_fb.water.screencenter[1], r_fb.water.screencenter[0], r_fb.water.screencenter[1]);
                        hlslPSSetParameter4f(D3DPSREGISTER_RefractColor, rsurface.texture->refractcolor4f[0], rsurface.texture->refractcolor4f[1], rsurface.texture->refractcolor4f[2], rsurface.texture->refractcolor4f[3] * rsurface.texture->lightmapcolor[3]);
                        hlslPSSetParameter4f(D3DPSREGISTER_ReflectColor, rsurface.texture->reflectcolor4f[0], rsurface.texture->reflectcolor4f[1], rsurface.texture->reflectcolor4f[2], rsurface.texture->reflectcolor4f[3] * rsurface.texture->lightmapcolor[3]);
                        hlslPSSetParameter1f(D3DPSREGISTER_ReflectFactor, rsurface.texture->reflectmax - rsurface.texture->reflectmin);
                        hlslPSSetParameter1f(D3DPSREGISTER_ReflectOffset, rsurface.texture->reflectmin);
-                       hlslPSSetParameter1f(D3DPSREGISTER_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+                       hlslPSSetParameter1f(D3DPSREGISTER_SpecularPower, (rsurface.texture->specularpower - 1.0f) * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
                        if (mode == SHADERMODE_WATER)
                                hlslPSSetParameter2f(D3DPSREGISTER_NormalmapScrollBlend, rsurface.texture->r_water_waterscroll[0], rsurface.texture->r_water_waterscroll[1]);
                }
                hlslPSSetParameter2f(D3DPSREGISTER_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
                hlslPSSetParameter4f(D3DPSREGISTER_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
                hlslPSSetParameter3f(D3DPSREGISTER_Color_Glow, rsurface.glowmod[0], rsurface.glowmod[1], rsurface.glowmod[2]);
-               hlslPSSetParameter1f(D3DPSREGISTER_Alpha, rsurface.texture->lightmapcolor[3] * ((rsurface.texture->basematerialflags & MATERIALFLAG_WATERSHADER && r_waterstate.enabled && !r_refdef.view.isoverlay) ? rsurface.texture->r_water_wateralpha : 1));
+               hlslPSSetParameter1f(D3DPSREGISTER_Alpha, rsurface.texture->lightmapcolor[3] * ((rsurface.texture->basematerialflags & MATERIALFLAG_WATERSHADER && r_fb.water.enabled && !r_refdef.view.isoverlay) ? rsurface.texture->r_water_wateralpha : 1));
                hlslPSSetParameter3f(D3DPSREGISTER_EyePosition, rsurface.localvieworigin[0], rsurface.localvieworigin[1], rsurface.localvieworigin[2]);
                if (rsurface.texture->pantstexture)
                        hlslPSSetParameter3f(D3DPSREGISTER_Color_Pants, rsurface.colormap_pantscolor[0], rsurface.colormap_pantscolor[1], rsurface.colormap_pantscolor[2]);
@@ -2600,6 +2611,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                                1.0 / max(1, (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING) ? r_glsl_offsetmapping_reliefmapping_steps.integer : r_glsl_offsetmapping_steps.integer),
                                max(1, r_glsl_offsetmapping_reliefmapping_refinesteps.integer)
                        );
+               hlslPSSetParameter1f(D3DPSREGISTER_OffsetMapping_LodDistance, r_glsl_offsetmapping_lod_distance.integer);
+               hlslPSSetParameter1f(D3DPSREGISTER_OffsetMapping_Bias, rsurface.texture->offsetbias);
                hlslPSSetParameter2f(D3DPSREGISTER_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
                hlslPSSetParameter2f(D3DPSREGISTER_PixelToScreenTexCoord, 1.0f/vid.width, 1.0/vid.height);
 
@@ -2683,7 +2696,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        // additive passes are only darkened by fog, not tinted
                        if (r_glsl_permutation->loc_FogColor >= 0)
                                qglUniform3f(r_glsl_permutation->loc_FogColor, 0, 0, 0);
-                       if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1f(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+                       if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1f(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f) - 1.0f);
                }
                else
                {
@@ -2718,13 +2731,13 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                                        qglUniform3f(r_glsl_permutation->loc_FogColor, r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2]);
                        }
                        if (r_glsl_permutation->loc_DistortScaleRefractReflect >= 0) qglUniform4f(r_glsl_permutation->loc_DistortScaleRefractReflect, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor);
-                       if (r_glsl_permutation->loc_ScreenScaleRefractReflect >= 0) qglUniform4f(r_glsl_permutation->loc_ScreenScaleRefractReflect, r_waterstate.screenscale[0], r_waterstate.screenscale[1], r_waterstate.screenscale[0], r_waterstate.screenscale[1]);
-                       if (r_glsl_permutation->loc_ScreenCenterRefractReflect >= 0) qglUniform4f(r_glsl_permutation->loc_ScreenCenterRefractReflect, r_waterstate.screencenter[0], r_waterstate.screencenter[1], r_waterstate.screencenter[0], r_waterstate.screencenter[1]);
+                       if (r_glsl_permutation->loc_ScreenScaleRefractReflect >= 0) qglUniform4f(r_glsl_permutation->loc_ScreenScaleRefractReflect, r_fb.water.screenscale[0], r_fb.water.screenscale[1], r_fb.water.screenscale[0], r_fb.water.screenscale[1]);
+                       if (r_glsl_permutation->loc_ScreenCenterRefractReflect >= 0) qglUniform4f(r_glsl_permutation->loc_ScreenCenterRefractReflect, r_fb.water.screencenter[0], r_fb.water.screencenter[1], r_fb.water.screencenter[0], r_fb.water.screencenter[1]);
                        if (r_glsl_permutation->loc_RefractColor >= 0) qglUniform4f(r_glsl_permutation->loc_RefractColor, rsurface.texture->refractcolor4f[0], rsurface.texture->refractcolor4f[1], rsurface.texture->refractcolor4f[2], rsurface.texture->refractcolor4f[3] * rsurface.texture->lightmapcolor[3]);
                        if (r_glsl_permutation->loc_ReflectColor >= 0) qglUniform4f(r_glsl_permutation->loc_ReflectColor, rsurface.texture->reflectcolor4f[0], rsurface.texture->reflectcolor4f[1], rsurface.texture->reflectcolor4f[2], rsurface.texture->reflectcolor4f[3] * rsurface.texture->lightmapcolor[3]);
                        if (r_glsl_permutation->loc_ReflectFactor >= 0) qglUniform1f(r_glsl_permutation->loc_ReflectFactor, rsurface.texture->reflectmax - rsurface.texture->reflectmin);
                        if (r_glsl_permutation->loc_ReflectOffset >= 0) qglUniform1f(r_glsl_permutation->loc_ReflectOffset, rsurface.texture->reflectmin);
-                       if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1f(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+                       if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1f(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f) - 1.0f);
                        if (r_glsl_permutation->loc_NormalmapScrollBlend >= 0) qglUniform2f(r_glsl_permutation->loc_NormalmapScrollBlend, rsurface.texture->r_water_waterscroll[0], rsurface.texture->r_water_waterscroll[1]);
                }
                if (r_glsl_permutation->loc_TexMatrix >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currenttexmatrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_TexMatrix, 1, false, m16f);}
@@ -2734,7 +2747,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                if (r_glsl_permutation->loc_ShadowMap_Parameters >= 0) qglUniform4f(r_glsl_permutation->loc_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
 
                if (r_glsl_permutation->loc_Color_Glow >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Glow, rsurface.glowmod[0], rsurface.glowmod[1], rsurface.glowmod[2]);
-               if (r_glsl_permutation->loc_Alpha >= 0) qglUniform1f(r_glsl_permutation->loc_Alpha, rsurface.texture->lightmapcolor[3] * ((rsurface.texture->basematerialflags & MATERIALFLAG_WATERSHADER && r_waterstate.enabled && !r_refdef.view.isoverlay) ? rsurface.texture->r_water_wateralpha : 1));
+               if (r_glsl_permutation->loc_Alpha >= 0) qglUniform1f(r_glsl_permutation->loc_Alpha, rsurface.texture->lightmapcolor[3] * ((rsurface.texture->basematerialflags & MATERIALFLAG_WATERSHADER && r_fb.water.enabled && !r_refdef.view.isoverlay) ? rsurface.texture->r_water_wateralpha : 1));
                if (r_glsl_permutation->loc_EyePosition >= 0) qglUniform3f(r_glsl_permutation->loc_EyePosition, rsurface.localvieworigin[0], rsurface.localvieworigin[1], rsurface.localvieworigin[2]);
                if (r_glsl_permutation->loc_Color_Pants >= 0)
                {
@@ -2760,6 +2773,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                                1.0 / max(1, (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING) ? r_glsl_offsetmapping_reliefmapping_steps.integer : r_glsl_offsetmapping_steps.integer),
                                max(1, r_glsl_offsetmapping_reliefmapping_refinesteps.integer)
                        );
+               if (r_glsl_permutation->loc_OffsetMapping_LodDistance >= 0) qglUniform1f(r_glsl_permutation->loc_OffsetMapping_LodDistance, r_glsl_offsetmapping_lod_distance.integer);
+               if (r_glsl_permutation->loc_OffsetMapping_Bias >= 0) qglUniform1f(r_glsl_permutation->loc_OffsetMapping_Bias, rsurface.texture->offsetbias);
                if (r_glsl_permutation->loc_ScreenToDepth >= 0) qglUniform2f(r_glsl_permutation->loc_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
                if (r_glsl_permutation->loc_PixelToScreenTexCoord >= 0) qglUniform2f(r_glsl_permutation->loc_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
                if (r_glsl_permutation->loc_BounceGridMatrix >= 0) {Matrix4x4_Concat(&tempmatrix, &r_shadow_bouncegridmatrix, &rsurface.matrix);Matrix4x4_ToArrayFloatGL(&tempmatrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_BounceGridMatrix, 1, false, m16f);}
@@ -2831,7 +2846,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        
                        // additive passes are only darkened by fog, not tinted
                        DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_FogColor, 0, 0, 0);
-                       DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+                       DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f) - 1.0f);
                }
                else
                {
@@ -2863,13 +2878,13 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        else
                                DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_FogColor, r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2]);
                        DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_DistortScaleRefractReflect, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor);
-                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ScreenScaleRefractReflect, r_waterstate.screenscale[0], r_waterstate.screenscale[1], r_waterstate.screenscale[0], r_waterstate.screenscale[1]);
-                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ScreenCenterRefractReflect, r_waterstate.screencenter[0], r_waterstate.screencenter[1], r_waterstate.screencenter[0], r_waterstate.screencenter[1]);
+                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ScreenScaleRefractReflect, r_fb.water.screenscale[0], r_fb.water.screenscale[1], r_fb.water.screenscale[0], r_fb.water.screenscale[1]);
+                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ScreenCenterRefractReflect, r_fb.water.screencenter[0], r_fb.water.screencenter[1], r_fb.water.screencenter[0], r_fb.water.screencenter[1]);
                        DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_RefractColor, rsurface.texture->refractcolor4f[0], rsurface.texture->refractcolor4f[1], rsurface.texture->refractcolor4f[2], rsurface.texture->refractcolor4f[3] * rsurface.texture->lightmapcolor[3]);
                        DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ReflectColor, rsurface.texture->reflectcolor4f[0], rsurface.texture->reflectcolor4f[1], rsurface.texture->reflectcolor4f[2], rsurface.texture->reflectcolor4f[3] * rsurface.texture->lightmapcolor[3]);
                        DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_ReflectFactor, rsurface.texture->reflectmax - rsurface.texture->reflectmin);
                        DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_ReflectOffset, rsurface.texture->reflectmin);
-                       DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+                       DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f) - 1.0f);
                        DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_NormalmapScrollBlend, rsurface.texture->r_water_waterscroll[0], rsurface.texture->r_water_waterscroll[1]);
                }
                {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currenttexmatrix, m16f);DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_TexMatrixM1, 1, false, m16f);}
@@ -2879,7 +2894,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
 
                DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Glow, rsurface.glowmod[0], rsurface.glowmod[1], rsurface.glowmod[2]);
-               DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_Alpha, rsurface.texture->lightmapcolor[3] * ((rsurface.texture->basematerialflags & MATERIALFLAG_WATERSHADER && r_waterstate.enabled && !r_refdef.view.isoverlay) ? rsurface.texture->r_water_wateralpha : 1));
+               DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_Alpha, rsurface.texture->lightmapcolor[3] * ((rsurface.texture->basematerialflags & MATERIALFLAG_WATERSHADER && r_fb.water.enabled && !r_refdef.view.isoverlay) ? rsurface.texture->r_water_wateralpha : 1));
                DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_EyePosition, rsurface.localvieworigin[0], rsurface.localvieworigin[1], rsurface.localvieworigin[2]);
                if (DPSOFTRAST_UNIFORM_Color_Pants >= 0)
                {
@@ -2905,6 +2920,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                                1.0 / max(1, (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING) ? r_glsl_offsetmapping_reliefmapping_steps.integer : r_glsl_offsetmapping_steps.integer),
                                max(1, r_glsl_offsetmapping_reliefmapping_refinesteps.integer)
                        );
+               DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_OffsetMapping_LodDistance, r_glsl_offsetmapping_lod_distance.integer);
+               DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_OffsetMapping_Bias, rsurface.texture->offsetbias);
                DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
                DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
 
@@ -3010,7 +3027,7 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight)
                hlslPSSetParameter3f(D3DPSREGISTER_DeferredColor_Specular, lightcolorbase[0] * specularscale * range, lightcolorbase[1] * specularscale * range, lightcolorbase[2] * specularscale * range);
                hlslPSSetParameter2f(D3DPSREGISTER_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
                hlslPSSetParameter4f(D3DPSREGISTER_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
-               hlslPSSetParameter1f(D3DPSREGISTER_SpecularPower, (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+               hlslPSSetParameter1f(D3DPSREGISTER_SpecularPower, (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 0.25f : 1.0f) - 1.0f);
                hlslPSSetParameter2f(D3DPSREGISTER_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
                hlslPSSetParameter2f(D3DPSREGISTER_PixelToScreenTexCoord, 1.0f/vid.width, 1.0/vid.height);
 
@@ -3038,7 +3055,7 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight)
                if (r_glsl_permutation->loc_DeferredColor_Specular    >= 0) qglUniform3f(       r_glsl_permutation->loc_DeferredColor_Specular   , lightcolorbase[0] * specularscale * range, lightcolorbase[1] * specularscale * range, lightcolorbase[2] * specularscale * range);
                if (r_glsl_permutation->loc_ShadowMap_TextureScale    >= 0) qglUniform2f(       r_glsl_permutation->loc_ShadowMap_TextureScale   , r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
                if (r_glsl_permutation->loc_ShadowMap_Parameters      >= 0) qglUniform4f(       r_glsl_permutation->loc_ShadowMap_Parameters     , r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
-               if (r_glsl_permutation->loc_SpecularPower             >= 0) qglUniform1f(       r_glsl_permutation->loc_SpecularPower            , (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+               if (r_glsl_permutation->loc_SpecularPower             >= 0) qglUniform1f(       r_glsl_permutation->loc_SpecularPower            , (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 0.25f : 1.0f) - 1.0f);
                if (r_glsl_permutation->loc_ScreenToDepth             >= 0) qglUniform2f(       r_glsl_permutation->loc_ScreenToDepth            , r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
                if (r_glsl_permutation->loc_PixelToScreenTexCoord     >= 0) qglUniform2f(       r_glsl_permutation->loc_PixelToScreenTexCoord    , 1.0f/vid.width, 1.0f/vid.height);
 
@@ -3062,7 +3079,7 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight)
                DPSOFTRAST_Uniform3f(       DPSOFTRAST_UNIFORM_DeferredColor_Specular   , lightcolorbase[0] * specularscale * range, lightcolorbase[1] * specularscale * range, lightcolorbase[2] * specularscale * range);
                DPSOFTRAST_Uniform2f(       DPSOFTRAST_UNIFORM_ShadowMap_TextureScale   , r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
                DPSOFTRAST_Uniform4f(       DPSOFTRAST_UNIFORM_ShadowMap_Parameters     , r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
-               DPSOFTRAST_Uniform1f(       DPSOFTRAST_UNIFORM_SpecularPower            , (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+               DPSOFTRAST_Uniform1f(       DPSOFTRAST_UNIFORM_SpecularPower            , (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 0.25f : 1.0f) - 1.0f);
                DPSOFTRAST_Uniform2f(       DPSOFTRAST_UNIFORM_ScreenToDepth            , r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
                DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
 
@@ -3165,7 +3182,7 @@ skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewid
 
        hashindex = CRC_Block((unsigned char *)basename, strlen(basename)) & (SKINFRAME_HASH - 1);
        for (item = r_skinframe.hash[hashindex];item;item = item->next)
-               if (!strcmp(item->basename, basename) && item->textureflags == textureflags && item->comparewidth == comparewidth && item->compareheight == compareheight && item->comparecrc == comparecrc)
+               if (!strcmp(item->basename, basename) && (comparecrc < 0 || (item->textureflags == textureflags && item->comparewidth == comparewidth && item->compareheight == compareheight && item->comparecrc == comparecrc)))
                        break;
 
        if (!item) {
@@ -3178,13 +3195,35 @@ skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewid
                memset(item, 0, sizeof(*item));
                strlcpy(item->basename, basename, sizeof(item->basename));
                item->base = dyntexture; // either NULL or dyntexture handle
-               item->textureflags = textureflags;
+               item->textureflags = textureflags & ~TEXF_FORCE_RELOAD;
                item->comparewidth = comparewidth;
                item->compareheight = compareheight;
                item->comparecrc = comparecrc;
                item->next = r_skinframe.hash[hashindex];
                r_skinframe.hash[hashindex] = item;
        }
+       else if (textureflags & TEXF_FORCE_RELOAD)
+       {
+               rtexture_t *dyntexture;
+               // check whether its a dynamic texture
+               dyntexture = CL_GetDynTexture( basename );
+               if (!add && !dyntexture)
+                       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;
@@ -3264,7 +3303,7 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        Image_StripImageExtension(name, basename, sizeof(basename));
 
        // check for DDS texture file first
-       if (!r_loaddds || !(ddsbase = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s.dds", basename), textureflags, &ddshasalpha, ddsavgcolor, miplevel)))
+       if (!r_loaddds || !(ddsbase = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s.dds", basename), vid.sRGB3D, textureflags, &ddshasalpha, ddsavgcolor, miplevel)))
        {
                basepixels = loadimagepixelsbgra(name, complain, true, false, &miplevel);
                if (basepixels == NULL)
@@ -3279,6 +3318,7 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        // we've got some pixels to store, so really allocate this new texture now
        if (!skinframe)
                skinframe = R_SkinFrame_Find(name, textureflags, 0, 0, 0, true);
+       textureflags &= ~TEXF_FORCE_RELOAD;
        skinframe->stain = NULL;
        skinframe->merged = NULL;
        skinframe->base = NULL;
@@ -3297,7 +3337,7 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                skinframe->hasalpha = ddshasalpha;
                VectorCopy(ddsavgcolor, skinframe->avgcolor);
                if (r_loadfog && skinframe->hasalpha)
-                       skinframe->fog = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_mask.dds", skinframe->basename), textureflags | TEXF_ALPHA, NULL, NULL, miplevel);
+                       skinframe->fog = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_mask.dds", skinframe->basename), false, textureflags | TEXF_ALPHA, NULL, NULL, miplevel);
                //Con_Printf("Texture %s has average colors %f %f %f alpha %f\n", name, skinframe->avgcolor[0], skinframe->avgcolor[1], skinframe->avgcolor[2], skinframe->avgcolor[3]);
        }
        else
@@ -3331,24 +3371,26 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                        }
                }
                R_SKINFRAME_LOAD_AVERAGE_COLORS(basepixels_width * basepixels_height, basepixels[4 * pix + comp]);
+#ifndef USE_GLES2
                //Con_Printf("Texture %s has average colors %f %f %f alpha %f\n", name, skinframe->avgcolor[0], skinframe->avgcolor[1], skinframe->avgcolor[2], skinframe->avgcolor[3]);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->base)
                        R_SaveTextureDDSFile(skinframe->base, va("dds/%s.dds", skinframe->basename), r_texture_dds_save.integer < 2, skinframe->hasalpha);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->fog)
                        R_SaveTextureDDSFile(skinframe->fog, va("dds/%s_mask.dds", skinframe->basename), r_texture_dds_save.integer < 2, true);
+#endif
        }
 
        if (r_loaddds)
        {
                mymiplevel = savemiplevel;
                if (r_loadnormalmap)
-                       skinframe->nmap = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_norm.dds", skinframe->basename), (TEXF_ALPHA | textureflags) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP), NULL, NULL, mymiplevel);
-               skinframe->glow = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_glow.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
+                       skinframe->nmap = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_norm.dds", skinframe->basename), false, (TEXF_ALPHA | textureflags) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP), NULL, NULL, mymiplevel);
+               skinframe->glow = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_glow.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel);
                if (r_loadgloss)
-                       skinframe->gloss = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_gloss.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
-               skinframe->pants = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_pants.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
-               skinframe->shirt = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_shirt.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
-               skinframe->reflect = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_reflect.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
+                       skinframe->gloss = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_gloss.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel);
+               skinframe->pants = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_pants.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel);
+               skinframe->shirt = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_shirt.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel);
+               skinframe->reflect = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_reflect.dds", skinframe->basename), vid.sRGB3D, textureflags, NULL, NULL, mymiplevel);
        }
 
        // _norm is the name used by tenebrae and has been adopted as standard
@@ -3376,8 +3418,10 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                        skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), basepixels_width, basepixels_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | textureflags) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP) & (gl_texturecompression_normal.integer && gl_texturecompression.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                        Mem_Free(pixels);
                }
+#ifndef USE_GLES2
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->nmap)
                        R_SaveTextureDDSFile(skinframe->nmap, va("dds/%s_norm.dds", skinframe->basename), r_texture_dds_save.integer < 2, true);
+#endif
        }
 
        // _luma is supported only for tenebrae compatibility
@@ -3386,17 +3430,21 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        if (skinframe->glow == NULL && ((pixels = loadimagepixelsbgra(va("%s_glow",  skinframe->basename), false, false, false, &mymiplevel)) || (pixels = loadimagepixelsbgra(va("%s_luma", skinframe->basename), false, false, false, &mymiplevel))))
        {
                skinframe->glow = R_LoadTexture2D (r_main_texturepool, va("%s_glow", skinframe->basename), image_width, image_height, pixels, vid.sRGB3D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, textureflags & (gl_texturecompression_glow.integer && gl_texturecompression.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
+#ifndef USE_GLES2
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->glow)
                        R_SaveTextureDDSFile(skinframe->glow, va("dds/%s_glow.dds", skinframe->basename), r_texture_dds_save.integer < 2, true);
+#endif
                Mem_Free(pixels);pixels = NULL;
        }
 
        mymiplevel = savemiplevel;
        if (skinframe->gloss == NULL && r_loadgloss && (pixels = loadimagepixelsbgra(va("%s_gloss", skinframe->basename), false, false, false, &mymiplevel)))
        {
-               skinframe->gloss = R_LoadTexture2D (r_main_texturepool, va("%s_gloss", skinframe->basename), image_width, image_height, pixels, vid.sRGB3D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, textureflags & (gl_texturecompression_gloss.integer && gl_texturecompression.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
+               skinframe->gloss = R_LoadTexture2D (r_main_texturepool, va("%s_gloss", skinframe->basename), image_width, image_height, pixels, vid.sRGB3D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, (TEXF_ALPHA | textureflags) & (gl_texturecompression_gloss.integer && gl_texturecompression.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
+#ifndef USE_GLES2
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->gloss)
                        R_SaveTextureDDSFile(skinframe->gloss, va("dds/%s_gloss.dds", skinframe->basename), r_texture_dds_save.integer < 2, true);
+#endif
                Mem_Free(pixels);
                pixels = NULL;
        }
@@ -3405,8 +3453,10 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        if (skinframe->pants == NULL && (pixels = loadimagepixelsbgra(va("%s_pants", skinframe->basename), false, false, false, &mymiplevel)))
        {
                skinframe->pants = R_LoadTexture2D (r_main_texturepool, va("%s_pants", skinframe->basename), image_width, image_height, pixels, vid.sRGB3D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, textureflags & (gl_texturecompression_color.integer && gl_texturecompression.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
+#ifndef USE_GLES2
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->pants)
                        R_SaveTextureDDSFile(skinframe->pants, va("dds/%s_pants.dds", skinframe->basename), r_texture_dds_save.integer < 2, false);
+#endif
                Mem_Free(pixels);
                pixels = NULL;
        }
@@ -3415,8 +3465,10 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        if (skinframe->shirt == NULL && (pixels = loadimagepixelsbgra(va("%s_shirt", skinframe->basename), false, false, false, &mymiplevel)))
        {
                skinframe->shirt = R_LoadTexture2D (r_main_texturepool, va("%s_shirt", skinframe->basename), image_width, image_height, pixels, vid.sRGB3D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, textureflags & (gl_texturecompression_color.integer && gl_texturecompression.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
+#ifndef USE_GLES2
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->shirt)
                        R_SaveTextureDDSFile(skinframe->shirt, va("dds/%s_shirt.dds", skinframe->basename), r_texture_dds_save.integer < 2, false);
+#endif
                Mem_Free(pixels);
                pixels = NULL;
        }
@@ -3425,8 +3477,10 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        if (skinframe->reflect == NULL && (pixels = loadimagepixelsbgra(va("%s_reflect", skinframe->basename), false, false, false, &mymiplevel)))
        {
                skinframe->reflect = R_LoadTexture2D (r_main_texturepool, va("%s_reflect", skinframe->basename), image_width, image_height, pixels, vid.sRGB3D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, textureflags & (gl_texturecompression_reflectmask.integer && gl_texturecompression.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
+#ifndef USE_GLES2
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->reflect)
                        R_SaveTextureDDSFile(skinframe->reflect, va("dds/%s_reflect.dds", skinframe->basename), r_texture_dds_save.integer < 2, true);
+#endif
                Mem_Free(pixels);
                pixels = NULL;
        }
@@ -3448,9 +3502,10 @@ skinframe_t *R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, co
                return NULL;
 
        // if already loaded just return it, otherwise make a new skinframe
-       skinframe = R_SkinFrame_Find(name, textureflags, width, height, skindata ? CRC_Block(skindata, width*height*4) : 0, true);
+       skinframe = R_SkinFrame_Find(name, textureflags, width, height, (textureflags & TEXF_FORCE_RELOAD) ? -1 : skindata ? CRC_Block(skindata, width*height*4) : 0, true);
        if (skinframe && skinframe->base)
                return skinframe;
+       textureflags &= ~TEXF_FORCE_RELOAD;
 
        skinframe->stain = NULL;
        skinframe->merged = NULL;
@@ -3520,6 +3575,7 @@ skinframe_t *R_SkinFrame_LoadInternalQuake(const char *name, int textureflags, i
        skinframe = R_SkinFrame_Find(name, textureflags, width, height, skindata ? CRC_Block(skindata, width*height) : 0, true);
        if (skinframe && skinframe->base)
                return skinframe;
+       textureflags &= ~TEXF_FORCE_RELOAD;
 
        skinframe->stain = NULL;
        skinframe->merged = NULL;
@@ -3641,6 +3697,7 @@ skinframe_t *R_SkinFrame_LoadInternal8bit(const char *name, int textureflags, co
        skinframe = R_SkinFrame_Find(name, textureflags, width, height, skindata ? CRC_Block(skindata, width*height) : 0, true);
        if (skinframe && skinframe->base)
                return skinframe;
+       textureflags &= ~TEXF_FORCE_RELOAD;
 
        skinframe->stain = NULL;
        skinframe->merged = NULL;
@@ -3999,8 +4056,7 @@ void gl_main_start(void)
        r_texture_fogheighttexture = NULL;
        r_texture_gammaramps = NULL;
        //r_texture_fogintensity = NULL;
-       memset(&r_bloomstate, 0, sizeof(r_bloomstate));
-       memset(&r_waterstate, 0, sizeof(r_waterstate));
+       memset(&r_fb, 0, sizeof(r_fb));
        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);
@@ -4033,8 +4089,10 @@ void gl_main_shutdown(void)
        case RENDERPATH_GL20:
        case RENDERPATH_GLES1:
        case RENDERPATH_GLES2:
+#ifdef GL_SAMPLES_PASSED_ARB
                if (r_maxqueries)
                        qglDeleteQueriesARB(r_maxqueries, r_queries);
+#endif
                break;
        case RENDERPATH_D3D9:
                //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
@@ -4076,8 +4134,7 @@ void gl_main_shutdown(void)
        r_texture_gammaramps = NULL;
        r_texture_numcubemaps = 0;
        //r_texture_fogintensity = NULL;
-       memset(&r_bloomstate, 0, sizeof(r_bloomstate));
-       memset(&r_waterstate, 0, sizeof(r_waterstate));
+       memset(&r_fb, 0, sizeof(r_fb));
        R_GLSL_Restart_f();
 
        r_glsl_permutation = NULL;
@@ -4137,13 +4194,17 @@ void GL_Main_Init(void)
                Cvar_RegisterVariable (&gl_skyclip);
        }
        Cvar_RegisterVariable(&r_motionblur);
-       Cvar_RegisterVariable(&r_motionblur_maxblur);
-       Cvar_RegisterVariable(&r_motionblur_bmin);
-       Cvar_RegisterVariable(&r_motionblur_vmin);
-       Cvar_RegisterVariable(&r_motionblur_vmax);
-       Cvar_RegisterVariable(&r_motionblur_vcoeff);
-       Cvar_RegisterVariable(&r_motionblur_randomize);
        Cvar_RegisterVariable(&r_damageblur);
+       Cvar_RegisterVariable(&r_motionblur_averaging);
+       Cvar_RegisterVariable(&r_motionblur_randomize);
+       Cvar_RegisterVariable(&r_motionblur_minblur);
+       Cvar_RegisterVariable(&r_motionblur_maxblur);
+       Cvar_RegisterVariable(&r_motionblur_velocityfactor);
+       Cvar_RegisterVariable(&r_motionblur_velocityfactor_minspeed);
+       Cvar_RegisterVariable(&r_motionblur_velocityfactor_maxspeed);
+       Cvar_RegisterVariable(&r_motionblur_mousefactor);
+       Cvar_RegisterVariable(&r_motionblur_mousefactor_minspeed);
+       Cvar_RegisterVariable(&r_motionblur_mousefactor_maxspeed);
        Cvar_RegisterVariable(&r_equalize_entities_fullbright);
        Cvar_RegisterVariable(&r_equalize_entities_minambient);
        Cvar_RegisterVariable(&r_equalize_entities_by);
@@ -4176,6 +4237,7 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_cullentities_trace_tempentitysamples);
        Cvar_RegisterVariable(&r_cullentities_trace_enlarge);
        Cvar_RegisterVariable(&r_cullentities_trace_delay);
+       Cvar_RegisterVariable(&r_sortentities);
        Cvar_RegisterVariable(&r_drawviewmodel);
        Cvar_RegisterVariable(&r_drawexteriormodel);
        Cvar_RegisterVariable(&r_speeds);
@@ -4202,6 +4264,7 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_fog_clear);
        Cvar_RegisterVariable(&r_drawfog);
        Cvar_RegisterVariable(&r_transparentdepthmasking);
+       Cvar_RegisterVariable(&r_transparent_sortmindist);
        Cvar_RegisterVariable(&r_transparent_sortmaxdist);
        Cvar_RegisterVariable(&r_transparent_sortarraysize);
        Cvar_RegisterVariable(&r_texture_dds_load);
@@ -4224,6 +4287,8 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_glsl_offsetmapping_reliefmapping_steps);
        Cvar_RegisterVariable(&r_glsl_offsetmapping_reliefmapping_refinesteps);
        Cvar_RegisterVariable(&r_glsl_offsetmapping_scale);
+       Cvar_RegisterVariable(&r_glsl_offsetmapping_lod);
+       Cvar_RegisterVariable(&r_glsl_offsetmapping_lod_distance);
        Cvar_RegisterVariable(&r_glsl_postprocess);
        Cvar_RegisterVariable(&r_glsl_postprocess_uservec1);
        Cvar_RegisterVariable(&r_glsl_postprocess_uservec2);
@@ -4241,6 +4306,8 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_water_reflectdistort);
        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);
@@ -4253,16 +4320,16 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_bloom_resolution);
        Cvar_RegisterVariable(&r_bloom_colorexponent);
        Cvar_RegisterVariable(&r_bloom_colorsubtract);
-       Cvar_RegisterVariable(&r_hdr);
        Cvar_RegisterVariable(&r_hdr_scenebrightness);
        Cvar_RegisterVariable(&r_hdr_glowintensity);
-       Cvar_RegisterVariable(&r_hdr_range);
        Cvar_RegisterVariable(&r_hdr_irisadaptation);
        Cvar_RegisterVariable(&r_hdr_irisadaptation_multiplier);
        Cvar_RegisterVariable(&r_hdr_irisadaptation_minvalue);
        Cvar_RegisterVariable(&r_hdr_irisadaptation_maxvalue);
        Cvar_RegisterVariable(&r_hdr_irisadaptation_value);
-       Cvar_RegisterVariable(&r_hdr_irisadaptation_fade);
+       Cvar_RegisterVariable(&r_hdr_irisadaptation_fade_up);
+       Cvar_RegisterVariable(&r_hdr_irisadaptation_fade_down);
+       Cvar_RegisterVariable(&r_hdr_irisadaptation_radius);
        Cvar_RegisterVariable(&r_smoothnormals_areaweighting);
        Cvar_RegisterVariable(&developer_texturelogging);
        Cvar_RegisterVariable(&gl_lightmaps);
@@ -4312,6 +4379,7 @@ void Render_Init(void)
 GL_Init
 ===============
 */
+#ifndef USE_GLES2
 extern char *ENGINE_EXTENSIONS;
 void GL_Init (void)
 {
@@ -4339,6 +4407,7 @@ void GL_Init (void)
        // clear to black (loading plaque will be seen over this)
        GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 128);
 }
+#endif
 
 int R_CullBox(const vec3_t mins, const vec3_t maxs)
 {
@@ -4711,8 +4780,8 @@ static void R_View_UpdateEntityLighting (void)
        {
                ent = r_refdef.scene.entities[i];
 
-               // skip unseen models
-               if (!r_refdef.viewcache.entityvisible[i] && skipunseen)
+               // skip unseen models and models that updated by CSQC
+               if ((!r_refdef.viewcache.entityvisible[i] && skipunseen) || ent->flags & RENDER_CUSTOMIZEDMODELLIGHT)
                        continue;
 
                // skip bsp models
@@ -4852,8 +4921,8 @@ static void R_View_UpdateEntityVisible (void)
        entity_render_t *ent;
 
        renderimask = r_refdef.envmap                                    ? (RENDER_EXTERIORMODEL | RENDER_VIEWMODEL)
-               : r_waterstate.renderingrefraction                       ? (RENDER_EXTERIORMODEL | RENDER_VIEWMODEL)
-               : (chase_active.integer || r_waterstate.renderingscene)  ? RENDER_VIEWMODEL
+               : r_fb.water.hideplayer                                      ? (RENDER_EXTERIORMODEL | RENDER_VIEWMODEL)
+               : (chase_active.integer || r_fb.water.renderingscene)  ? RENDER_VIEWMODEL
                :                                                          RENDER_EXTERIORMODEL;
        if (!r_drawviewmodel.integer)
                renderimask |= RENDER_VIEWMODEL;
@@ -4868,7 +4937,7 @@ static void R_View_UpdateEntityVisible (void)
                        ent = r_refdef.scene.entities[i];
                        if (!(ent->flags & renderimask))
                        if (!R_CullBox(ent->mins, ent->maxs) || (ent->model && ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE)))
-                       if ((ent->flags & (RENDER_NODEPTHTEST | RENDER_VIEWMODEL)) || r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, ent->mins, ent->maxs))
+                       if ((ent->flags & (RENDER_NODEPTHTEST | RENDER_WORLDOBJECT | RENDER_VIEWMODEL)) || r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, ent->mins, ent->maxs))
                                r_refdef.viewcache.entityvisible[i] = true;
                }
        }
@@ -4889,7 +4958,7 @@ static void R_View_UpdateEntityVisible (void)
                        if (!r_refdef.viewcache.entityvisible[i])
                                continue;
                        ent = r_refdef.scene.entities[i];
-                       if(!(ent->flags & (RENDER_VIEWMODEL | RENDER_NOCULL | RENDER_NODEPTHTEST)) && !(ent->model && (ent->model->name[0] == '*')))
+                       if(!(ent->flags & (RENDER_VIEWMODEL | RENDER_WORLDOBJECT | RENDER_NODEPTHTEST)) && !(ent->model && (ent->model->name[0] == '*')))
                        {
                                samples = ent->entitynumber ? r_cullentities_trace_samples.integer : r_cullentities_trace_tempentitysamples.integer;
                                if (samples < 0)
@@ -4998,28 +5067,43 @@ static void R_DrawModelsAddWaterPlanes(void)
        }
 }
 
+static float irisvecs[7][3] = {{0, 0, 0}, {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}};
+
 void R_HDR_UpdateIrisAdaptation(const vec3_t point)
 {
        if (r_hdr_irisadaptation.integer)
        {
+               vec3_t p;
                vec3_t ambient;
                vec3_t diffuse;
                vec3_t diffusenormal;
-               vec_t brightness;
+               vec3_t forward;
+               vec_t brightness = 0.0f;
                vec_t goal;
-               vec_t adjust;
                vec_t current;
-               R_CompleteLightPoint(ambient, diffuse, diffusenormal, point, LP_LIGHTMAP | LP_RTWORLD | LP_DYNLIGHT);
-               brightness = (ambient[0] + ambient[1] + ambient[2] + diffuse[0] + diffuse[1] + diffuse[2]) * (1.0f / 3.0f);
-               brightness = max(0.0000001f, brightness);
+               vec_t d;
+               int c;
+               VectorCopy(r_refdef.view.forward, forward);
+               for (c = 0;c < (int)(sizeof(irisvecs)/sizeof(irisvecs[0]));c++)
+               {
+                       p[0] = point[0] + irisvecs[c][0] * r_hdr_irisadaptation_radius.value;
+                       p[1] = point[1] + irisvecs[c][1] * r_hdr_irisadaptation_radius.value;
+                       p[2] = point[2] + irisvecs[c][2] * r_hdr_irisadaptation_radius.value;
+                       R_CompleteLightPoint(ambient, diffuse, diffusenormal, p, LP_LIGHTMAP | LP_RTWORLD | LP_DYNLIGHT);
+                       d = DotProduct(forward, diffusenormal);
+                       brightness += VectorLength(ambient);
+                       if (d > 0)
+                               brightness += d * VectorLength(diffuse);
+               }
+               brightness *= 1.0f / c;
+               brightness += 0.00001f; // make sure it's never zero
                goal = r_hdr_irisadaptation_multiplier.value / brightness;
                goal = bound(r_hdr_irisadaptation_minvalue.value, goal, r_hdr_irisadaptation_maxvalue.value);
-               adjust = r_hdr_irisadaptation_fade.value * cl.realframetime;
                current = r_hdr_irisadaptation_value.value;
                if (current < goal)
-                       current = min(current + adjust, goal);
+                       current = min(current + r_hdr_irisadaptation_fade_up.value * cl.realframetime, goal);
                else if (current > goal)
-                       current = max(current - adjust, goal);
+                       current = max(current - r_hdr_irisadaptation_fade_down.value * cl.realframetime, goal);
                if (fabs(r_hdr_irisadaptation_value.value - current) > 0.0001f)
                        Cvar_SetValueQuick(&r_hdr_irisadaptation_value, current);
        }
@@ -5237,6 +5321,7 @@ void R_View_UpdateWithScissor(const int *myscissor)
        R_View_WorldVisibility(r_refdef.view.useclipplane);
        R_View_UpdateEntityVisible();
        R_View_UpdateEntityLighting();
+       R_AnimCache_CacheVisibleEntities();
 }
 
 void R_View_Update(void)
@@ -5246,6 +5331,7 @@ void R_View_Update(void)
        R_View_WorldVisibility(r_refdef.view.useclipplane);
        R_View_UpdateEntityVisible();
        R_View_UpdateEntityLighting();
+       R_AnimCache_CacheVisibleEntities();
 }
 
 float viewscalefpsadjusted = 1.0f;
@@ -5258,19 +5344,11 @@ void R_GetScaledViewSize(int width, int height, int *outwidth, int *outheight)
        *outheight = (int)ceil(height * scale);
 }
 
-void R_Mesh_SetMainRenderTargets(void)
-{
-       if (r_bloomstate.fbo_framebuffer)
-               R_Mesh_SetRenderTargets(r_bloomstate.fbo_framebuffer, r_bloomstate.texture_framebufferdepth, r_bloomstate.texture_framebuffercolor, NULL, NULL, NULL);
-       else
-               R_Mesh_ResetRenderTargets();
-}
-
-void R_SetupView(qboolean allowwaterclippingplane)
+void R_SetupView(qboolean allowwaterclippingplane, int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
 {
        const float *customclipplane = NULL;
        float plane[4];
-       int scaledwidth, scaledheight;
+       int /*rtwidth,*/ rtheight, scaledwidth, scaledheight;
        if (r_refdef.view.useclipplane && allowwaterclippingplane)
        {
                // LordHavoc: couldn't figure out how to make this approach the
@@ -5285,14 +5363,17 @@ void R_SetupView(qboolean allowwaterclippingplane)
                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;
+
        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, vid.height - 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, 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);
        else if (vid.stencil && r_useinfinitefarclip.integer)
-               R_Viewport_InitPerspectiveInfinite(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, vid.height - 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, 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);
        else
-               R_Viewport_InitPerspective(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, vid.height - 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_SetMainRenderTargets();
+               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_SetViewport(&r_refdef.view.viewport);
        if (r_refdef.view.useclipplane && allowwaterclippingplane && vid.renderpath == RENDERPATH_SOFT)
        {
@@ -5349,14 +5430,14 @@ void R_EntityMatrix(const matrix4x4_t *matrix)
        }
 }
 
-void R_ResetViewRendering2D(void)
+void R_ResetViewRendering2D(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
 {
        r_viewport_t viewport;
        DrawQ_Finish();
 
        // 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, 1, 1, -10, 100, NULL);
-       R_Mesh_ResetRenderTargets();
+       R_Mesh_SetRenderTargets(fbo, depthtexture, colortexture, NULL, NULL, NULL);
        R_SetViewport(&viewport);
        GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height);
        GL_Color(1, 1, 1, 1);
@@ -5389,11 +5470,11 @@ void R_ResetViewRendering2D(void)
        GL_CullFace(GL_NONE);
 }
 
-void R_ResetViewRendering3D(void)
+void R_ResetViewRendering3D(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
 {
        DrawQ_Finish();
 
-       R_SetupView(true);
+       R_SetupView(true, fbo, depthtexture, colortexture);
        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);
@@ -5441,14 +5522,15 @@ static void R_RenderView_UpdateViewVectors(void)
        Matrix4x4_Invert_Simple(&r_refdef.view.inverse_matrix, &r_refdef.view.matrix);
 }
 
-void R_RenderScene(void);
-void R_RenderWaterPlanes(void);
+void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture);
+void R_RenderWaterPlanes(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture);
 
 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.samples < 2;
 
        if (vid.width > (int)vid.maxtexturesize_2d || vid.height > (int)vid.maxtexturesize_2d)
                return;
@@ -5492,82 +5574,103 @@ static void R_Water_StartFrame(void)
        }
 
        // allocate textures as needed
-       if (r_waterstate.texturewidth != texturewidth || r_waterstate.textureheight != textureheight || r_waterstate.camerawidth != camerawidth || r_waterstate.cameraheight != cameraheight)
+       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_waterstate.maxwaterplanes = MAX_WATERPLANES;
-               for (i = 0, p = r_waterstate.waterplanes;i < r_waterstate.maxwaterplanes;i++, p++)
+               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_waterstate, 0, sizeof(r_waterstate));
-               r_waterstate.texturewidth = texturewidth;
-               r_waterstate.textureheight = textureheight;
-               r_waterstate.camerawidth = camerawidth;
-               r_waterstate.cameraheight = cameraheight;
+               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_waterstate.texturewidth)
+       if (r_fb.water.texturewidth)
        {
                int scaledwidth, scaledheight;
 
-               r_waterstate.enabled = true;
+               r_fb.water.enabled = true;
 
-               // when doing a reduced render (HDR) we want to use a smaller area
-               r_waterstate.waterwidth = (int)bound(1, r_refdef.view.width * r_water_resolutionmultiplier.value, r_refdef.view.width);
-               r_waterstate.waterheight = (int)bound(1, r_refdef.view.height * r_water_resolutionmultiplier.value, r_refdef.view.height);
-               R_GetScaledViewSize(r_waterstate.waterwidth, r_waterstate.waterheight, &scaledwidth, &scaledheight);
+               // 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_waterstate.screenscale[0] = 0.5f * (float)scaledwidth / (float)r_waterstate.texturewidth;
-               r_waterstate.screenscale[1] = 0.5f * (float)scaledheight / (float)r_waterstate.textureheight;
-               r_waterstate.screencenter[0] = 0.5f * (float)scaledwidth / (float)r_waterstate.texturewidth;
-               r_waterstate.screencenter[1] = 0.5f * (float)scaledheight / (float)r_waterstate.textureheight;
+               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;
        }
 
-       r_waterstate.maxwaterplanes = MAX_WATERPLANES;
-       r_waterstate.numwaterplanes = 0;
+       r_fb.water.maxwaterplanes = MAX_WATERPLANES;
+       r_fb.water.numwaterplanes = 0;
 }
 
 void R_Water_AddWaterPlane(msurface_t *surface, int entno)
 {
-       int triangleindex, planeindex;
-       const int *e;
-       vec3_t vert[3];
-       vec3_t normal;
-       vec3_t center;
+       int planeindex, bestplaneindex, vertexindex;
+       vec3_t mins, maxs, normal, center, v, n;
+       vec_t planescore, bestplanescore;
        mplane_t plane;
        r_waterstate_waterplane_t *p;
        texture_t *t = R_GetCurrentTexture(surface->texture);
 
-       // just use the first triangle with a valid normal for any decisions
-       VectorClear(normal);
-       for (triangleindex = 0, e = rsurface.modelelement3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
-       {
-               Matrix4x4_Transform(&rsurface.matrix, rsurface.modelvertex3f + e[0]*3, vert[0]);
-               Matrix4x4_Transform(&rsurface.matrix, rsurface.modelvertex3f + e[1]*3, vert[1]);
-               Matrix4x4_Transform(&rsurface.matrix, rsurface.modelvertex3f + e[2]*3, vert[2]);
-               TriangleNormal(vert[0], vert[1], vert[2], normal);
-               if (VectorLength2(normal) >= 0.001)
-                       break;
-       }
+       rsurface.texture = t;
+       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_NOGAPS, 1, ((const msurface_t **)&surface));
+       // if the model has no normals, it's probably off-screen and they were not generated, so don't add it anyway
+       if (!rsurface.batchnormal3f || rsurface.batchnumvertices < 1)
+               return;
+       // average the vertex normals, find the surface bounds (after deformvertexes)
+       Matrix4x4_Transform(&rsurface.matrix, rsurface.batchvertex3f, v);
+       Matrix4x4_Transform3x3(&rsurface.matrix, rsurface.batchnormal3f, n);
+       VectorCopy(n, normal);
+       VectorCopy(v, mins);
+       VectorCopy(v, maxs);
+       for (vertexindex = 1;vertexindex < rsurface.batchnumvertices;vertexindex++)
+       {
+               Matrix4x4_Transform(&rsurface.matrix, rsurface.batchvertex3f + vertexindex*3, v);
+               Matrix4x4_Transform3x3(&rsurface.matrix, rsurface.batchnormal3f + vertexindex*3, n);
+               VectorAdd(normal, n, normal);
+               mins[0] = min(mins[0], v[0]);
+               mins[1] = min(mins[1], v[1]);
+               mins[2] = min(mins[2], v[2]);
+               maxs[0] = max(maxs[0], v[0]);
+               maxs[1] = max(maxs[1], v[1]);
+               maxs[2] = max(maxs[2], v[2]);
+       }
+       VectorNormalize(normal);
+       VectorMAM(0.5f, mins, 0.5f, maxs, center);
 
        VectorCopy(normal, plane.normal);
        VectorNormalize(plane.normal);
-       plane.dist = DotProduct(vert[0], plane.normal);
+       plane.dist = DotProduct(center, plane.normal);
        PlaneClassify(&plane);
        if (PlaneDiff(r_refdef.view.origin, &plane) < 0)
        {
                // skip backfaces (except if nocullface is set)
-               if (!(t->currentmaterialflags & MATERIALFLAG_NOCULLFACE))
-                       return;
+//             if (!(t->currentmaterialflags & MATERIALFLAG_NOCULLFACE))
+//                     return;
                VectorNegate(plane.normal, plane.normal);
                plane.dist *= -1;
                PlaneClassify(&plane);
@@ -5575,42 +5678,52 @@ void R_Water_AddWaterPlane(msurface_t *surface, int entno)
 
 
        // find a matching plane if there is one
-       for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
+       bestplaneindex = -1;
+       bestplanescore = 1048576.0f;
+       for (planeindex = 0, p = r_fb.water.waterplanes;planeindex < r_fb.water.numwaterplanes;planeindex++, p++)
+       {
                if(p->camera_entity == t->camera_entity)
-                       if (fabs(PlaneDiff(vert[0], &p->plane)) < 1 && fabs(PlaneDiff(vert[1], &p->plane)) < 1 && fabs(PlaneDiff(vert[2], &p->plane)) < 1)
-                               break;
-       if (planeindex >= r_waterstate.maxwaterplanes)
-               return; // nothing we can do, out of planes
+               {
+                       planescore = 1.0f - DotProduct(plane.normal, p->plane.normal) + fabs(plane.dist - p->plane.dist) * 0.001f;
+                       if (bestplaneindex < 0 || bestplanescore > planescore)
+                       {
+                               bestplaneindex = planeindex;
+                               bestplanescore = planescore;
+                       }
+               }
+       }
+       planeindex = bestplaneindex;
+       p = r_fb.water.waterplanes + planeindex;
 
-       // if this triangle does not fit any known plane rendered this frame, add one
-       if (planeindex >= r_waterstate.numwaterplanes)
+       // if this surface does not fit any known plane rendered this frame, add one
+       if ((planeindex < 0 || bestplanescore > 0.001f) && r_fb.water.numwaterplanes < r_fb.water.maxwaterplanes)
        {
                // store the new plane
-               r_waterstate.numwaterplanes++;
+               planeindex = r_fb.water.numwaterplanes++;
+               p = r_fb.water.waterplanes + planeindex;
                p->plane = plane;
                // clear materialflags and pvs
                p->materialflags = 0;
                p->pvsvalid = false;
                p->camera_entity = t->camera_entity;
-               VectorCopy(surface->mins, p->mins);
-               VectorCopy(surface->maxs, p->maxs);
+               VectorCopy(mins, p->mins);
+               VectorCopy(maxs, p->maxs);
        }
        else
        {
-               // merge mins/maxs
-               p->mins[0] = min(p->mins[0], surface->mins[0]);
-               p->mins[1] = min(p->mins[1], surface->mins[1]);
-               p->mins[2] = min(p->mins[2], surface->mins[2]);
-               p->maxs[0] = max(p->maxs[0], surface->maxs[0]);
-               p->maxs[1] = max(p->maxs[1], surface->maxs[1]);
-               p->maxs[2] = max(p->maxs[2], surface->maxs[2]);
+               // merge mins/maxs when we're adding this surface to the plane
+               p->mins[0] = min(p->mins[0], mins[0]);
+               p->mins[1] = min(p->mins[1], mins[1]);
+               p->mins[2] = min(p->mins[2], mins[2]);
+               p->maxs[0] = max(p->maxs[0], maxs[0]);
+               p->maxs[1] = max(p->maxs[1], maxs[1]);
+               p->maxs[2] = max(p->maxs[2], maxs[2]);
        }
        // merge this surface's materialflags into the waterplane
        p->materialflags |= t->currentmaterialflags;
        if(!(p->materialflags & MATERIALFLAG_CAMERA))
        {
                // merge this surface's PVS into the waterplane
-               VectorMAM(0.5f, surface->mins, 0.5f, surface->maxs, center);
                if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.FatPVS
                 && r_refdef.scene.worldmodel->brush.PointInLeaf && r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, center)->clusterindex >= 0)
                {
@@ -5623,7 +5736,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(void)
+static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
 {
        int myscissor[4];
        r_refdef_view_t originalview;
@@ -5631,6 +5744,7 @@ static void R_Water_ProcessPlanes(void)
        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.samples < 2;
 
        originalview = r_refdef.view;
 
@@ -5659,48 +5773,69 @@ static void R_Water_ProcessPlanes(void)
        }
 
        // make sure enough textures are allocated
-       for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
+       for (planeindex = 0, p = r_fb.water.waterplanes;planeindex < r_fb.water.numwaterplanes;planeindex++, p++)
        {
                if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
                {
                        if (!p->texture_refraction)
-                               p->texture_refraction = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_refraction", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
+                               p->texture_refraction = R_LoadTexture2D(r_main_texturepool, va("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_LoadTextureShadowMap2D(r_main_texturepool, "waterviewdepth", r_fb.water.texturewidth, r_fb.water.textureheight, 24, false);
+                               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("waterplane%i_camera", planeindex), r_waterstate.camerawidth, r_waterstate.cameraheight, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCELINEAR, -1, NULL);
+                               p->texture_camera = R_LoadTexture2D(r_main_texturepool, va("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_LoadTextureShadowMap2D(r_main_texturepool, "waterviewdepth", r_fb.water.texturewidth, r_fb.water.textureheight, 24, false);
+                               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("waterplane%i_reflection", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
+                               p->texture_reflection = R_LoadTexture2D(r_main_texturepool, va("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_LoadTextureShadowMap2D(r_main_texturepool, "waterviewdepth", r_fb.water.texturewidth, r_fb.water.textureheight, 24, false);
+                               if (p->fbo_reflection == 0)
+                                       p->fbo_reflection = R_Mesh_CreateFramebufferObject(r_fb.water.depthtexture, p->texture_reflection, NULL, NULL, NULL);
+                       }
                }
        }
 
        // render views
        r_refdef.view = originalview;
        r_refdef.view.showdebug = false;
-       r_refdef.view.width = r_waterstate.waterwidth;
-       r_refdef.view.height = r_waterstate.waterheight;
+       r_refdef.view.width = r_fb.water.waterwidth;
+       r_refdef.view.height = r_fb.water.waterheight;
        r_refdef.view.useclipplane = true;
        myview = r_refdef.view;
-       r_waterstate.renderingscene = true;
-       for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
+       r_fb.water.renderingscene = true;
+       for (planeindex = 0, p = r_fb.water.waterplanes;planeindex < r_fb.water.numwaterplanes;planeindex++, p++)
        {
                if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION))
                {
                        r_refdef.view = myview;
                        if(r_water_scissormode.integer)
                        {
-                               R_SetupView(true);
+                               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
                        }
@@ -5722,7 +5857,8 @@ static void R_Water_ProcessPlanes(void)
                                        memset(r_refdef.viewcache.world_pvsbits, 0xFF, r_refdef.scene.worldmodel->brush.num_pvsclusterbytes);
                        }
 
-                       R_ResetViewRendering3D();
+                       r_fb.water.hideplayer = r_water_hideplayer.integer >= 2;
+                       R_ResetViewRendering3D(p->fbo_reflection, r_fb.water.depthtexture, p->texture_reflection);
                        R_ClearScreen(r_refdef.fogenabled);
                        if(r_water_scissormode.integer & 2)
                                R_View_UpdateWithScissor(myscissor);
@@ -5730,9 +5866,11 @@ static void R_Water_ProcessPlanes(void)
                                R_View_Update();
                        if(r_water_scissormode.integer & 1)
                                GL_Scissor(myscissor[0], myscissor[1], myscissor[2], myscissor[3]);
-                       R_RenderScene();
+                       R_RenderScene(p->fbo_reflection, r_fb.water.depthtexture, p->texture_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);
+                       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;
                }
 
                // render the normal view scene and copy into texture
@@ -5742,12 +5880,12 @@ static void R_Water_ProcessPlanes(void)
                        r_refdef.view = myview;
                        if(r_water_scissormode.integer)
                        {
-                               R_SetupView(true);
+                               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_waterstate.renderingrefraction = true;
+                       r_fb.water.hideplayer = r_water_hideplayer.integer >= 1;
 
                        r_refdef.view.clipplane = p->plane;
                        VectorNegate(r_refdef.view.clipplane.normal, r_refdef.view.clipplane.normal);
@@ -5756,7 +5894,7 @@ static void R_Water_ProcessPlanes(void)
                        if((p->materialflags & MATERIALFLAG_CAMERA) && p->camera_entity)
                        {
                                // we need to perform a matrix transform to render the view... so let's get the transformation matrix
-                               r_waterstate.renderingrefraction = false; // we don't want to hide the player model from these ones
+                               r_fb.water.hideplayer = false; // we don't want to hide the player model from these ones
                                CL_VM_TransformView(p->camera_entity - MAX_EDICTS, &r_refdef.view.matrix, &r_refdef.view.clipplane, visorigin);
                                R_RenderView_UpdateViewVectors();
                                if(r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.FatPVS)
@@ -5768,7 +5906,7 @@ static void R_Water_ProcessPlanes(void)
 
                        PlaneClassify(&r_refdef.view.clipplane);
 
-                       R_ResetViewRendering3D();
+                       R_ResetViewRendering3D(p->fbo_refraction, r_fb.water.depthtexture, p->texture_refraction);
                        R_ClearScreen(r_refdef.fogenabled);
                        if(r_water_scissormode.integer & 2)
                                R_View_UpdateWithScissor(myscissor);
@@ -5776,10 +5914,11 @@ static void R_Water_ProcessPlanes(void)
                                R_View_Update();
                        if(r_water_scissormode.integer & 1)
                                GL_Scissor(myscissor[0], myscissor[1], myscissor[2], myscissor[3]);
-                       R_RenderScene();
+                       R_RenderScene(p->fbo_refraction, r_fb.water.depthtexture, p->texture_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_waterstate.renderingrefraction = false;
+                       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;
                }
                else if (p->materialflags & MATERIALFLAG_CAMERA)
                {
@@ -5789,10 +5928,12 @@ static void R_Water_ProcessPlanes(void)
                        VectorNegate(r_refdef.view.clipplane.normal, r_refdef.view.clipplane.normal);
                        r_refdef.view.clipplane.dist = -r_refdef.view.clipplane.dist;
 
-                       r_refdef.view.width = r_waterstate.camerawidth;
-                       r_refdef.view.height = r_waterstate.cameraheight;
+                       r_refdef.view.width = r_fb.water.camerawidth;
+                       r_refdef.view.height = r_fb.water.cameraheight;
                        r_refdef.view.frustum_x = 1; // tan(45 * M_PI / 180.0);
                        r_refdef.view.frustum_y = 1; // tan(45 * M_PI / 180.0);
+                       r_refdef.view.ortho_x = 90; // abused as angle by VM_CL_R_SetView
+                       r_refdef.view.ortho_y = 90; // abused as angle by VM_CL_R_SetView
 
                        if(p->camera_entity)
                        {
@@ -5820,26 +5961,30 @@ static void R_Water_ProcessPlanes(void)
 
                        PlaneClassify(&r_refdef.view.clipplane);
 
-                       R_ResetViewRendering3D();
+                       r_fb.water.hideplayer = false;
+
+                       R_ResetViewRendering3D(p->fbo_camera, r_fb.water.depthtexture, p->texture_camera);
                        R_ClearScreen(r_refdef.fogenabled);
                        R_View_Update();
-                       R_RenderScene();
+                       R_RenderScene(p->fbo_camera, r_fb.water.depthtexture, p->texture_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_waterstate.renderingrefraction = false;
+                       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;
                }
 
        }
        if(vid.renderpath==RENDERPATH_SOFT) DPSOFTRAST_ClipPlane(0, 0, 0, 1);
-       r_waterstate.renderingscene = false;
+       r_fb.water.renderingscene = false;
        r_refdef.view = originalview;
-       R_ResetViewRendering3D();
-       R_ClearScreen(r_refdef.fogenabled);
+       R_ResetViewRendering3D(fbo, depthtexture, colortexture);
+       if (!r_fb.water.depthtexture)
+               R_ClearScreen(r_refdef.fogenabled);
        R_View_Update();
        goto finish;
 error:
        r_refdef.view = originalview;
-       r_waterstate.renderingscene = false;
+       r_fb.water.renderingscene = false;
        Cvar_SetValueQuick(&r_water, 0);
        Con_Printf("R_Water_ProcessPlanes: Error: texture creation failed!  Turned off r_water.\n");
 finish:
@@ -5863,9 +6008,31 @@ finish:
 
 void R_Bloom_StartFrame(void)
 {
+       int i;
        int bloomtexturewidth, bloomtextureheight, screentexturewidth, screentextureheight;
        int viewwidth, viewheight;
-       textype_t textype;
+       qboolean useviewfbo = r_viewfbo.integer >= 1 && vid.support.ext_framebuffer_object && vid.samples < 2;
+       textype_t textype = TEXTYPE_COLORBUFFER;
+
+       switch (vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+       case RENDERPATH_GLES2:
+               if (vid.support.ext_framebuffer_object)
+               {
+                       if (r_viewfbo.integer == 2) textype = TEXTYPE_COLORBUFFER16F;
+                       if (r_viewfbo.integer == 3) textype = TEXTYPE_COLORBUFFER32F;
+               }
+               break;
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GLES1:
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
+               break;
+       }
 
        if (r_viewscale_fpsscaling.integer)
        {
@@ -5903,143 +6070,152 @@ void R_Bloom_StartFrame(void)
 
        // set bloomwidth and bloomheight to the bloom resolution that will be
        // used (often less than the screen resolution for faster rendering)
-       r_bloomstate.bloomwidth = bound(1, r_bloom_resolution.integer, vid.height);
-       r_bloomstate.bloomheight = r_bloomstate.bloomwidth * vid.height / vid.width;
-       r_bloomstate.bloomheight = bound(1, r_bloomstate.bloomheight, vid.height);
-       r_bloomstate.bloomwidth = bound(1, r_bloomstate.bloomwidth, (int)vid.maxtexturesize_2d);
-       r_bloomstate.bloomheight = bound(1, r_bloomstate.bloomheight, (int)vid.maxtexturesize_2d);
+       r_fb.bloomwidth = bound(1, r_bloom_resolution.integer, vid.height);
+       r_fb.bloomheight = r_fb.bloomwidth * vid.height / vid.width;
+       r_fb.bloomheight = bound(1, r_fb.bloomheight, vid.height);
+       r_fb.bloomwidth = bound(1, r_fb.bloomwidth, (int)vid.maxtexturesize_2d);
+       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_bloomstate.bloomwidth;
-               bloomtextureheight = r_bloomstate.bloomheight;
+               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_bloomstate.bloomwidth ;bloomtexturewidth   *= 2);
-               for (bloomtextureheight  = 1;bloomtextureheight  < r_bloomstate.bloomheight;bloomtextureheight  *= 2);
+               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);
        }
 
-       if ((r_hdr.integer || 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))
+       if ((r_bloom.integer || (!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0))) && ((r_bloom_resolution.integer < 4 || r_bloom_blur.value < 1 || r_bloom_blur.value >= 512) || r_refdef.view.width > (int)vid.maxtexturesize_2d || r_refdef.view.height > (int)vid.maxtexturesize_2d))
        {
-               Cvar_SetValueQuick(&r_hdr, 0);
                Cvar_SetValueQuick(&r_bloom, 0);
                Cvar_SetValueQuick(&r_motionblur, 0);
                Cvar_SetValueQuick(&r_damageblur, 0);
        }
 
-       if (!(r_glsl_postprocess.integer || (!R_Stereo_ColorMasking() && r_glsl_saturation.value != 1) || (v_glslgamma.integer && !vid_gammatables_trivial)) && !r_bloom.integer && !r_hdr.integer && (R_Stereo_Active() || (r_motionblur.value <= 0 && r_damageblur.value <= 0)) && r_viewfbo.integer < 1 && r_viewscale.value == 1.0f && !r_viewscale_fpsscaling.integer)
+       if (!(r_glsl_postprocess.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_hdr.integer && !r_bloom.integer)
+       if (!r_bloom.integer)
                bloomtexturewidth = bloomtextureheight = 0;
 
-       textype = TEXTYPE_COLORBUFFER;
-       switch (vid.renderpath)
+       // 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))
        {
-       case RENDERPATH_GL20:
-       case RENDERPATH_GLES2:
-               if (vid.support.ext_framebuffer_object)
+               for (i = 0;i < (int)(sizeof(r_fb.bloomtexture)/sizeof(r_fb.bloomtexture[i]));i++)
                {
-                       if (r_viewfbo.integer == 2) textype = TEXTYPE_COLORBUFFER16F;
-                       if (r_viewfbo.integer == 3) textype = TEXTYPE_COLORBUFFER32F;
+                       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;
                }
-               break;
-       case RENDERPATH_GL11:
-       case RENDERPATH_GL13:
-       case RENDERPATH_GLES1:
-       case RENDERPATH_D3D9:
-       case RENDERPATH_D3D10:
-       case RENDERPATH_D3D11:
-       case RENDERPATH_SOFT:
-               break;
-       }
 
-       // allocate textures as needed
-       if (r_bloomstate.screentexturewidth != screentexturewidth
-        || r_bloomstate.screentextureheight != screentextureheight
-        || r_bloomstate.bloomtexturewidth != bloomtexturewidth
-        || r_bloomstate.bloomtextureheight != bloomtextureheight
-        || r_bloomstate.texturetype != textype
-        || r_bloomstate.viewfbo != r_viewfbo.integer)
-       {
-               if (r_bloomstate.texture_bloom)
-                       R_FreeTexture(r_bloomstate.texture_bloom);
-               r_bloomstate.texture_bloom = NULL;
-               if (r_bloomstate.texture_screen)
-                       R_FreeTexture(r_bloomstate.texture_screen);
-               r_bloomstate.texture_screen = NULL;
-               if (r_bloomstate.fbo_framebuffer)
-                       R_Mesh_DestroyFramebufferObject(r_bloomstate.fbo_framebuffer);
-               r_bloomstate.fbo_framebuffer = 0;
-               if (r_bloomstate.texture_framebuffercolor)
-                       R_FreeTexture(r_bloomstate.texture_framebuffercolor);
-               r_bloomstate.texture_framebuffercolor = NULL;
-               if (r_bloomstate.texture_framebufferdepth)
-                       R_FreeTexture(r_bloomstate.texture_framebufferdepth);
-               r_bloomstate.texture_framebufferdepth = NULL;
-               r_bloomstate.screentexturewidth = screentexturewidth;
-               r_bloomstate.screentextureheight = screentextureheight;
-               if (r_bloomstate.screentexturewidth && r_bloomstate.screentextureheight)
-                       r_bloomstate.texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", r_bloomstate.screentexturewidth, r_bloomstate.screentextureheight, NULL, textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
-               if (r_viewfbo.integer >= 1 && vid.support.ext_framebuffer_object)
-               {
-                       // FIXME: choose depth bits based on a cvar
-                       r_bloomstate.texture_framebufferdepth = R_LoadTextureShadowMap2D(r_main_texturepool, "framebufferdepth", r_bloomstate.screentexturewidth, r_bloomstate.screentextureheight, 24, false);
-                       r_bloomstate.texture_framebuffercolor = R_LoadTexture2D(r_main_texturepool, "framebuffercolor", r_bloomstate.screentexturewidth, r_bloomstate.screentextureheight, NULL, textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
-                       r_bloomstate.fbo_framebuffer = R_Mesh_CreateFramebufferObject(r_bloomstate.texture_framebufferdepth, r_bloomstate.texture_framebuffercolor, NULL, NULL, NULL);
-                       R_Mesh_SetRenderTargets(r_bloomstate.fbo_framebuffer, r_bloomstate.texture_framebufferdepth, r_bloomstate.texture_framebuffercolor, NULL, NULL, NULL);
-                       // render depth into one texture and normalmap into the other
-                       if (qglDrawBuffer)
+               if (r_fb.fbo)
+                       R_Mesh_DestroyFramebufferObject(r_fb.fbo);
+               r_fb.fbo = 0;
+
+               if (r_fb.colortexture)
+                       R_FreeTexture(r_fb.colortexture);
+               r_fb.colortexture = NULL;
+
+               if (r_fb.depthtexture)
+                       R_FreeTexture(r_fb.depthtexture);
+               r_fb.depthtexture = NULL;
+
+               if (r_fb.ghosttexture)
+                       R_FreeTexture(r_fb.ghosttexture);
+               r_fb.ghosttexture = NULL;
+
+               r_fb.screentexturewidth = screentexturewidth;
+               r_fb.screentextureheight = screentextureheight;
+               r_fb.bloomtexturewidth = bloomtexturewidth;
+               r_fb.bloomtextureheight = bloomtextureheight;
+               r_fb.textype = textype;
+
+               if (r_fb.screentexturewidth && r_fb.screentextureheight)
+               {
+                       if (r_motionblur.value > 0 || r_damageblur.value > 0)
+                               r_fb.ghosttexture = R_LoadTexture2D(r_main_texturepool, "framebuffermotionblur", r_fb.screentexturewidth, r_fb.screentextureheight, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
+                       r_fb.ghosttexture_valid = false;
+                       r_fb.colortexture = R_LoadTexture2D(r_main_texturepool, "framebuffercolor", r_fb.screentexturewidth, r_fb.screentextureheight, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
+                       if (useviewfbo)
+                       {
+                               // FIXME: choose depth bits based on a cvar
+                               r_fb.depthtexture = R_LoadTextureShadowMap2D(r_main_texturepool, "framebufferdepth", r_fb.screentexturewidth, r_fb.screentextureheight, 24, false);
+                               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);
+#ifndef USE_GLES2
+                               // render depth into one texture and color into the other
+                               if (qglDrawBuffer)
+                               {
+                                       int status;
+                                       qglDrawBuffer(GL_COLOR_ATTACHMENT0);CHECKGLERROR
+                                       qglReadBuffer(GL_COLOR_ATTACHMENT0);CHECKGLERROR
+                                       status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER);CHECKGLERROR
+                                       if (status != GL_FRAMEBUFFER_COMPLETE)
+                                               Con_Printf("R_Bloom_StartFrame: glCheckFramebufferStatusEXT returned %i\n", status);
+                               }
+#endif
+                       }
+               }
+
+               if (r_fb.bloomtexturewidth && r_fb.bloomtextureheight)
+               {
+                       for (i = 0;i < (int)(sizeof(r_fb.bloomtexture)/sizeof(r_fb.bloomtexture[i]));i++)
                        {
-                               int status;
-                               qglDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);CHECKGLERROR
-                               qglReadBuffer(GL_COLOR_ATTACHMENT0_EXT);CHECKGLERROR
-                               status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
-                               if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
-                                       Con_Printf("R_Bloom_StartFrame: glCheckFramebufferStatusEXT returned %i\n", status);
+                               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);
                        }
                }
-               r_bloomstate.bloomtexturewidth = bloomtexturewidth;
-               r_bloomstate.bloomtextureheight = bloomtextureheight;
-               if (r_bloomstate.bloomtexturewidth && r_bloomstate.bloomtextureheight)
-                       r_bloomstate.texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", r_bloomstate.bloomtexturewidth, r_bloomstate.bloomtextureheight, NULL, textype, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
-               r_bloomstate.viewfbo = r_viewfbo.integer;
-               r_bloomstate.texturetype = textype;
        }
 
-       // when doing a reduced render (HDR) we want to use a smaller area
-       r_bloomstate.bloomwidth = bound(1, r_bloom_resolution.integer, r_refdef.view.height);
-       r_bloomstate.bloomheight = r_bloomstate.bloomwidth * r_refdef.view.height / r_refdef.view.width;
-       r_bloomstate.bloomheight = bound(1, r_bloomstate.bloomheight, r_refdef.view.height);
-       r_bloomstate.bloomwidth = bound(1, r_bloomstate.bloomwidth, r_bloomstate.bloomtexturewidth);
-       r_bloomstate.bloomheight = bound(1, r_bloomstate.bloomheight, r_bloomstate.bloomtextureheight);
+       // bloom texture is a different resolution
+       r_fb.bloomwidth = bound(1, r_bloom_resolution.integer, r_refdef.view.height);
+       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_bloomstate.screentexcoord2f[0] = 0;
-       r_bloomstate.screentexcoord2f[1] = (float)viewheight    / (float)r_bloomstate.screentextureheight;
-       r_bloomstate.screentexcoord2f[2] = (float)viewwidth     / (float)r_bloomstate.screentexturewidth;
-       r_bloomstate.screentexcoord2f[3] = (float)viewheight    / (float)r_bloomstate.screentextureheight;
-       r_bloomstate.screentexcoord2f[4] = (float)viewwidth     / (float)r_bloomstate.screentexturewidth;
-       r_bloomstate.screentexcoord2f[5] = 0;
-       r_bloomstate.screentexcoord2f[6] = 0;
-       r_bloomstate.screentexcoord2f[7] = 0;
+       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;
 
        // set up a texcoord array for the reduced resolution bloom image
        // (which will be additive blended over the screen image)
-       r_bloomstate.bloomtexcoord2f[0] = 0;
-       r_bloomstate.bloomtexcoord2f[1] = (float)r_bloomstate.bloomheight / (float)r_bloomstate.bloomtextureheight;
-       r_bloomstate.bloomtexcoord2f[2] = (float)r_bloomstate.bloomwidth  / (float)r_bloomstate.bloomtexturewidth;
-       r_bloomstate.bloomtexcoord2f[3] = (float)r_bloomstate.bloomheight / (float)r_bloomstate.bloomtextureheight;
-       r_bloomstate.bloomtexcoord2f[4] = (float)r_bloomstate.bloomwidth  / (float)r_bloomstate.bloomtexturewidth;
-       r_bloomstate.bloomtexcoord2f[5] = 0;
-       r_bloomstate.bloomtexcoord2f[6] = 0;
-       r_bloomstate.bloomtexcoord2f[7] = 0;
+       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)
        {
@@ -6057,35 +6233,41 @@ void R_Bloom_StartFrame(void)
                        int i;
                        for (i = 0;i < 4;i++)
                        {
-                               r_bloomstate.screentexcoord2f[i*2+0] += 0.5f / (float)r_bloomstate.screentexturewidth;
-                               r_bloomstate.screentexcoord2f[i*2+1] += 0.5f / (float)r_bloomstate.screentextureheight;
-                               r_bloomstate.bloomtexcoord2f[i*2+0] += 0.5f / (float)r_bloomstate.bloomtexturewidth;
-                               r_bloomstate.bloomtexcoord2f[i*2+1] += 0.5f / (float)r_bloomstate.bloomtextureheight;
+                               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;
        }
 
-       if ((r_hdr.integer || r_bloom.integer) && r_bloomstate.bloomwidth)
-       {
-               r_bloomstate.enabled = true;
-               r_bloomstate.hdr = r_hdr.integer != 0 && !r_bloomstate.fbo_framebuffer;
-       }
-
-       R_Viewport_InitOrtho(&r_bloomstate.viewport, &identitymatrix, r_refdef.view.x, vid.height - r_bloomstate.bloomheight - r_refdef.view.y, r_bloomstate.bloomwidth, r_bloomstate.bloomheight, 0, 0, 1, 1, -10, 100, NULL);
+       R_Viewport_InitOrtho(&r_fb.bloomviewport, &identitymatrix, r_refdef.view.x, (r_fb.bloomfbo[0] ? r_fb.bloomtextureheight : vid.height) - r_fb.bloomheight - r_refdef.view.y, r_fb.bloomwidth, r_fb.bloomheight, 0, 0, 1, 1, -10, 100, NULL);
 
-       if (r_bloomstate.fbo_framebuffer)
+       if (r_fb.fbo)
                r_refdef.view.clear = true;
 }
 
-void R_Bloom_CopyBloomTexture(float colorscale)
+void R_Bloom_MakeTexture(void)
 {
+       int x, range, dir;
+       float xoffset, yoffset, r, brighten;
+       rtexture_t *intex;
+       float colorscale = r_bloom_colorscale.value;
+
        r_refdef.stats.bloom++;
 
+       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.bloom_copypixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
+       }
+
        // scale down screen texture to the bloom texture size
        CHECKGLERROR
-       R_Mesh_SetMainRenderTargets();
-       R_SetViewport(&r_bloomstate.viewport);
+       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);
        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...
@@ -6097,90 +6279,81 @@ void R_Bloom_CopyBloomTexture(float colorscale)
        case RENDERPATH_GLES1:
        case RENDERPATH_GLES2:
        case RENDERPATH_SOFT:
-               R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.screentexcoord2f);
+               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_bloomstate.screentexcoord2f);
+               R_Mesh_PrepareVertices_Generic_Arrays(4, r_d3dscreenvertex3f, NULL, r_fb.screentexcoord2f);
                break;
        }
        // TODO: do boxfilter scale-down in shader?
-       R_SetupShader_Generic(r_bloomstate.texture_screen, NULL, GL_MODULATE, 1, true);
+       R_SetupShader_Generic(r_fb.colortexture, NULL, GL_MODULATE, 1, false, true, true);
        R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
-       r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
-
-       // we now have a bloom image in the framebuffer
-       // copy it into the bloom image texture for later processing
-       R_Mesh_CopyToTexture(r_bloomstate.texture_bloom, 0, 0, r_bloomstate.viewport.x, r_bloomstate.viewport.y, r_bloomstate.viewport.width, r_bloomstate.viewport.height);
-       r_refdef.stats.bloom_copypixels += r_bloomstate.viewport.width * r_bloomstate.viewport.height;
-}
-
-void R_Bloom_CopyHDRTexture(void)
-{
-       R_Mesh_CopyToTexture(r_bloomstate.texture_bloom, 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.bloom_copypixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
-}
-
-void R_Bloom_MakeTexture(void)
-{
-       int x, range, dir;
-       float xoffset, yoffset, r, brighten;
+       r_refdef.stats.bloom_drawpixels += r_fb.bloomwidth * r_fb.bloomheight;
 
-       r_refdef.stats.bloom++;
-
-       R_ResetViewRendering2D();
-
-       // we have a bloom image in the framebuffer
-       CHECKGLERROR
-       R_SetViewport(&r_bloomstate.viewport);
+       // 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.bloom_copypixels += r_fb.bloomviewport.width * r_fb.bloomviewport.height;
+       }
 
+       // multiply bloom image by itself as many times as desired
        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);
                x *= 2;
                r = bound(0, r_bloom_colorexponent.value / x, 1);
                GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
                GL_Color(r,r,r,1);
-               R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.bloomtexcoord2f);
-               R_SetupShader_Generic(r_bloomstate.texture_bloom, NULL, GL_MODULATE, 1, true);
+               R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_fb.bloomtexcoord2f);
+               R_SetupShader_Generic(intex, NULL, GL_MODULATE, 1, false, true, false);
                R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
-               r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+               r_refdef.stats.bloom_drawpixels += r_fb.bloomwidth * r_fb.bloomheight;
 
-               // copy the vertically blurred bloom view to a texture
-               R_Mesh_CopyToTexture(r_bloomstate.texture_bloom, 0, 0, r_bloomstate.viewport.x, r_bloomstate.viewport.y, r_bloomstate.viewport.width, r_bloomstate.viewport.height);
-               r_refdef.stats.bloom_copypixels += r_bloomstate.viewport.width * r_bloomstate.viewport.height;
+               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.bloom_copypixels += r_fb.bloomviewport.width * r_fb.bloomviewport.height;
+               }
        }
 
-       range = r_bloom_blur.integer * r_bloomstate.bloomwidth / 320;
+       range = r_bloom_blur.integer * r_fb.bloomwidth / 320;
        brighten = r_bloom_brighten.value;
-       if (r_bloomstate.hdr)
-               brighten *= r_hdr_range.value;
        brighten = sqrt(brighten);
        if(range >= 1)
                brighten *= (3 * range) / (2 * range - 1); // compensate for the "dot particle"
-       R_SetupShader_Generic(r_bloomstate.texture_bloom, NULL, GL_MODULATE, 1, true);
 
        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);
                // 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);
                for (x = -range;x <= range;x++)
                {
                        if (!dir){xoffset = 0;yoffset = x;}
                        else {xoffset = x;yoffset = 0;}
-                       xoffset /= (float)r_bloomstate.bloomtexturewidth;
-                       yoffset /= (float)r_bloomstate.bloomtextureheight;
+                       xoffset /= (float)r_fb.bloomtexturewidth;
+                       yoffset /= (float)r_fb.bloomtextureheight;
                        // compute a texcoord array with the specified x and y offset
-                       r_bloomstate.offsettexcoord2f[0] = xoffset+0;
-                       r_bloomstate.offsettexcoord2f[1] = yoffset+(float)r_bloomstate.bloomheight / (float)r_bloomstate.bloomtextureheight;
-                       r_bloomstate.offsettexcoord2f[2] = xoffset+(float)r_bloomstate.bloomwidth / (float)r_bloomstate.bloomtexturewidth;
-                       r_bloomstate.offsettexcoord2f[3] = yoffset+(float)r_bloomstate.bloomheight / (float)r_bloomstate.bloomtextureheight;
-                       r_bloomstate.offsettexcoord2f[4] = xoffset+(float)r_bloomstate.bloomwidth / (float)r_bloomstate.bloomtexturewidth;
-                       r_bloomstate.offsettexcoord2f[5] = yoffset+0;
-                       r_bloomstate.offsettexcoord2f[6] = xoffset+0;
-                       r_bloomstate.offsettexcoord2f[7] = yoffset+0;
+                       r_fb.offsettexcoord2f[0] = xoffset+0;
+                       r_fb.offsettexcoord2f[1] = yoffset+(float)r_fb.bloomheight / (float)r_fb.bloomtextureheight;
+                       r_fb.offsettexcoord2f[2] = xoffset+(float)r_fb.bloomwidth / (float)r_fb.bloomtexturewidth;
+                       r_fb.offsettexcoord2f[3] = yoffset+(float)r_fb.bloomheight / (float)r_fb.bloomtextureheight;
+                       r_fb.offsettexcoord2f[4] = xoffset+(float)r_fb.bloomwidth / (float)r_fb.bloomtexturewidth;
+                       r_fb.offsettexcoord2f[5] = yoffset+0;
+                       r_fb.offsettexcoord2f[6] = xoffset+0;
+                       r_fb.offsettexcoord2f[7] = yoffset+0;
                        // this r value looks like a 'dot' particle, fading sharply to
                        // black at the edges
                        // (probably not realistic but looks good enough)
@@ -6190,79 +6363,22 @@ void R_Bloom_MakeTexture(void)
                        if(range >= 1)
                                r *= (1 - x*x/(float)(range*range));
                        GL_Color(r, r, r, 1);
-                       R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.offsettexcoord2f);
+                       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.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+                       r_refdef.stats.bloom_drawpixels += r_fb.bloomwidth * r_fb.bloomheight;
                        GL_BlendFunc(GL_ONE, GL_ONE);
                }
 
-               // copy the vertically blurred bloom view to a texture
-               R_Mesh_CopyToTexture(r_bloomstate.texture_bloom, 0, 0, r_bloomstate.viewport.x, r_bloomstate.viewport.y, r_bloomstate.viewport.width, r_bloomstate.viewport.height);
-               r_refdef.stats.bloom_copypixels += r_bloomstate.viewport.width * r_bloomstate.viewport.height;
+               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.bloom_copypixels += r_fb.bloomviewport.width * r_fb.bloomviewport.height;
+               }
        }
 }
 
-void R_HDR_RenderBloomTexture(void)
-{
-       int oldwidth, oldheight;
-       float oldcolorscale;
-       qboolean oldwaterstate;
-
-       oldwaterstate = r_waterstate.enabled;
-       oldcolorscale = r_refdef.view.colorscale;
-       oldwidth = r_refdef.view.width;
-       oldheight = r_refdef.view.height;
-       r_refdef.view.width = r_bloomstate.bloomwidth;
-       r_refdef.view.height = r_bloomstate.bloomheight;
-
-       if(r_hdr.integer < 2)
-               r_waterstate.enabled = false;
-
-       // TODO: support GL_EXT_framebuffer_object rather than reusing the framebuffer?  it might improve SLI performance.
-       // TODO: add exposure compensation features
-       // TODO: add fp16 framebuffer support (using GL_EXT_framebuffer_object)
-
-       r_refdef.view.showdebug = false;
-       r_refdef.view.colorscale *= r_bloom_colorscale.value / bound(1, r_hdr_range.value, 16);
-
-       R_ResetViewRendering3D();
-
-       R_ClearScreen(r_refdef.fogenabled);
-       if (r_timereport_active)
-               R_TimeReport("HDRclear");
-
-       R_View_Update();
-       if (r_timereport_active)
-               R_TimeReport("visibility");
-
-       // only do secondary renders with HDR if r_hdr is 2 or higher
-       r_waterstate.numwaterplanes = 0;
-       if (r_waterstate.enabled)
-               R_RenderWaterPlanes();
-
-       r_refdef.view.showdebug = true;
-       R_RenderScene();
-       r_waterstate.numwaterplanes = 0;
-
-       R_ResetViewRendering2D();
-
-       R_Bloom_CopyHDRTexture();
-       R_Bloom_MakeTexture();
-
-       // restore the view settings
-       r_waterstate.enabled = oldwaterstate;
-       r_refdef.view.width = oldwidth;
-       r_refdef.view.height = oldheight;
-       r_refdef.view.colorscale = oldcolorscale;
-
-       R_ResetViewRendering3D();
-
-       R_ClearScreen(r_refdef.fogenabled);
-       if (r_timereport_active)
-               R_TimeReport("viewclear");
-}
-
-static void R_BlendView(void)
+static void R_BlendView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
 {
        unsigned int permutation;
        float uservecs[4][4];
@@ -6276,50 +6392,62 @@ static void R_BlendView(void)
        case RENDERPATH_SOFT:
        case RENDERPATH_GLES2:
                permutation =
-                         (r_bloomstate.texture_bloom ? SHADERPERMUTATION_BLOOM : 0)
+                         (r_fb.bloomtexture[r_fb.bloomindex] ? SHADERPERMUTATION_BLOOM : 0)
                        | (r_refdef.viewblend[3] > 0 ? SHADERPERMUTATION_VIEWTINT : 0)
                        | ((v_glslgamma.value && !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_bloomstate.texture_screen)
+               if (r_fb.colortexture)
                {
-                       // make sure the buffer is available
-                       if (r_bloom_blur.value < 1) { Cvar_SetValueQuick(&r_bloom_blur, 1); }
-
-                       R_ResetViewRendering2D();
-                       R_Mesh_SetMainRenderTargets();
+                       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.bloom_copypixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
+                       }
 
-                       if(!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0))
+                       if(!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0) && r_fb.ghosttexture)
                        {
                                // declare variables
-                               float speed;
-                               static float avgspeed;
+                               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));
 
-                               speed = VectorLength(cl.movement_velocity);
+                               // 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;
 
-                               cl.motionbluralpha = bound(0, (cl.time - cl.oldtime) / max(0.001, r_motionblur_vcoeff.value), 1);
-                               avgspeed = avgspeed * (1 - cl.motionbluralpha) + speed * cl.motionbluralpha;
+                               // enforce minimum amount of blur 
+                               blur_factor = blur_average * (1 - r_motionblur_minblur.value) + r_motionblur_minblur.value;
 
-                               speed = (avgspeed - r_motionblur_vmin.value) / max(1, r_motionblur_vmax.value - r_motionblur_vmin.value);
-                               speed = bound(0, speed, 1);
-                               speed = speed * (1 - r_motionblur_bmin.value) + r_motionblur_bmin.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 * speed / 80)
+                                                (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
-                               if (cl.motionbluralpha > 0 && !r_refdef.envmap)
+                               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);
@@ -6331,45 +6459,47 @@ static void R_BlendView(void)
                                        case RENDERPATH_GLES1:
                                        case RENDERPATH_GLES2:
                                        case RENDERPATH_SOFT:
-                                               R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.screentexcoord2f);
+                                               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_bloomstate.screentexcoord2f);
+                                               R_Mesh_PrepareVertices_Generic_Arrays(4, r_d3dscreenvertex3f, NULL, r_fb.screentexcoord2f);
                                                break;
                                        }
-                                       R_SetupShader_Generic(r_bloomstate.texture_screen, NULL, GL_MODULATE, 1, true);
+                                       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.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
                                }
-                       }
 
-                       // copy view into the screen texture
-                       R_Mesh_CopyToTexture(r_bloomstate.texture_screen, 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.bloom_copypixels += 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.bloom_copypixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
+                               r_fb.ghosttexture_valid = true;
+                       }
                }
-               else if (!r_bloomstate.texture_bloom)
+               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))
                        {
                                // apply a color tint to the whole view
-                               R_ResetViewRendering2D();
+                               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(NULL, NULL, GL_MODULATE, 1, true);
+                               R_SetupShader_Generic_NoTexture(false, true);
                                GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                                R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                        }
                        break; // no screen processing, no bloom, skip it
                }
 
-               if (r_bloomstate.texture_bloom && !r_bloomstate.hdr)
+               if (r_fb.bloomtexture[0])
                {
-                       // render simple bloom effect
-                       // copy the screen and shrink it and darken it for the bloom process
-                       R_Bloom_CopyBloomTexture(r_bloom_colorscale.value);
                        // make the bloom texture
                        R_Bloom_MakeTexture();
                }
@@ -6387,7 +6517,7 @@ static void R_BlendView(void)
                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();
+               R_ResetViewRendering2D(0, NULL, NULL); // here we render to the real framebuffer!
                GL_Color(1, 1, 1, 1);
                GL_BlendFunc(GL_ONE, GL_ZERO);
 
@@ -6395,13 +6525,13 @@ static void R_BlendView(void)
                {
                case RENDERPATH_GL20:
                case RENDERPATH_GLES2:
-                       R_Mesh_PrepareVertices_Mesh_Arrays(4, r_screenvertex3f, NULL, NULL, NULL, NULL, r_bloomstate.screentexcoord2f, r_bloomstate.bloomtexcoord2f);
+                       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_bloomstate.texture_screen);
-                       if (r_glsl_permutation->tex_Texture_Second          >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second    , r_bloomstate.texture_bloom );
+                       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_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_bloomstate.screentexturewidth, 1.0/r_bloomstate.screentextureheight);
+                       if (r_glsl_permutation->loc_PixelSize               >= 0) qglUniform2f(r_glsl_permutation->loc_PixelSize         , 1.0/r_fb.screentexturewidth, 1.0/r_fb.screentextureheight);
                        if (r_glsl_permutation->loc_UserVec1                >= 0) qglUniform4f(r_glsl_permutation->loc_UserVec1          , uservecs[0][0], uservecs[0][1], uservecs[0][2], uservecs[0][3]);
                        if (r_glsl_permutation->loc_UserVec2                >= 0) qglUniform4f(r_glsl_permutation->loc_UserVec2          , uservecs[1][0], uservecs[1][1], uservecs[1][2], uservecs[1][3]);
                        if (r_glsl_permutation->loc_UserVec3                >= 0) qglUniform4f(r_glsl_permutation->loc_UserVec3          , uservecs[2][0], uservecs[2][1], uservecs[2][2], uservecs[2][3]);
@@ -6413,13 +6543,13 @@ static void R_BlendView(void)
                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_bloomstate.screentexcoord2f, r_bloomstate.bloomtexcoord2f);
+                       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_bloomstate.texture_screen);
-                       R_Mesh_TexBind(GL20TU_SECOND    , r_bloomstate.texture_bloom );
+                       R_Mesh_TexBind(GL20TU_FIRST     , r_fb.colortexture);
+                       R_Mesh_TexBind(GL20TU_SECOND    , r_fb.bloomtexture[r_fb.bloomindex]);
                        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_bloomstate.screentexturewidth, 1.0/r_bloomstate.screentextureheight);
+                       hlslPSSetParameter2f(D3DPSREGISTER_PixelSize            , 1.0/r_fb.screentexturewidth, 1.0/r_fb.screentextureheight);
                        hlslPSSetParameter4f(D3DPSREGISTER_UserVec1             , uservecs[0][0], uservecs[0][1], uservecs[0][2], uservecs[0][3]);
                        hlslPSSetParameter4f(D3DPSREGISTER_UserVec2             , uservecs[1][0], uservecs[1][1], uservecs[1][2], uservecs[1][3]);
                        hlslPSSetParameter4f(D3DPSREGISTER_UserVec3             , uservecs[2][0], uservecs[2][1], uservecs[2][2], uservecs[2][3]);
@@ -6436,13 +6566,13 @@ static void R_BlendView(void)
                        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_bloomstate.screentexcoord2f, r_bloomstate.bloomtexcoord2f);
+                       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_bloomstate.texture_screen);
-                       R_Mesh_TexBind(GL20TU_SECOND    , r_bloomstate.texture_bloom );
+                       R_Mesh_TexBind(GL20TU_FIRST     , r_fb.colortexture);
+                       R_Mesh_TexBind(GL20TU_SECOND    , r_fb.bloomtexture[r_fb.bloomindex]);
                        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_bloomstate.screentexturewidth, 1.0/r_bloomstate.screentextureheight);
+                       DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_PixelSize         , 1.0/r_fb.screentexturewidth, 1.0/r_fb.screentextureheight);
                        DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_UserVec1          , uservecs[0][0], uservecs[0][1], uservecs[0][2], uservecs[0][3]);
                        DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_UserVec2          , uservecs[1][0], uservecs[1][1], uservecs[1][2], uservecs[1][3]);
                        DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_UserVec3          , uservecs[2][0], uservecs[2][1], uservecs[2][2], uservecs[2][3]);
@@ -6463,10 +6593,10 @@ static void R_BlendView(void)
                if (r_refdef.viewblend[3] >= (1.0f / 256.0f))
                {
                        // apply a color tint to the whole view
-                       R_ResetViewRendering2D();
+                       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(NULL, NULL, GL_MODULATE, 1, true);
+                       R_SetupShader_Generic_NoTexture(false, true);
                        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                        R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                }
@@ -6476,67 +6606,9 @@ static void R_BlendView(void)
 
 matrix4x4_t r_waterscrollmatrix;
 
-void R_UpdateFogColor(void) // needs to be called before HDR subrender too, as that changes colorscale!
-{
-       if (r_refdef.fog_density)
-       {
-               r_refdef.fogcolor[0] = r_refdef.fog_red;
-               r_refdef.fogcolor[1] = r_refdef.fog_green;
-               r_refdef.fogcolor[2] = r_refdef.fog_blue;
-
-               Vector4Set(r_refdef.fogplane, 0, 0, 1, -r_refdef.fog_height);
-               r_refdef.fogplaneviewdist = DotProduct(r_refdef.fogplane, r_refdef.view.origin) + r_refdef.fogplane[3];
-               r_refdef.fogplaneviewabove = r_refdef.fogplaneviewdist >= 0;
-               r_refdef.fogheightfade = -0.5f/max(0.125f, r_refdef.fog_fadedepth);
-
-               {
-                       vec3_t fogvec;
-                       VectorCopy(r_refdef.fogcolor, fogvec);
-                       //   color.rgb *= ContrastBoost * SceneBrightness;
-                       VectorScale(fogvec, r_refdef.view.colorscale, fogvec);
-                       r_refdef.fogcolor[0] = bound(0.0f, fogvec[0], 1.0f);
-                       r_refdef.fogcolor[1] = bound(0.0f, fogvec[1], 1.0f);
-                       r_refdef.fogcolor[2] = bound(0.0f, fogvec[2], 1.0f);
-               }
-       }
-}
-
-void R_UpdateVariables(void)
+void R_UpdateFog(void)
 {
-       R_Textures_Frame();
-
-       r_refdef.scene.ambient = r_ambient.value * (1.0f / 64.0f);
-
-       r_refdef.farclip = r_farclip_base.value;
-       if (r_refdef.scene.worldmodel)
-               r_refdef.farclip += r_refdef.scene.worldmodel->radius * r_farclip_world.value * 2;
-       r_refdef.nearclip = bound (0.001f, r_nearclip.value, r_refdef.farclip - 1.0f);
-
-       if (r_shadow_frontsidecasting.integer < 0 || r_shadow_frontsidecasting.integer > 1)
-               Cvar_SetValueQuick(&r_shadow_frontsidecasting, 1);
-       r_refdef.polygonfactor = 0;
-       r_refdef.polygonoffset = 0;
-       r_refdef.shadowpolygonfactor = r_refdef.polygonfactor + r_shadow_polygonfactor.value * (r_shadow_frontsidecasting.integer ? 1 : -1);
-       r_refdef.shadowpolygonoffset = r_refdef.polygonoffset + r_shadow_polygonoffset.value * (r_shadow_frontsidecasting.integer ? 1 : -1);
-
-       r_refdef.scene.rtworld = r_shadow_realtime_world.integer != 0;
-       r_refdef.scene.rtworldshadows = r_shadow_realtime_world_shadows.integer && vid.stencil;
-       r_refdef.scene.rtdlight = r_shadow_realtime_dlight.integer != 0 && !gl_flashblend.integer && r_dynamic.integer;
-       r_refdef.scene.rtdlightshadows = r_refdef.scene.rtdlight && r_shadow_realtime_dlight_shadows.integer && vid.stencil;
-       r_refdef.lightmapintensity = r_refdef.scene.rtworld ? r_shadow_realtime_world_lightmaps.value : 1;
-       if (FAKELIGHT_ENABLED)
-       {
-               r_refdef.lightmapintensity *= r_fakelight_intensity.value;
-       }
-       if (r_showsurfaces.integer)
-       {
-               r_refdef.scene.rtworld = false;
-               r_refdef.scene.rtworldshadows = false;
-               r_refdef.scene.rtdlight = false;
-               r_refdef.scene.rtdlightshadows = false;
-               r_refdef.lightmapintensity = 0;
-       }
-
+       // Nehahra fog
        if (gamemode == GAME_NEHAHRA)
        {
                if (gl_fogenable.integer)
@@ -6567,12 +6639,11 @@ void R_UpdateVariables(void)
                }
        }
 
+       // fog parms
        r_refdef.fog_alpha = bound(0, r_refdef.fog_alpha, 1);
        r_refdef.fog_start = max(0, r_refdef.fog_start);
        r_refdef.fog_end = max(r_refdef.fog_start + 0.01, r_refdef.fog_end);
 
-       // R_UpdateFogColor(); // why? R_RenderScene does it anyway
-
        if (r_refdef.fog_density && r_drawfog.integer)
        {
                r_refdef.fogenabled = true;
@@ -6598,6 +6669,66 @@ void R_UpdateVariables(void)
        else
                r_refdef.fogenabled = false;
 
+       // fog color
+       if (r_refdef.fog_density)
+       {
+               r_refdef.fogcolor[0] = r_refdef.fog_red;
+               r_refdef.fogcolor[1] = r_refdef.fog_green;
+               r_refdef.fogcolor[2] = r_refdef.fog_blue;
+
+               Vector4Set(r_refdef.fogplane, 0, 0, 1, -r_refdef.fog_height);
+               r_refdef.fogplaneviewdist = DotProduct(r_refdef.fogplane, r_refdef.view.origin) + r_refdef.fogplane[3];
+               r_refdef.fogplaneviewabove = r_refdef.fogplaneviewdist >= 0;
+               r_refdef.fogheightfade = -0.5f/max(0.125f, r_refdef.fog_fadedepth);
+
+               {
+                       vec3_t fogvec;
+                       VectorCopy(r_refdef.fogcolor, fogvec);
+                       //   color.rgb *= ContrastBoost * SceneBrightness;
+                       VectorScale(fogvec, r_refdef.view.colorscale, fogvec);
+                       r_refdef.fogcolor[0] = bound(0.0f, fogvec[0], 1.0f);
+                       r_refdef.fogcolor[1] = bound(0.0f, fogvec[1], 1.0f);
+                       r_refdef.fogcolor[2] = bound(0.0f, fogvec[2], 1.0f);
+               }
+       }
+}
+
+void R_UpdateVariables(void)
+{
+       R_Textures_Frame();
+
+       r_refdef.scene.ambient = r_ambient.value * (1.0f / 64.0f);
+
+       r_refdef.farclip = r_farclip_base.value;
+       if (r_refdef.scene.worldmodel)
+               r_refdef.farclip += r_refdef.scene.worldmodel->radius * r_farclip_world.value * 2;
+       r_refdef.nearclip = bound (0.001f, r_nearclip.value, r_refdef.farclip - 1.0f);
+
+       if (r_shadow_frontsidecasting.integer < 0 || r_shadow_frontsidecasting.integer > 1)
+               Cvar_SetValueQuick(&r_shadow_frontsidecasting, 1);
+       r_refdef.polygonfactor = 0;
+       r_refdef.polygonoffset = 0;
+       r_refdef.shadowpolygonfactor = r_refdef.polygonfactor + r_shadow_polygonfactor.value * (r_shadow_frontsidecasting.integer ? 1 : -1);
+       r_refdef.shadowpolygonoffset = r_refdef.polygonoffset + r_shadow_polygonoffset.value * (r_shadow_frontsidecasting.integer ? 1 : -1);
+
+       r_refdef.scene.rtworld = r_shadow_realtime_world.integer != 0;
+       r_refdef.scene.rtworldshadows = r_shadow_realtime_world_shadows.integer && vid.stencil;
+       r_refdef.scene.rtdlight = r_shadow_realtime_dlight.integer != 0 && !gl_flashblend.integer && r_dynamic.integer;
+       r_refdef.scene.rtdlightshadows = r_refdef.scene.rtdlight && r_shadow_realtime_dlight_shadows.integer && vid.stencil;
+       r_refdef.lightmapintensity = r_refdef.scene.rtworld ? r_shadow_realtime_world_lightmaps.value : 1;
+       if (FAKELIGHT_ENABLED)
+       {
+               r_refdef.lightmapintensity *= r_fakelight_intensity.value;
+       }
+       if (r_showsurfaces.integer)
+       {
+               r_refdef.scene.rtworld = false;
+               r_refdef.scene.rtworldshadows = false;
+               r_refdef.scene.rtdlight = false;
+               r_refdef.scene.rtdlightshadows = false;
+               r_refdef.lightmapintensity = 0;
+       }
+
        switch(vid.renderpath)
        {
        case RENDERPATH_GL20:
@@ -6680,15 +6811,51 @@ r_refdef_scene_t * R_GetScenePointer( r_refdef_scene_type_t scenetype )
        }
 }
 
+int R_SortEntities_Compare(const void *ap, const void *bp)
+{
+       const entity_render_t *a = *(const entity_render_t **)ap;
+       const entity_render_t *b = *(const entity_render_t **)bp;
+
+       // 1. compare model
+       if(a->model < b->model)
+               return -1;
+       if(a->model > b->model)
+               return +1;
+
+       // 2. compare skin
+       // TODO possibly calculate the REAL skinnum here first using
+       // skinscenes?
+       if(a->skinnum < b->skinnum)
+               return -1;
+       if(a->skinnum > b->skinnum)
+               return +1;
+
+       // everything we compared is equal
+       return 0;
+}
+void R_SortEntities(void)
+{
+       // below or equal 2 ents, sorting never gains anything
+       if(r_refdef.scene.numentities <= 2)
+               return;
+       // sort
+       qsort(r_refdef.scene.entities, r_refdef.scene.numentities, sizeof(*r_refdef.scene.entities), R_SortEntities_Compare);
+}
+
 /*
 ================
 R_RenderView
 ================
 */
 int dpsoftrast_test;
+extern void R_Shadow_UpdateBounceGridTexture(void);
+extern cvar_t r_shadow_bouncegrid;
 void R_RenderView(void)
 {
        matrix4x4_t originalmatrix = r_refdef.view.matrix, offsetmatrix;
+       int fbo;
+       rtexture_t *depthtexture;
+       rtexture_t *colortexture;
 
        dpsoftrast_test = r_test.integer;
 
@@ -6702,6 +6869,8 @@ void R_RenderView(void)
 
        if (!r_drawentities.integer)
                r_refdef.scene.numentities = 0;
+       else if (r_sortentities.integer)
+               R_SortEntities();
 
        R_AnimCache_ClearCache();
        R_FrameData_NewFrame();
@@ -6716,15 +6885,16 @@ void R_RenderView(void)
        if (r_refdef.view.isoverlay)
        {
                // TODO: FIXME: move this into its own backend function maybe? [2/5/2008 Andreas]
+               R_Mesh_SetRenderTargets(0, NULL, NULL, NULL, NULL, NULL);
                GL_Clear(GL_DEPTH_BUFFER_BIT, NULL, 1.0f, 0);
                R_TimeReport("depthclear");
 
                r_refdef.view.showdebug = false;
 
-               r_waterstate.enabled = false;
-               r_waterstate.numwaterplanes = 0;
+               r_fb.water.enabled = false;
+               r_fb.water.numwaterplanes = 0;
 
-               R_RenderScene();
+               R_RenderScene(0, NULL, NULL);
 
                r_refdef.view.matrix = originalmatrix;
 
@@ -6747,13 +6917,18 @@ void R_RenderView(void)
        R_Bloom_StartFrame();
        R_Water_StartFrame();
 
+       // now we probably have an fbo to render into
+       fbo = r_fb.fbo;
+       depthtexture = r_fb.depthtexture;
+       colortexture = r_fb.colortexture;
+
        CHECKGLERROR
        if (r_timereport_active)
                R_TimeReport("viewsetup");
 
-       R_ResetViewRendering3D();
+       R_ResetViewRendering3D(fbo, depthtexture, colortexture);
 
-       if (r_refdef.view.clear || r_refdef.fogenabled)
+       if (r_refdef.view.clear || r_refdef.fogenabled || fbo)
        {
                R_ClearScreen(r_refdef.fogenabled);
                if (r_timereport_active)
@@ -6761,28 +6936,24 @@ void R_RenderView(void)
        }
        r_refdef.view.clear = true;
 
-       // this produces a bloom texture to be used in R_BlendView() later
-       if (r_bloomstate.hdr)
-       {
-               R_HDR_RenderBloomTexture();
-               // we have to bump the texture frame again because r_refdef.view.colorscale is cached in the textures
-               r_textureframe++; // used only by R_GetCurrentTexture
-       }
-
        r_refdef.view.showdebug = true;
 
        R_View_Update();
        if (r_timereport_active)
                R_TimeReport("visibility");
 
-       r_waterstate.numwaterplanes = 0;
-       if (r_waterstate.enabled)
-               R_RenderWaterPlanes();
+       R_Shadow_UpdateBounceGridTexture();
+       if (r_timereport_active && r_shadow_bouncegrid.integer)
+               R_TimeReport("bouncegrid");
+
+       r_fb.water.numwaterplanes = 0;
+       if (r_fb.water.enabled)
+               R_RenderWaterPlanes(fbo, depthtexture, colortexture);
 
-       R_RenderScene();
-       r_waterstate.numwaterplanes = 0;
+       R_RenderScene(fbo, depthtexture, colortexture);
+       r_fb.water.numwaterplanes = 0;
 
-       R_BlendView();
+       R_BlendView(fbo, depthtexture, colortexture);
        if (r_timereport_active)
                R_TimeReport("blendview");
 
@@ -6794,7 +6965,7 @@ void R_RenderView(void)
        CHECKGLERROR
 }
 
-void R_RenderWaterPlanes(void)
+void R_RenderWaterPlanes(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
 {
        if (cl.csqc_vidvars.drawworld && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->DrawAddWaterPlanes)
        {
@@ -6811,9 +6982,9 @@ void R_RenderWaterPlanes(void)
        if (r_timereport_active)
                R_TimeReport("watermodels");
 
-       if (r_waterstate.numwaterplanes)
+       if (r_fb.water.numwaterplanes)
        {
-               R_Water_ProcessPlanes();
+               R_Water_ProcessPlanes(fbo, depthtexture, colortexture);
                if (r_timereport_active)
                        R_TimeReport("waterscenes");
        }
@@ -6826,11 +6997,9 @@ extern cvar_t cl_locs_show;
 static void R_DrawLocs(void);
 static void R_DrawEntityBBoxes(void);
 static void R_DrawModelDecals(void);
-extern void R_DrawModelShadows(void);
-extern void R_DrawModelShadowMaps(void);
 extern cvar_t cl_decals_newsystem;
 extern qboolean r_shadow_usingdeferredprepass;
-void R_RenderScene(void)
+void R_RenderScene(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
 {
        qboolean shadowmapping = false;
 
@@ -6839,7 +7008,7 @@ void R_RenderScene(void)
 
        r_refdef.stats.renders++;
 
-       R_UpdateFogColor();
+       R_UpdateFog();
 
        // don't let sound skip if going slow
        if (r_refdef.scene.extraupdate)
@@ -6873,19 +7042,15 @@ void R_RenderScene(void)
                if (skyrendermasked && skyrenderlater)
                {
                        // we have to force off the water clipping plane while rendering sky
-                       R_SetupView(false);
+                       R_SetupView(false, fbo, depthtexture, colortexture);
                        R_Sky();
-                       R_SetupView(true);
+                       R_SetupView(true, fbo, depthtexture, colortexture);
                        if (r_timereport_active)
                                R_TimeReport("sky");
                }
        }
 
-       R_AnimCache_CacheVisibleEntities();
-       if (r_timereport_active)
-               R_TimeReport("animation");
-
-       R_Shadow_PrepareLights();
+       R_Shadow_PrepareLights(fbo, depthtexture, colortexture);
        if (r_shadows.integer > 0 && r_refdef.lightmapintensity > 0)
                R_Shadow_PrepareModelShadows();
        if (r_timereport_active)
@@ -6912,8 +7077,9 @@ void R_RenderScene(void)
 
        if (r_shadows.integer >= 2 && shadowmapping && r_refdef.lightmapintensity > 0)
        {
-               R_DrawModelShadowMaps();
-               R_ResetViewRendering3D();
+               R_ResetViewRendering3D(fbo, depthtexture, colortexture);
+               R_DrawModelShadowMaps(fbo, depthtexture, colortexture);
+               R_ResetViewRendering3D(fbo, depthtexture, colortexture);
                // don't let sound skip if going slow
                if (r_refdef.scene.extraupdate)
                        S_ExtraUpdate ();
@@ -6940,8 +7106,9 @@ void R_RenderScene(void)
 
        if ((r_shadows.integer == 1 || (r_shadows.integer > 0 && !shadowmapping)) && !r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0)
        {
-               R_DrawModelShadows();
-               R_ResetViewRendering3D();
+               R_ResetViewRendering3D(fbo, depthtexture, colortexture);
+               R_DrawModelShadows(fbo, depthtexture, colortexture);
+               R_ResetViewRendering3D(fbo, depthtexture, colortexture);
                // don't let sound skip if going slow
                if (r_refdef.scene.extraupdate)
                        S_ExtraUpdate ();
@@ -6960,8 +7127,9 @@ void R_RenderScene(void)
 
        if ((r_shadows.integer == 1 || (r_shadows.integer > 0 && !shadowmapping)) && r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0)
        {
-               R_DrawModelShadows();
-               R_ResetViewRendering3D();
+               R_ResetViewRendering3D(fbo, depthtexture, colortexture);
+               R_DrawModelShadows(fbo, depthtexture, colortexture);
+               R_ResetViewRendering3D(fbo, depthtexture, colortexture);
                // don't let sound skip if going slow
                if (r_refdef.scene.extraupdate)
                        S_ExtraUpdate ();
@@ -7069,8 +7237,6 @@ void R_RenderScene(void)
        // don't let sound skip if going slow
        if (r_refdef.scene.extraupdate)
                S_ExtraUpdate ();
-
-       R_ResetViewRendering2D();
 }
 
 static const unsigned short bboxelements[36] =
@@ -7118,7 +7284,7 @@ void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, floa
        }
        R_Mesh_PrepareVertices_Generic_Arrays(8, vertex3f, color4f, NULL);
        R_Mesh_ResetTextureState();
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
+       R_SetupShader_Generic_NoTexture(false, false);
        R_Mesh_Draw(0, 8, 0, 12, NULL, NULL, 0, bboxelements, NULL, 0);
 }
 
@@ -7134,7 +7300,7 @@ static void R_DrawEntityBBoxes_Callback(const entity_render_t *ent, const rtligh
                return;
 
        GL_CullFace(GL_NONE);
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
+       R_SetupShader_Generic_NoTexture(false, false);
 
        prog = 0;
        SV_VM_Begin();
@@ -7284,7 +7450,7 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, const rtlight
                }
        }
 //     R_Mesh_ResetTextureState();
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
+       R_SetupShader_Generic_NoTexture(false, false);
        R_Mesh_PrepareVertices_Generic_Arrays(6, nomodelvertex3f, color4f, NULL);
        R_Mesh_Draw(0, 6, 0, 8, nomodelelement3i, NULL, 0, nomodelelement3s, NULL, 0);
 }
@@ -7464,7 +7630,7 @@ static qboolean R_TestQ3WaveFunc(q3wavefunc_t func, const float *parms)
        if(parms[0] == 0 && parms[1] == 0)
                return false;
        if(func >> Q3WAVEFUNC_USER_SHIFT) // assumes rsurface to be set!
-               if(rsurface.userwavefunc_param[bound(0, (func >> Q3WAVEFUNC_USER_SHIFT) - 1, Q3WAVEFUNC_USER_COUNT)] == 0)
+               if(rsurface.userwavefunc_param[bound(0, (func >> Q3WAVEFUNC_USER_SHIFT) - 1, Q3WAVEFUNC_USER_COUNT - 1)] == 0)
                        return false;
        return true;
 }
@@ -7490,7 +7656,9 @@ static float R_EvaluateQ3WaveFunc(q3wavefunc_t func, const float *parms)
                index *= 4;
                f = index - floor(index);
                if (index < 1)
-                       f = f;
+               {
+                       // f = f;
+               }
                else if (index < 2)
                        f = 1 - f;
                else if (index < 3)
@@ -7501,7 +7669,7 @@ static float R_EvaluateQ3WaveFunc(q3wavefunc_t func, const float *parms)
        }
        f = parms[0] + parms[1] * f;
        if(func >> Q3WAVEFUNC_USER_SHIFT) // assumes rsurface to be set!
-               f *= rsurface.userwavefunc_param[bound(0, (func >> Q3WAVEFUNC_USER_SHIFT) - 1, Q3WAVEFUNC_USER_COUNT)];
+               f *= rsurface.userwavefunc_param[bound(0, (func >> Q3WAVEFUNC_USER_SHIFT) - 1, Q3WAVEFUNC_USER_COUNT - 1)];
        return (float) f;
 }
 
@@ -7527,8 +7695,9 @@ void R_tcMod_ApplyToMatrix(matrix4x4_t *texmatrix, q3shaderinfo_layer_tcmod_t *t
                        Matrix4x4_CreateTranslate(&matrix, 0, 0, 0);
                        break;
                case Q3TCMOD_ROTATE:
+                       f = tcmod->parms[0] * rsurface.shadertime;
                        Matrix4x4_CreateTranslate(&matrix, 0.5, 0.5, 0);
-                       Matrix4x4_ConcatRotate(&matrix, tcmod->parms[0] * rsurface.shadertime, 0, 0, 1);
+                       Matrix4x4_ConcatRotate(&matrix, (f / 360 - floor(f / 360)) * 360, 0, 0, 1);
                        Matrix4x4_ConcatTranslate(&matrix, -0.5, -0.5, 0);
                        break;
                case Q3TCMOD_SCALE:
@@ -7665,9 +7834,9 @@ texture_t *R_GetCurrentTexture(texture_t *t)
        t->currentalpha = rsurface.colormod[3];
        if (t->basematerialflags & MATERIALFLAG_WATERALPHA && (model->brush.supportwateralpha || r_novis.integer || r_trippy.integer))
                t->currentalpha *= r_wateralpha.value;
-       if(t->basematerialflags & MATERIALFLAG_WATERSHADER && r_waterstate.enabled && !r_refdef.view.isoverlay)
+       if(t->basematerialflags & MATERIALFLAG_WATERSHADER && r_fb.water.enabled && !r_refdef.view.isoverlay)
                t->currentmaterialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW; // we apply wateralpha later
-       if(!r_waterstate.enabled || r_refdef.view.isoverlay)
+       if(!r_fb.water.enabled || r_refdef.view.isoverlay)
                t->currentmaterialflags &= ~(MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA);
        if (!(rsurface.ent_flags & RENDER_LIGHT))
                t->currentmaterialflags |= MATERIALFLAG_FULLBRIGHT;
@@ -7687,6 +7856,9 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                t->currentmaterialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
        else if (t->currentalpha < 1)
                t->currentmaterialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
+       // LordHavoc: prevent bugs where code checks add or alpha at higher priority than customblend by clearing these flags
+       if (t->currentmaterialflags & MATERIALFLAG_CUSTOMBLEND)
+               t->currentmaterialflags &= ~(MATERIALFLAG_ADD | MATERIALFLAG_ALPHA);
        if (rsurface.ent_flags & RENDER_DOUBLESIDED)
                t->currentmaterialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
        if (rsurface.ent_flags & (RENDER_NODEPTHTEST | RENDER_VIEWMODEL))
@@ -7780,6 +7952,7 @@ texture_t *R_GetCurrentTexture(texture_t *t)
        }
        t->specularscale *= t->specularscalemod;
        t->specularpower *= t->specularpowermod;
+       t->rtlightambient = 0;
 
        // lightmaps mode looks bad with dlights using actual texturing, so turn
        // off the colormap and glossmap, but leave the normalmap on as it still
@@ -9260,7 +9433,7 @@ static int RSurf_FindWaterPlaneForSurface(const msurface_t *surface)
        r_waterstate_waterplane_t *p;
        qboolean prepared = false;
        bestd = 0;
-       for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
+       for (planeindex = 0, p = r_fb.water.waterplanes;planeindex < r_fb.water.numwaterplanes;planeindex++, p++)
        {
                if(p->camera_entity != rsurface.texture->camera_entity)
                        continue;
@@ -9593,7 +9766,7 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_
        // transparent sky would be ridiculous
        if (rsurface.texture->currentmaterialflags & MATERIALFLAGMASK_DEPTHSORTED)
                return;
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
+       R_SetupShader_Generic_NoTexture(false, false);
        skyrenderlater = true;
        RSurf_SetupDepthAndCulling();
        GL_DepthMask(true);
@@ -9622,7 +9795,7 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_
                }
                else
                {
-                       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
+                       R_SetupShader_Generic_NoTexture(false, false);
                        // fog sky
                        GL_BlendFunc(GL_ONE, GL_ZERO);
                        RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
@@ -9641,7 +9814,7 @@ extern rtexture_t *r_shadow_prepasslightingdiffusetexture;
 extern rtexture_t *r_shadow_prepasslightingspeculartexture;
 static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth, qboolean prepass)
 {
-       if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA)))
+       if (r_fb.water.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA)))
                return;
        if (prepass)
        {
@@ -9674,7 +9847,7 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface
                        {
                                // render water or distortion background
                                GL_DepthMask(true);
-                               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BACKGROUND, end-start, texturesurfacelist + start, (void *)(r_waterstate.waterplanes + startplaneindex), false);
+                               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BACKGROUND, end-start, texturesurfacelist + start, (void *)(r_fb.water.waterplanes + startplaneindex), false);
                                RSurf_DrawBatch();
                                // blend surface on top
                                GL_DepthMask(false);
@@ -9685,7 +9858,7 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface
                        {
                                // render surface with reflection texture as input
                                GL_DepthMask(writedepth && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED));
-                               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE, end-start, texturesurfacelist + start, (void *)(r_waterstate.waterplanes + startplaneindex), false);
+                               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE, end-start, texturesurfacelist + start, (void *)(r_fb.water.waterplanes + startplaneindex), false);
                                RSurf_DrawBatch();
                        }
                }
@@ -9917,7 +10090,7 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const
        float c[4];
 
 //     R_Mesh_ResetTextureState();
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
+       R_SetupShader_Generic_NoTexture(false, false);
 
        if(rsurface.texture && rsurface.texture->currentskinframe)
        {
@@ -10023,7 +10196,7 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const
                RSurf_DrawBatch_GL11_ClampColor();
 
                R_Mesh_PrepareVertices_Generic_Arrays(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.passcolor4f, NULL);
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
+               R_SetupShader_Generic_NoTexture(false, false);
                RSurf_DrawBatch();
        }
        else if (!r_refdef.view.showdebug)
@@ -10311,7 +10484,7 @@ static void R_DrawTextureSurfaceList_DepthOnly(int texturenumsurfaces, const msu
 {
        if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_BLENDED | MATERIALFLAG_ALPHATEST)))
                return;
-       if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)))
+       if (r_fb.water.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)))
                return;
        RSurf_SetupDepthAndCulling();
        RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, texturenumsurfaces, texturesurfacelist);
@@ -10538,7 +10711,7 @@ void R_DrawLoc_Callback(const entity_render_t *ent, const rtlight_t *rtlight, in
                        vertex3f[i] = mins[j] + size[j] * locboxvertex3f[i];
 
        R_Mesh_PrepareVertices_Generic_Arrays(6*4, vertex3f, NULL, NULL);
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
+       R_SetupShader_Generic_NoTexture(false, false);
        R_Mesh_Draw(0, 6*4, 0, 6*2, NULL, NULL, 0, locboxelements, NULL, 0);
 }
 
@@ -11165,7 +11338,7 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
                GL_DepthTest(true);
                GL_CullFace(GL_NONE);
                GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
-               R_SetupShader_Generic(decalskinframe->base, NULL, GL_MODULATE, 1, false);
+               R_SetupShader_Generic(decalskinframe->base, NULL, GL_MODULATE, 1, false, false, false);
                R_Mesh_Draw(0, numtris * 3, 0, numtris, decalsystem->element3i, NULL, 0, decalsystem->element3s, NULL, 0);
        }
 }
@@ -11222,7 +11395,7 @@ void R_DrawDebugModel(void)
        {
                float c = r_refdef.view.colorscale * r_showoverdraw.value * 0.125f;
                flagsmask = MATERIALFLAG_SKY | MATERIALFLAG_WALL;
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
+               R_SetupShader_Generic_NoTexture(false, false);
                GL_DepthTest(false);
                GL_DepthMask(false);
                GL_DepthRange(0, 1);
@@ -11252,7 +11425,7 @@ void R_DrawDebugModel(void)
        flagsmask = MATERIALFLAG_SKY | MATERIALFLAG_WALL;
 
 //     R_Mesh_ResetTextureState();
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
+       R_SetupShader_Generic_NoTexture(false, false);
        GL_DepthRange(0, 1);
        GL_DepthTest(!r_showdisabledepthtest.integer);
        GL_DepthMask(false);
@@ -11308,6 +11481,7 @@ void R_DrawDebugModel(void)
 
        GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
 
+#ifndef USE_GLES2
        if (r_showtris.integer && qglPolygonMode)
        {
                if (r_showdisabledepthtest.integer)
@@ -11418,6 +11592,7 @@ void R_DrawDebugModel(void)
                }
                rsurface.texture = NULL;
        }
+#endif
 }
 
 extern void R_BuildLightMap(const entity_render_t *ent, msurface_t *surface);