]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
added simple affine check to accelerate texture fetches on 2D art
[xonotic/darkplaces.git] / gl_rmain.c
index dbbb262564a14891956b1e3e80c2a7ae174fe9b7..7f998d21a7db8d087192f88cf27bf53144a91d7e 100644 (file)
@@ -115,6 +115,7 @@ cvar_t r_polygonoffset_submodel_offset = {0, "r_polygonoffset_submodel_offset",
 cvar_t r_polygonoffset_decals_factor = {0, "r_polygonoffset_decals_factor", "0", "biases depth values of decals to prevent z-fighting artifacts"};
 cvar_t r_polygonoffset_decals_offset = {0, "r_polygonoffset_decals_offset", "-14", "biases depth values of decals to prevent z-fighting artifacts"};
 cvar_t r_fog_exp2 = {0, "r_fog_exp2", "0", "uses GL_EXP2 fog (as in Nehahra) rather than realistic GL_EXP fog"};
+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"};
 
@@ -140,6 +141,15 @@ cvar_t r_textureunits = {0, "r_textureunits", "32", "number of texture units to
 static cvar_t gl_combine = {CVAR_READONLY, "gl_combine", "1", "indicates whether the OpenGL 1.3 rendering path is active"};
 static cvar_t r_glsl = {CVAR_READONLY, "r_glsl", "1", "indicates whether the OpenGL 2.0 rendering path is active"};
 
+cvar_t r_viewfbo = {CVAR_SAVE, "r_viewfbo", "0", "enables use of an 8bit (1) or 16bit (2) or 32bit (3) per component float framebuffer render, which may be at a different resolution than the video mode"};
+cvar_t r_viewscale = {CVAR_SAVE, "r_viewscale", "1", "scaling factor for resolution of the fbo rendering method, must be > 0, can be above 1 for a costly antialiasing behavior, typical values are 0.5 for 1/4th as many pixels rendered, or 1 for normal rendering"};
+cvar_t r_viewscale_fpsscaling = {CVAR_SAVE, "r_viewscale_fpsscaling", "0", "change resolution based on framerate"};
+cvar_t r_viewscale_fpsscaling_min = {CVAR_SAVE, "r_viewscale_fpsscaling_min", "0.0625", "worst acceptable quality"};
+cvar_t r_viewscale_fpsscaling_multiply = {CVAR_SAVE, "r_viewscale_fpsscaling_multiply", "5", "adjust quality up or down by the frametime difference from 1.0/target, multiplied by this factor"};
+cvar_t r_viewscale_fpsscaling_stepsize = {CVAR_SAVE, "r_viewscale_fpsscaling_stepsize", "0.01", "smallest adjustment to hit the target framerate (this value prevents minute oscillations)"};
+cvar_t r_viewscale_fpsscaling_stepmax = {CVAR_SAVE, "r_viewscale_fpsscaling_stepmax", "1.00", "largest adjustment to hit the target framerate (this value prevents wild overshooting of the estimate)"};
+cvar_t r_viewscale_fpsscaling_target = {CVAR_SAVE, "r_viewscale_fpsscaling_target", "70", "desired framerate"};
+
 cvar_t r_glsl_deluxemapping = {CVAR_SAVE, "r_glsl_deluxemapping", "1", "use per pixel lighting on deluxemap-compiled q3bsp maps (or a value of 2 forces deluxemap shading even without deluxemaps)"};
 cvar_t r_glsl_offsetmapping = {CVAR_SAVE, "r_glsl_offsetmapping", "0", "offset mapping effect (also known as parallax mapping or virtual displacement mapping)"};
 cvar_t r_glsl_offsetmapping_steps = {CVAR_SAVE, "r_glsl_offsetmapping_steps", "2", "offset mapping steps (note: too high values may be not supported by your GPU)"};
@@ -206,6 +216,8 @@ cvar_t r_overheadsprites_scaley = {CVAR_SAVE, "r_overheadsprites_scaley", "1", "
 cvar_t r_glsl_saturation = {CVAR_SAVE, "r_glsl_saturation", "1", "saturation multiplier (only working in glsl!)"};
 cvar_t r_glsl_saturation_redcompensate = {CVAR_SAVE, "r_glsl_saturation_redcompensate", "0", "a 'vampire sight' addition to desaturation effect, does compensation for red color, r_glsl_restart is required"};
 
+cvar_t r_glsl_vertextextureblend_usebothalphas = {CVAR_SAVE, "r_glsl_vertextextureblend_usebothalphas", "0", "use both alpha layers on vertex blended surfaces, each alpha layer sets amount of 'blend leak' on another layer."};
+
 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;
@@ -219,6 +231,13 @@ static struct r_bloomstate_s
 
        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!
 
@@ -656,6 +675,7 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] =
        {"#define USEREFLECTCUBE\n", " reflectcube"},
        {"#define USENORMALMAPSCROLLBLEND\n", " normalmapscrollblend"},
        {"#define USEBOUNCEGRID\n", " bouncegrid"},
+       {"#define USEBOUNCEGRIDDIRECTIONAL\n", " bouncegriddirectional"},
 };
 
 // NOTE: MUST MATCH ORDER OF SHADERMODE_* ENUMS!
@@ -843,9 +863,10 @@ enum
        SHADERSTATICPARM_POSTPROCESS_USERVEC1 = 2, ///< postprocess uservec1 is enabled
        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_POSTPROCESS_USERVEC4 = 5,  ///< postprocess uservec4 is enabled
+       SHADERSTATICPARM_VERTEXTEXTUREBLEND_USEBOTHALPHAS = 6 // use both alpha layers while blending materials, allows more advanced microblending
 };
-#define SHADERSTATICPARMS_COUNT 6
+#define SHADERSTATICPARMS_COUNT 7
 
 static const char *shaderstaticparmstrings_list[SHADERSTATICPARMS_COUNT];
 static int shaderstaticparms_count = 0;
@@ -861,6 +882,8 @@ qboolean R_CompileShader_CheckStaticParms(void)
        // detect all
        if (r_glsl_saturation_redcompensate.integer)
                R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_SATURATION_REDCOMPENSATE);
+       if (r_glsl_vertextextureblend_usebothalphas.integer)
+               R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_VERTEXTEXTUREBLEND_USEBOTHALPHAS);
        if (r_shadow_glossexact.integer)
                R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_EXACTSPECULARMATH);
        if (r_glsl_postprocess.integer)
@@ -893,6 +916,7 @@ void R_CompileShader_AddStaticParms(unsigned int mode, unsigned int permutation)
        R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_POSTPROCESS_USERVEC2, "USERVEC2");
        R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_POSTPROCESS_USERVEC3, "USERVEC3");
        R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_POSTPROCESS_USERVEC4, "USERVEC4");
+       R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_VERTEXTEXTUREBLEND_USEBOTHALPHAS, "USEBOTHALPHAS");
 }
 
 /// information about each possible shader permutation
@@ -988,6 +1012,9 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
                vertstrings_list[vertstrings_count++] = "#version 130\n";
                geomstrings_list[geomstrings_count++] = "#version 130\n";
                fragstrings_list[fragstrings_count++] = "#version 130\n";
+               vertstrings_list[vertstrings_count++] = "#define GLSL130\n";
+               geomstrings_list[geomstrings_count++] = "#define GLSL130\n";
+               fragstrings_list[fragstrings_count++] = "#define GLSL130\n";
        }
 
        // the first pretext is which type of shader to compile as
@@ -1761,7 +1788,7 @@ void R_SetupShader_SetPermutationHLSL(unsigned int mode, unsigned int permutatio
 
 void R_SetupShader_SetPermutationSoft(unsigned int mode, unsigned int permutation)
 {
-       DPSOFTRAST_SetShader(mode, permutation);
+       DPSOFTRAST_SetShader(mode, permutation, r_shadow_glossexact.integer);
        DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ModelViewProjectionMatrixM1, 1, false, gl_modelviewprojection16f);
        DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ModelViewMatrixM1, 1, false, gl_modelview16f);
        DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_ClientTime, cl.time);
@@ -2061,14 +2088,14 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        r_waterstate_waterplane_t *waterplane = (r_waterstate_waterplane_t *)surfacewaterplane;
        if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
                permutation |= SHADERPERMUTATION_ALPHAKILL;
+       if (rsurface.texture->r_water_waterscroll[0] && rsurface.texture->r_water_waterscroll[1])
+               permutation |= SHADERPERMUTATION_NORMALMAPSCROLLBLEND; // todo: make generic
        if (rsurfacepass == RSURFPASS_BACKGROUND)
        {
                // distorted background
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_WATERSHADER)
                {
                        mode = SHADERMODE_WATER;
-                       if (rsurface.texture->r_water_waterscroll[0] && rsurface.texture->r_water_waterscroll[1])
-                               permutation |= SHADERPERMUTATION_NORMALMAPSCROLLBLEND;
                        if((r_wateralpha.value < 1) && (rsurface.texture->currentmaterialflags & MATERIALFLAG_WATERALPHA))
                        {
                                // this is the right thing to do for wateralpha
@@ -2261,7 +2288,11 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                if (rsurface.texture->reflectmasktexture)
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
                if (r_shadow_bouncegridtexture)
+               {
                        permutation |= SHADERPERMUTATION_BOUNCEGRID;
+                       if (r_shadow_bouncegriddirectional)
+                               permutation |= SHADERPERMUTATION_BOUNCEGRIDDIRECTIONAL;
+               }
                GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
                blendfuncflags = R_BlendFuncFlags(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
        }
@@ -2309,7 +2340,11 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                if (rsurface.texture->reflectmasktexture)
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
                if (r_shadow_bouncegridtexture)
+               {
                        permutation |= SHADERPERMUTATION_BOUNCEGRID;
+                       if (r_shadow_bouncegriddirectional)
+                               permutation |= SHADERPERMUTATION_BOUNCEGRIDDIRECTIONAL;
+               }
                GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
                blendfuncflags = R_BlendFuncFlags(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
        }
@@ -2393,7 +2428,11 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        mode = SHADERMODE_VERTEXCOLOR;
                }
                if (r_shadow_bouncegridtexture)
+               {
                        permutation |= SHADERPERMUTATION_BOUNCEGRID;
+                       if (r_shadow_bouncegriddirectional)
+                               permutation |= SHADERPERMUTATION_BOUNCEGRIDDIRECTIONAL;
+               }
                GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
                blendfuncflags = R_BlendFuncFlags(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
        }
@@ -2657,7 +2696,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                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);}
-               if (r_glsl_permutation->loc_BounceGridIntensity >= 0) qglUniform1f(r_glsl_permutation->loc_BounceGridIntensity, r_shadow_bouncegridintensity);
+               if (r_glsl_permutation->loc_BounceGridIntensity >= 0) qglUniform1f(r_glsl_permutation->loc_BounceGridIntensity, r_shadow_bouncegridintensity*r_refdef.view.colorscale);
 
                if (r_glsl_permutation->tex_Texture_First           >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First            , r_texture_white                                     );
                if (r_glsl_permutation->tex_Texture_Second          >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second           , r_texture_white                                     );
@@ -2865,7 +2904,7 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight)
                permutation |= SHADERPERMUTATION_CUBEFILTER;
        if (diffusescale > 0)
                permutation |= SHADERPERMUTATION_DIFFUSE;
-       if (specularscale > 0)
+       if (specularscale > 0 && r_shadow_gloss.integer > 0)
                permutation |= SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_DIFFUSE;
        if (r_shadow_usingshadowmap2d)
        {
@@ -3801,6 +3840,7 @@ void gl_main_start(void)
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
        case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
                Cvar_SetValueQuick(&r_textureunits, vid.texunits);
                Cvar_SetValueQuick(&gl_combine, 1);
                Cvar_SetValueQuick(&r_glsl, 1);
@@ -3824,14 +3864,6 @@ void gl_main_start(void)
                r_loadgloss = false;
                r_loadfog = true;
                break;
-       case RENDERPATH_GLES2:
-               Cvar_SetValueQuick(&r_textureunits, 1);
-               Cvar_SetValueQuick(&gl_combine, 1);
-               Cvar_SetValueQuick(&r_glsl, 1);
-               r_loadnormalmap = true;
-               r_loadgloss = false;
-               r_loadfog = false;
-               break;
        }
 
        R_AnimCache_Free();
@@ -3844,6 +3876,9 @@ void gl_main_start(void)
        r_qwskincache = NULL;
        r_qwskincache_size = 0;
 
+       // due to caching of texture_t references, the collision cache must be reset
+       Collision_Cache_Reset(true);
+
        // set up r_skinframe loading system for textures
        memset(&r_skinframe, 0, sizeof(r_skinframe));
        r_skinframe.loadsequence = 1;
@@ -4053,6 +4088,7 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_polygonoffset_decals_factor);
        Cvar_RegisterVariable(&r_polygonoffset_decals_offset);
        Cvar_RegisterVariable(&r_fog_exp2);
+       Cvar_RegisterVariable(&r_fog_clear);
        Cvar_RegisterVariable(&r_drawfog);
        Cvar_RegisterVariable(&r_transparentdepthmasking);
        Cvar_RegisterVariable(&r_texture_dds_load);
@@ -4064,6 +4100,14 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_texture_convertsRGB_particles);
        Cvar_RegisterVariable(&r_textureunits);
        Cvar_RegisterVariable(&gl_combine);
+       Cvar_RegisterVariable(&r_viewfbo);
+       Cvar_RegisterVariable(&r_viewscale);
+       Cvar_RegisterVariable(&r_viewscale_fpsscaling);
+       Cvar_RegisterVariable(&r_viewscale_fpsscaling_min);
+       Cvar_RegisterVariable(&r_viewscale_fpsscaling_multiply);
+       Cvar_RegisterVariable(&r_viewscale_fpsscaling_stepsize);
+       Cvar_RegisterVariable(&r_viewscale_fpsscaling_stepmax);
+       Cvar_RegisterVariable(&r_viewscale_fpsscaling_target);
        Cvar_RegisterVariable(&r_glsl);
        Cvar_RegisterVariable(&r_glsl_deluxemapping);
        Cvar_RegisterVariable(&r_glsl_offsetmapping);
@@ -4114,6 +4158,7 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_test);
        Cvar_RegisterVariable(&r_glsl_saturation);
        Cvar_RegisterVariable(&r_glsl_saturation_redcompensate);
+       Cvar_RegisterVariable(&r_glsl_vertextextureblend_usebothalphas);
        Cvar_RegisterVariable(&r_framedatasize);
        if (gamemode == GAME_NEHAHRA || gamemode == GAME_TENEBRAE)
                Cvar_SetValue("r_fullbrights", 0);
@@ -4586,7 +4631,7 @@ static void R_View_UpdateEntityLighting (void)
                        {
                                if (ent->model->sprite.sprnum_type == SPR_OVERHEAD) // apply offset for overhead sprites
                                        org[2] = org[2] + r_overheadsprites_pushback.value;
-                               R_CompleteLightPoint(ent->modellight_ambient, ent->modellight_diffuse, tempdiffusenormal, org, LP_LIGHTMAP | LP_RTWORLD | LP_DYNLIGHT);
+                               R_LightPoint(ent->modellight_ambient, org, LP_LIGHTMAP | LP_RTWORLD | LP_DYNLIGHT);
                        }
                        else
                                R_CompleteLightPoint(ent->modellight_ambient, ent->modellight_diffuse, tempdiffusenormal, org, LP_LIGHTMAP);
@@ -4878,11 +4923,11 @@ static void R_View_SetFrustum(const int *scissor)
                        case RENDERPATH_D3D9:
                        case RENDERPATH_D3D10:
                        case RENDERPATH_D3D11:
-                       case RENDERPATH_SOFT:
                                // non-flipped y coordinates
                                fny = -1.0 + 2.0 * (vid.height - scissor[1] - scissor[3] - r_refdef.view.viewport.y) / (double) (r_refdef.view.viewport.height);
                                fpy = -1.0 + 2.0 * (vid.height - scissor[1]              - r_refdef.view.viewport.y) / (double) (r_refdef.view.viewport.height);
                                break;
+                       case RENDERPATH_SOFT:
                        case RENDERPATH_GL11:
                        case RENDERPATH_GL13:
                        case RENDERPATH_GL20:
@@ -5080,10 +5125,29 @@ void R_View_Update(void)
        R_View_UpdateEntityLighting();
 }
 
+float viewscalefpsadjusted = 1.0f;
+
+void R_GetScaledViewSize(int width, int height, int *outwidth, int *outheight)
+{
+       float scale = r_viewscale.value * sqrt(viewscalefpsadjusted);
+       scale = bound(0.03125f, scale, 1.0f);
+       *outwidth = (int)ceil(width * scale);
+       *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)
 {
        const float *customclipplane = NULL;
        float plane[4];
+       int scaledwidth, scaledheight;
        if (r_refdef.view.useclipplane && allowwaterclippingplane)
        {
                // LordHavoc: couldn't figure out how to make this approach the
@@ -5094,17 +5158,29 @@ void R_SetupView(qboolean allowwaterclippingplane)
                plane[0] = r_refdef.view.clipplane.normal[0];
                plane[1] = r_refdef.view.clipplane.normal[1];
                plane[2] = r_refdef.view.clipplane.normal[2];
-               plane[3] = dist;
-               customclipplane = plane;
+               plane[3] = -dist;
+               if(vid.renderpath != RENDERPATH_SOFT) customclipplane = plane;
        }
 
+       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 - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height, -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, 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);
        else if (vid.stencil && r_useinfinitefarclip.integer)
-               R_Viewport_InitPerspectiveInfinite(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, vid.height - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height, 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, vid.height - 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 - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, r_refdef.farclip, customclipplane);
+               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_SetViewport(&r_refdef.view.viewport);
+       if (r_refdef.view.useclipplane && allowwaterclippingplane && vid.renderpath == RENDERPATH_SOFT)
+       {
+               matrix4x4_t mvpmatrix, invmvpmatrix, invtransmvpmatrix;
+               float screenplane[4];
+               Matrix4x4_Concat(&mvpmatrix, &r_refdef.view.viewport.projectmatrix, &r_refdef.view.viewport.viewmatrix);
+               Matrix4x4_Invert_Full(&invmvpmatrix, &mvpmatrix);
+               Matrix4x4_Transpose(&invtransmvpmatrix, &invmvpmatrix);
+               Matrix4x4_Transform4(&invtransmvpmatrix, plane, screenplane);
+               DPSOFTRAST_ClipPlane(screenplane[0], screenplane[1], screenplane[2], screenplane[3]);
+       }
 }
 
 void R_EntityMatrix(const matrix4x4_t *matrix)
@@ -5156,6 +5232,7 @@ void R_ResetViewRendering2D(void)
 
        // 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_SetViewport(&viewport);
        GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height);
        GL_Color(1, 1, 1, 1);
@@ -5598,6 +5675,7 @@ static void R_Water_ProcessPlanes(void)
                }
 
        }
+       DPSOFTRAST_ClipPlane(0, 0, 0, 1);
        r_waterstate.renderingscene = false;
        r_refdef.view = originalview;
        R_ResetViewRendering3D();
@@ -5615,6 +5693,27 @@ error:
 void R_Bloom_StartFrame(void)
 {
        int bloomtexturewidth, bloomtextureheight, screentexturewidth, screentextureheight;
+       int viewwidth, viewheight;
+       textype_t textype;
+
+       if (r_viewscale_fpsscaling.integer)
+       {
+               double actualframetime;
+               double targetframetime;
+               double adjust;
+               actualframetime = r_refdef.lastdrawscreentime;
+               targetframetime = (1.0 / r_viewscale_fpsscaling_target.value);
+               adjust = (targetframetime - actualframetime) * r_viewscale_fpsscaling_multiply.value;
+               adjust = bound(-r_viewscale_fpsscaling_stepmax.value, adjust, r_viewscale_fpsscaling_stepmax.value);
+               if (r_viewscale_fpsscaling_stepsize.value > 0)
+                       adjust = (int)(adjust / r_viewscale_fpsscaling_stepsize.value) * r_viewscale_fpsscaling_stepsize.value;
+               viewscalefpsadjusted += adjust;
+               viewscalefpsadjusted = bound(r_viewscale_fpsscaling_min.value, viewscalefpsadjusted, 1.0f);
+       }
+       else
+               viewscalefpsadjusted = 1.0f;
+
+       R_GetScaledViewSize(r_refdef.view.width, r_refdef.view.height, &viewwidth, &viewheight);
 
        switch(vid.renderpath)
        {
@@ -5641,8 +5740,8 @@ void R_Bloom_StartFrame(void)
        // calculate desired texture sizes
        if (vid.support.arb_texture_non_power_of_two)
        {
-               screentexturewidth = r_refdef.view.width;
-               screentextureheight = r_refdef.view.height;
+               screentexturewidth = vid.width;
+               screentextureheight = vid.height;
                bloomtexturewidth = r_bloomstate.bloomwidth;
                bloomtextureheight = r_bloomstate.bloomheight;
        }
@@ -5662,31 +5761,82 @@ void R_Bloom_StartFrame(void)
                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)))
+       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)
                screentexturewidth = screentextureheight = 0;
        if (!r_hdr.integer && !r_bloom.integer)
                bloomtexturewidth = bloomtextureheight = 0;
 
+       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_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               break;
+       }
+
        // allocate textures as needed
-       if (r_bloomstate.screentexturewidth != screentexturewidth || r_bloomstate.screentextureheight != screentextureheight)
+       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_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCENEAREST | TEXF_CLAMP, -1, NULL);
-       }
-       if (r_bloomstate.bloomtexturewidth != bloomtexturewidth || r_bloomstate.bloomtextureheight != bloomtextureheight)
-       {
-               if (r_bloomstate.texture_bloom)
-                       R_FreeTexture(r_bloomstate.texture_bloom);
-               r_bloomstate.texture_bloom = NULL;
+                       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)
+                       {
+                               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_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_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
+                       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
@@ -5699,10 +5849,10 @@ void R_Bloom_StartFrame(void)
        // 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)r_refdef.view.height    / (float)r_bloomstate.screentextureheight;
-       r_bloomstate.screentexcoord2f[2] = (float)r_refdef.view.width     / (float)r_bloomstate.screentexturewidth;
-       r_bloomstate.screentexcoord2f[3] = (float)r_refdef.view.height    / (float)r_bloomstate.screentextureheight;
-       r_bloomstate.screentexcoord2f[4] = (float)r_refdef.view.width     / (float)r_bloomstate.screentexturewidth;
+       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;
@@ -5742,13 +5892,16 @@ void R_Bloom_StartFrame(void)
                break;
        }
 
-       if (r_hdr.integer || r_bloom.integer)
+       if ((r_hdr.integer || r_bloom.integer) && r_bloomstate.bloomwidth)
        {
                r_bloomstate.enabled = true;
-               r_bloomstate.hdr = r_hdr.integer != 0;
+               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);
+
+       if (r_bloomstate.fbo_framebuffer)
+               r_refdef.view.clear = true;
 }
 
 void R_Bloom_CopyBloomTexture(float colorscale)
@@ -5757,6 +5910,7 @@ void R_Bloom_CopyBloomTexture(float colorscale)
 
        // scale down screen texture to the bloom texture size
        CHECKGLERROR
+       R_Mesh_SetMainRenderTargets();
        R_SetViewport(&r_bloomstate.viewport);
        GL_BlendFunc(GL_ONE, GL_ZERO);
        GL_Color(colorscale, colorscale, colorscale, 1);
@@ -5824,7 +5978,7 @@ void R_Bloom_MakeTexture(void)
 
        range = r_bloom_blur.integer * r_bloomstate.bloomwidth / 320;
        brighten = r_bloom_brighten.value;
-       if (r_hdr.integer)
+       if (r_bloomstate.hdr)
                brighten *= r_hdr_range.value;
        brighten = sqrt(brighten);
        if(range >= 1)
@@ -5959,6 +6113,7 @@ static void R_BlendView(void)
                        if (r_bloom_blur.value < 1) { Cvar_SetValueQuick(&r_bloom_blur, 1); }
 
                        R_ResetViewRendering2D();
+                       R_Mesh_SetMainRenderTargets();
 
                        if(!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0))
                        {
@@ -6124,7 +6279,7 @@ static void R_BlendView(void)
                        break;
                }
                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;
+               r_refdef.stats.bloom_drawpixels += r_refdef.view.width * r_refdef.view.height;
                break;
        case RENDERPATH_GL13:
        case RENDERPATH_GL11:
@@ -6429,7 +6584,7 @@ void R_RenderView(void)
        r_refdef.view.clear = true;
 
        // this produces a bloom texture to be used in R_BlendView() later
-       if (r_hdr.integer && r_bloomstate.bloomwidth)
+       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
@@ -9178,6 +9333,12 @@ static void RSurf_DrawBatch_GL11_MakeFogColor(float r, float g, float b, float a
        float f;
        const float *v;
        float *c;
+
+       // fake shading
+       rsurface.passcolor4f = (float *)R_FrameData_Alloc(rsurface.batchnumvertices * sizeof(float[4]));
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
+
        for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, c += 4)
        {
                f = 1 - RSurf_FogVertex(v);
@@ -10269,11 +10430,23 @@ static void R_DecalSystem_SplatTriangle(decalsystem_t *decalsystem, float r, flo
        vertex3f = rsurface.modelvertex3f;
        normal3f = rsurface.modelnormal3f;
 
-       for (cornerindex = 0;cornerindex < 3;cornerindex++)
+       if (normal3f)
+       {
+               for (cornerindex = 0;cornerindex < 3;cornerindex++)
+               {
+                       index = 3*e[cornerindex];
+                       VectorMA(vertex3f + index, cl_decals_bias.value, normal3f + index, v[cornerindex]);
+               }
+       }
+       else
        {
-               index = 3*e[cornerindex];
-               VectorMA(vertex3f + index, cl_decals_bias.value, normal3f + index, v[cornerindex]);
+               for (cornerindex = 0;cornerindex < 3;cornerindex++)
+               {
+                       index = 3*e[cornerindex];
+                       VectorCopy(vertex3f + index, v[cornerindex]);
+               }
        }
+
        // cull backfaces
        //TriangleNormal(v[0], v[1], v[2], normal);
        //if (DotProduct(normal, localnormal) < 0.0f)