]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
physics: fix and refactor unsticking
[xonotic/darkplaces.git] / gl_rmain.c
index 4a33fd9fe4145d17db15ee172a590071e0adb581..26c90911d26ba016dd676a6a4ba83fe995ab06cc 100644 (file)
@@ -82,7 +82,7 @@ cvar_t r_transparent_useplanardistance = {CF_CLIENT, "r_transparent_useplanardis
 cvar_t r_showoverdraw = {CF_CLIENT, "r_showoverdraw", "0", "shows overlapping geometry"};
 cvar_t r_showbboxes = {CF_CLIENT, "r_showbboxes", "0", "shows bounding boxes of server entities, value controls opacity scaling (1 = 10%,  10 = 100%)"};
 cvar_t r_showbboxes_client = {CF_CLIENT, "r_showbboxes_client", "0", "shows bounding boxes of clientside qc entities, value controls opacity scaling (1 = 10%,  10 = 100%)"};
-cvar_t r_showsurfaces = {CF_CLIENT, "r_showsurfaces", "0", "1 shows surfaces as different colors, or a value of 2 shows triangle draw order (for analyzing whether meshes are optimized for vertex cache)"};
+cvar_t r_showsurfaces = {CF_CLIENT, "r_showsurfaces", "0", "1 shows surfaces as different colors, or a value of 3 shows an approximation to vertex or object color (for a very approximate view of the game)"};
 cvar_t r_showtris = {CF_CLIENT, "r_showtris", "0", "shows triangle outlines, value controls brightness (can be above 1)"};
 cvar_t r_shownormals = {CF_CLIENT, "r_shownormals", "0", "shows per-vertex surface normals and tangent vectors for bumpmapped lighting"};
 cvar_t r_showlighting = {CF_CLIENT, "r_showlighting", "0", "shows areas lit by lights, useful for finding out why some areas of a map render slowly (bright orange = lots of passes = slow), a value of 2 disables depth testing which can be interesting but not very useful"};
@@ -156,12 +156,8 @@ cvar_t gl_skyclip = {CF_CLIENT, "gl_skyclip", "4608", "nehahra farclip distance
 cvar_t r_texture_dds_load = {CF_CLIENT | CF_ARCHIVE, "r_texture_dds_load", "0", "load compressed dds/filename.dds texture instead of filename.tga, if the file exists (requires driver support)"};
 cvar_t r_texture_dds_save = {CF_CLIENT | CF_ARCHIVE, "r_texture_dds_save", "0", "save compressed dds/filename.dds texture when filename.tga is loaded, so that it can be loaded instead next time"};
 
-cvar_t r_textureunits = {CF_CLIENT, "r_textureunits", "32", "number of texture units to use in GL 1.1 and GL 1.3 rendering paths"};
-static cvar_t gl_combine = {CF_CLIENT | CF_READONLY, "gl_combine", "1", "indicates whether the OpenGL 1.3 rendering path is active"};
-static cvar_t r_glsl = {CF_CLIENT | CF_READONLY, "r_glsl", "1", "indicates whether the OpenGL 2.0 rendering path is active"};
-
 cvar_t r_usedepthtextures = {CF_CLIENT | CF_ARCHIVE, "r_usedepthtextures", "1", "use depth texture instead of depth renderbuffer where possible, uses less video memory but may render slower (or faster) depending on hardware"};
-cvar_t r_viewfbo = {CF_CLIENT | CF_ARCHIVE, "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_viewfbo = {CF_CLIENT | CF_ARCHIVE, "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; the default setting of 0 uses a framebuffer render when required, and renders directly to the screen otherwise"};
 cvar_t r_rendertarget_debug = {CF_CLIENT, "r_rendertarget_debug", "-1", "replaces the view with the contents of the specified render target (by number - note that these can fluctuate depending on scene)"};
 cvar_t r_viewscale = {CF_CLIENT | CF_ARCHIVE, "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 = {CF_CLIENT | CF_ARCHIVE, "r_viewscale_fpsscaling", "0", "change resolution based on framerate"};
@@ -191,6 +187,7 @@ cvar_t r_glsl_postprocess_uservec2_enable = {CF_CLIENT | CF_ARCHIVE, "r_glsl_pos
 cvar_t r_glsl_postprocess_uservec3_enable = {CF_CLIENT | CF_ARCHIVE, "r_glsl_postprocess_uservec3_enable", "1", "enables postprocessing uservec3 usage, creates USERVEC1 define (only useful if default.glsl has been customized)"};
 cvar_t r_glsl_postprocess_uservec4_enable = {CF_CLIENT | CF_ARCHIVE, "r_glsl_postprocess_uservec4_enable", "1", "enables postprocessing uservec4 usage, creates USERVEC1 define (only useful if default.glsl has been customized)"};
 cvar_t r_colorfringe = {CF_CLIENT | CF_ARCHIVE, "r_colorfringe", "0", "Chromatic aberration. Values higher than 0.025 will noticeably distort the image"};
+cvar_t r_fxaa = {CF_CLIENT | CF_ARCHIVE, "r_fxaa", "0", "fast approximate anti aliasing"};
 
 cvar_t r_water = {CF_CLIENT | CF_ARCHIVE, "r_water", "0", "whether to use reflections and refraction on water surfaces (note: r_wateralpha must be set below 1)"};
 cvar_t r_water_cameraentitiesonly = {CF_CLIENT | CF_ARCHIVE, "r_water_cameraentitiesonly", "0", "whether to only show QC-defined reflections/refractions (typically used for camera- or portal-like effects)"};
@@ -247,6 +244,7 @@ cvar_t r_glsl_saturation_redcompensate = {CF_CLIENT | CF_ARCHIVE, "r_glsl_satura
 
 cvar_t r_glsl_vertextextureblend_usebothalphas = {CF_CLIENT | CF_ARCHIVE, "r_glsl_vertextextureblend_usebothalphas", "0", "use both alpha layers on vertex blended surfaces, each alpha layer sets amount of 'blend leak' on another layer, requires mod_q3shader_force_terrain_alphaflag on."};
 
+// FIXME: This cvar would grow to a ridiculous size after several launches and clean exits when used during surface sorting.
 cvar_t r_framedatasize = {CF_CLIENT | CF_ARCHIVE, "r_framedatasize", "0.5", "size of renderer data cache used during one frame (for skeletal animation caching, light processing, etc)"};
 cvar_t r_buffermegs[R_BUFFERDATA_COUNT] =
 {
@@ -256,6 +254,10 @@ cvar_t r_buffermegs[R_BUFFERDATA_COUNT] =
        {CF_CLIENT | CF_ARCHIVE, "r_buffermegs_uniform", "0.25", "uniform buffer size for one frame"},
 };
 
+cvar_t r_q1bsp_lightmap_updates_enabled = {CF_CLIENT, "r_q1bsp_lightmap_updates_enabled", "1", "allow lightmaps to be updated on Q1BSP maps (don't turn this off except for debugging)"};
+cvar_t r_q1bsp_lightmap_updates_combine = {CF_CLIENT | CF_ARCHIVE, "r_q1bsp_lightmap_updates_combine", "2", "combine lightmap texture updates to make fewer glTexSubImage2D calls, modes: 0 = immediately upload lightmaps (may be thousands of small 3x3 updates), 1 = combine to one call, 2 = combine to one full texture update (glTexImage2D) which tells the driver it does not need to lock the resource (faster on most drivers)"};
+cvar_t r_q1bsp_lightmap_updates_hidden_surfaces = {CF_CLIENT | CF_ARCHIVE, "r_q1bsp_lightmap_updates_hidden_surfaces", "0", "update lightmaps on surfaces that are not visible, so that updates only occur on frames where lightstyles changed value (animation or light switches), only makes sense with combine = 2"};
+
 extern cvar_t v_glslgamma_2d;
 
 extern qbool v_flipped_state;
@@ -511,8 +513,8 @@ static void R_BuildFogTexture(void)
        }
        if (r_texture_fogattenuation)
        {
-               R_UpdateTexture(r_texture_fogattenuation, &data1[0][0], 0, 0, 0, FOGWIDTH, 1, 1);
-               //R_UpdateTexture(r_texture_fogattenuation, &data2[0][0], 0, 0, 0, FOGWIDTH, 1, 1);
+               R_UpdateTexture(r_texture_fogattenuation, &data1[0][0], 0, 0, 0, FOGWIDTH, 1, 1, 0);
+               //R_UpdateTexture(r_texture_fogattenuation, &data2[0][0], 0, 0, 0, FOGWIDTH, 1, 1, 0);
        }
        else
        {
@@ -531,7 +533,7 @@ static void R_BuildFogHeightTexture(void)
        float c[4];
        float f;
        inpixels = NULL;
-       strlcpy(r_refdef.fogheighttexturename, r_refdef.fog_height_texturename, sizeof(r_refdef.fogheighttexturename));
+       dp_strlcpy(r_refdef.fogheighttexturename, r_refdef.fog_height_texturename, sizeof(r_refdef.fogheighttexturename));
        if (r_refdef.fogheighttexturename[0])
                inpixels = loadimagepixelsbgra(r_refdef.fogheighttexturename, true, false, false, NULL);
        if (!inpixels)
@@ -843,9 +845,10 @@ enum
        SHADERSTATICPARM_SHADOWSAMPLER = 10, ///< sampler
        SHADERSTATICPARM_CELSHADING = 11, ///< celshading (alternative diffuse and specular math)
        SHADERSTATICPARM_CELOUTLINES = 12, ///< celoutline (depth buffer analysis to produce outlines)
-       SHADERSTATICPARM_FXAA = 13 ///< fast approximate anti aliasing
+       SHADERSTATICPARM_FXAA = 13, ///< fast approximate anti aliasing
+       SHADERSTATICPARM_COLORFRINGE = 14 ///< colorfringe (chromatic aberration)
 };
-#define SHADERSTATICPARMS_COUNT 14
+#define SHADERSTATICPARMS_COUNT 15
 
 static const char *shaderstaticparmstrings_list[SHADERSTATICPARMS_COUNT];
 static int shaderstaticparms_count = 0;
@@ -894,6 +897,8 @@ qbool R_CompileShader_CheckStaticParms(void)
                R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_CELSHADING);
        if (r_celoutlines.integer)
                R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_CELOUTLINES);
+       if (r_colorfringe.value)
+               R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_COLORFRINGE);
 
        return memcmp(r_compileshader_staticparms, r_compileshader_staticparms_save, sizeof(r_compileshader_staticparms)) != 0;
 }
@@ -922,6 +927,7 @@ static void R_CompileShader_AddStaticParms(unsigned int mode, uint64_t permutati
        R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_CELSHADING, "USECELSHADING");
        R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_CELOUTLINES, "USECELOUTLINES");
        R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_FXAA, "USEFXAA");
+       R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_COLORFRINGE, "USECOLORFRINGE");
 }
 
 /// information about each possible shader permutation
@@ -1017,7 +1023,7 @@ static char *ShaderModeInfo_GetShaderText(shadermodeinfo_t *modeinfo, qbool prin
 
 static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode, uint64_t permutation)
 {
-       int i;
+       unsigned i;
        int ubibind;
        int sampler;
        shadermodeinfo_t *modeinfo = &shadermodeinfo[SHADERLANGUAGE_GLSL][mode];
@@ -1038,7 +1044,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
        permutationname[0] = 0;
        sourcestring = ShaderModeInfo_GetShaderText(modeinfo, true, false);
 
-       strlcat(permutationname, modeinfo->filename, sizeof(permutationname));
+       dp_strlcat(permutationname, modeinfo->filename, sizeof(permutationname));
 
        // we need 140 for r_glsl_skeletal (GL_ARB_uniform_buffer_object)
        if(vid.support.glshaderversion >= 140)
@@ -1092,7 +1098,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
        vertstrings_list[vertstrings_count++] = modeinfo->pretext;
        geomstrings_list[geomstrings_count++] = modeinfo->pretext;
        fragstrings_list[fragstrings_count++] = modeinfo->pretext;
-       strlcat(permutationname, modeinfo->name, sizeof(permutationname));
+       dp_strlcat(permutationname, modeinfo->name, sizeof(permutationname));
 
        // now add all the permutation pretexts
        for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
@@ -1102,7 +1108,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
                        vertstrings_list[vertstrings_count++] = shaderpermutationinfo[i].pretext;
                        geomstrings_list[geomstrings_count++] = shaderpermutationinfo[i].pretext;
                        fragstrings_list[fragstrings_count++] = shaderpermutationinfo[i].pretext;
-                       strlcat(permutationname, shaderpermutationinfo[i].name, sizeof(permutationname));
+                       dp_strlcat(permutationname, shaderpermutationinfo[i].name, sizeof(permutationname));
                }
                else
                {
@@ -1353,7 +1359,7 @@ static void R_SetupShader_SetPermutationGLSL(unsigned int mode, uint64_t permuta
                        if (!r_glsl_permutation->program)
                        {
                                // remove features until we find a valid permutation
-                               int i;
+                               unsigned i;
                                for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
                                {
                                        // reduce i more quickly whenever it would not remove any bits
@@ -1412,7 +1418,8 @@ void R_GLSL_Restart_f(cmd_state_t *cmd)
 
 static void R_GLSL_DumpShader_f(cmd_state_t *cmd)
 {
-       int i, language, mode, dupe;
+       unsigned i;
+       int language, mode, dupe;
        char *text;
        shadermodeinfo_t *modeinfo;
        qfile_t *file;
@@ -1553,7 +1560,7 @@ static int R_BlendFuncFlags(int src, int dst)
        return r;
 }
 
-void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdiffuse[3], const float rtlightspecular[3], rsurfacepass_t rsurfacepass, int texturenumsurfaces, const msurface_t **texturesurfacelist, void *surfacewaterplane, qbool notrippy)
+void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdiffuse[3], const float rtlightspecular[3], rsurfacepass_t rsurfacepass, int texturenumsurfaces, const msurface_t **texturesurfacelist, void *surfacewaterplane, qbool notrippy, qbool ui)
 {
        // select a permutation of the lighting shader appropriate to this
        // combination of texture, entity, light source, and fogging, only use the
@@ -1820,7 +1827,7 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif
                // lightmapped wall
                if ((t->glowtexture || t->backgroundglowtexture) && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer)
                        permutation |= SHADERPERMUTATION_GLOW;
-               if (r_refdef.fogenabled && !notrippy)
+               if (r_refdef.fogenabled && !ui)
                        permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE);
                if (t->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
@@ -1892,7 +1899,7 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif
        }
        if(!(blendfuncflags & BLENDFUNC_ALLOWS_ANYFOG))
                permutation &= ~(SHADERPERMUTATION_FOGHEIGHTTEXTURE | SHADERPERMUTATION_FOGOUTSIDE | SHADERPERMUTATION_FOGINSIDE);
-       if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACKALPHA && !notrippy)
+       if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACKALPHA && !ui)
                permutation |= SHADERPERMUTATION_FOGALPHAHACK;
        switch(vid.renderpath)
        {
@@ -1916,7 +1923,7 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif
                        if (r_glsl_permutation->loc_Color_Ambient >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Ambient, rtlightambient[0], rtlightambient[1], rtlightambient[2]);
                        if (r_glsl_permutation->loc_Color_Diffuse >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Diffuse, rtlightdiffuse[0], rtlightdiffuse[1], rtlightdiffuse[2]);
                        if (r_glsl_permutation->loc_Color_Specular >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Specular, rtlightspecular[0], rtlightspecular[1], rtlightspecular[2]);
-       
+
                        // 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);
@@ -1954,7 +1961,7 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif
                                if (r_glsl_permutation->loc_DeferredMod_Specular >= 0) qglUniform3f(r_glsl_permutation->loc_DeferredMod_Specular, t->render_rtlight_specular[0], t->render_rtlight_specular[1], t->render_rtlight_specular[2]);
                        }
                        // additive passes are only darkened by fog, not tinted
-                       if (r_glsl_permutation->loc_FogColor >= 0 && !notrippy)
+                       if (r_glsl_permutation->loc_FogColor >= 0 && !ui)
                        {
                                if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACK0)
                                        qglUniform3f(r_glsl_permutation->loc_FogColor, 0, 0, 0);
@@ -2254,7 +2261,7 @@ skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewid
                        return NULL;
                item = (skinframe_t *)Mem_ExpandableArray_AllocRecord(&r_skinframe.array);
                memset(item, 0, sizeof(*item));
-               strlcpy(item->basename, basename, sizeof(item->basename));
+               dp_strlcpy(item->basename, basename, sizeof(item->basename));
                item->textureflags = compareflags;
                item->comparewidth = comparewidth;
                item->compareheight = compareheight;
@@ -2983,7 +2990,7 @@ rtexture_t *R_GetCubemap(const char *basename)
                return r_texture_whitecube;
        r_texture_numcubemaps++;
        r_texture_cubemaps[i] = (cubemapinfo_t *)Mem_Alloc(r_main_mempool, sizeof(cubemapinfo_t));
-       strlcpy(r_texture_cubemaps[i]->basename, basename, sizeof(r_texture_cubemaps[i]->basename));
+       dp_strlcpy(r_texture_cubemaps[i]->basename, basename, sizeof(r_texture_cubemaps[i]->basename));
        r_texture_cubemaps[i]->texture = R_LoadCubemap(r_texture_cubemaps[i]->basename);
        return r_texture_cubemaps[i]->texture;
 }
@@ -3062,9 +3069,6 @@ static void gl_main_start(void)
        {
        case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
-               Cvar_SetValueQuick(&r_textureunits, MAX_TEXTUREUNITS);
-               Cvar_SetValueQuick(&gl_combine, 1);
-               Cvar_SetValueQuick(&r_glsl, 1);
                r_loadnormalmap = true;
                r_loadgloss = true;
                r_loadfog = false;
@@ -3189,7 +3193,7 @@ static void gl_main_shutdown(void)
        r_texture_numcubemaps = 0;
        //r_texture_fogintensity = NULL;
        memset(&r_fb, 0, sizeof(r_fb));
-       R_GLSL_Restart_f(&cmd_local);
+       R_GLSL_Restart_f(cmd_local);
 
        r_glsl_permutation = NULL;
        memset(r_glsl_permutationhash, 0, sizeof(r_glsl_permutationhash));
@@ -3326,8 +3330,6 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_transparent_sortarraysize);
        Cvar_RegisterVariable(&r_texture_dds_load);
        Cvar_RegisterVariable(&r_texture_dds_save);
-       Cvar_RegisterVariable(&r_textureunits);
-       Cvar_RegisterVariable(&gl_combine);
        Cvar_RegisterVariable(&r_usedepthtextures);
        Cvar_RegisterVariable(&r_viewfbo);
        Cvar_RegisterVariable(&r_rendertarget_debug);
@@ -3338,7 +3340,6 @@ void GL_Main_Init(void)
        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);
        Cvar_RegisterVariable(&r_glsl_offsetmapping_steps);
@@ -3359,6 +3360,7 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_glsl_postprocess_uservec4_enable);
        Cvar_RegisterVariable(&r_celshading);
        Cvar_RegisterVariable(&r_celoutlines);
+       Cvar_RegisterVariable(&r_fxaa);
 
        Cvar_RegisterVariable(&r_water);
        Cvar_RegisterVariable(&r_water_cameraentitiesonly);
@@ -3409,6 +3411,9 @@ void GL_Main_Init(void)
        for (i = 0;i < R_BUFFERDATA_COUNT;i++)
                Cvar_RegisterVariable(&r_buffermegs[i]);
        Cvar_RegisterVariable(&r_batch_dynamicbuffer);
+       Cvar_RegisterVariable(&r_q1bsp_lightmap_updates_enabled);
+       Cvar_RegisterVariable(&r_q1bsp_lightmap_updates_combine);
+       Cvar_RegisterVariable(&r_q1bsp_lightmap_updates_hidden_surfaces);
        if (gamemode == GAME_NEHAHRA || gamemode == GAME_TENEBRAE)
                Cvar_SetValue(&cvars_all, "r_fullbrights", 0);
 #ifdef DP_MOBILETOUCH
@@ -3435,107 +3440,48 @@ void Render_Init(void)
        R_Particles_Init();
        R_Explosion_Init();
        R_LightningBeams_Init();
+       CL_MeshEntities_Init();
        Mod_RenderInit();
 }
 
-int R_CullBox(const vec3_t mins, const vec3_t maxs)
+static void R_GetCornerOfBox(vec3_t out, const vec3_t mins, const vec3_t maxs, int signbits)
 {
-       int i;
-       mplane_t *p;
-       if (r_trippy.integer)
-               return false;
-       for (i = 0;i < r_refdef.view.numfrustumplanes;i++)
-       {
-               p = r_refdef.view.frustum + i;
-               switch(p->signbits)
-               {
-               default:
-               case 0:
-                       if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
-                               return true;
-                       break;
-               case 1:
-                       if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
-                               return true;
-                       break;
-               case 2:
-                       if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
-                               return true;
-                       break;
-               case 3:
-                       if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
-                               return true;
-                       break;
-               case 4:
-                       if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
-                               return true;
-                       break;
-               case 5:
-                       if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
-                               return true;
-                       break;
-               case 6:
-                       if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
-                               return true;
-                       break;
-               case 7:
-                       if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
-                               return true;
-                       break;
-               }
-       }
-       return false;
+       out[0] = ((signbits & 1) ? mins : maxs)[0];
+       out[1] = ((signbits & 2) ? mins : maxs)[1];
+       out[2] = ((signbits & 4) ? mins : maxs)[2];
 }
 
-int R_CullBoxCustomPlanes(const vec3_t mins, const vec3_t maxs, int numplanes, const mplane_t *planes)
+static qbool _R_CullBox(const vec3_t mins, const vec3_t maxs, int numplanes, const mplane_t *planes, int ignore)
 {
        int i;
        const mplane_t *p;
+       vec3_t corner;
        if (r_trippy.integer)
                return false;
        for (i = 0;i < numplanes;i++)
        {
+               if(i == ignore)
+                       continue;
                p = planes + i;
-               switch(p->signbits)
-               {
-               default:
-               case 0:
-                       if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
-                               return true;
-                       break;
-               case 1:
-                       if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
-                               return true;
-                       break;
-               case 2:
-                       if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
-                               return true;
-                       break;
-               case 3:
-                       if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
-                               return true;
-                       break;
-               case 4:
-                       if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
-                               return true;
-                       break;
-               case 5:
-                       if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
-                               return true;
-                       break;
-               case 6:
-                       if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
-                               return true;
-                       break;
-               case 7:
-                       if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
-                               return true;
-                       break;
-               }
+               R_GetCornerOfBox(corner, mins, maxs, p->signbits);
+               if (DotProduct(p->normal, corner) < p->dist)
+                       return true;
        }
        return false;
 }
 
+qbool R_CullFrustum(const vec3_t mins, const vec3_t maxs)
+{
+       // skip nearclip plane, it often culls portals when you are very close, and is almost never useful
+       return _R_CullBox(mins, maxs, r_refdef.view.numfrustumplanes, r_refdef.view.frustum, 4);
+}
+
+qbool R_CullBox(const vec3_t mins, const vec3_t maxs, int numplanes, const mplane_t *planes)
+{
+       // nothing to ignore
+       return _R_CullBox(mins, maxs, numplanes, planes, -1);
+}
+
 //==================================================================================
 
 // LadyHavoc: this stores temporary data used within the same frame
@@ -3883,7 +3829,7 @@ qbool R_AnimCache_GetEntity(entity_render_t *ent, qbool wantnormals, qbool wantt
                r_refdef.stats[r_stat_animcache_skeletal_bones] += model->num_bones;
                r_refdef.stats[r_stat_animcache_skeletal_maxbones] = max(r_refdef.stats[r_stat_animcache_skeletal_maxbones], model->num_bones);
                ent->animcache_skeletaltransform3x4 = (float *)R_FrameData_Alloc(sizeof(float[3][4]) * model->num_bones);
-               Mod_Skeletal_BuildTransforms(model, ent->frameblend, ent->skeleton, NULL, ent->animcache_skeletaltransform3x4); 
+               Mod_Skeletal_BuildTransforms(model, ent->frameblend, ent->skeleton, NULL, ent->animcache_skeletaltransform3x4);
                // note: this can fail if the buffer is at the grow limit
                ent->animcache_skeletaltransform3x4size = sizeof(float[3][4]) * model->num_bones;
                ent->animcache_skeletaltransform3x4buffer = R_BufferData_Store(ent->animcache_skeletaltransform3x4size, ent->animcache_skeletaltransform3x4, R_BUFFERDATA_UNIFORM, &ent->animcache_skeletaltransform3x4offset);
@@ -4007,13 +3953,13 @@ qbool R_CanSeeBox(int numsamples, vec_t eyejitter, vec_t entboxenlarge, vec_t en
        if (BoxesOverlap(boxmins, boxmaxs, eyemins, eyemaxs))
                return true;
 
+       VectorCopy(eye, start);
        // try specific positions in the box first - note that these can be cached
        if (r_cullentities_trace_entityocclusion.integer)
        {
                for (i = 0; i < sizeof(positions) / sizeof(positions[0]); i++)
                {
                        trace_t trace;
-                       VectorCopy(eye, start);
                        end[0] = boxmins[0] + (boxmaxs[0] - boxmins[0]) * positions[i][0];
                        end[1] = boxmins[1] + (boxmaxs[1] - boxmins[1]) * positions[i][1];
                        end[2] = boxmins[2] + (boxmaxs[2] - boxmins[2]) * positions[i][2];
@@ -4024,8 +3970,13 @@ qbool R_CanSeeBox(int numsamples, vec_t eyejitter, vec_t entboxenlarge, vec_t en
                                return true;
                }
        }
-       else if (model->brush.TraceLineOfSight(model, start, end, padmins, padmaxs))
-               return true;
+       else
+       {
+               // try center
+               VectorMAM(0.5f, boxmins, 0.5f, boxmaxs, end);
+               if (model->brush.TraceLineOfSight(model, start, end, padmins, padmaxs))
+                       return true;
+       }
 
        // try various random positions
        for (j = 0; j < numsamples; j++)
@@ -4071,13 +4022,13 @@ static void R_View_UpdateEntityVisible (void)
                for (i = 0;i < r_refdef.scene.numentities;i++)
                {
                        ent = r_refdef.scene.entities[i];
-                       if (ent != &cl.viewent.render && r_refdef.viewcache.world_novis)
+                       if (r_refdef.viewcache.world_novis && !(ent->flags & RENDER_VIEWMODEL))
                        {
                                r_refdef.viewcache.entityvisible[i] = false;
                                continue;
                        }
                        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 (!R_CullFrustum(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_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;
                }
@@ -4089,7 +4040,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 (!R_CullFrustum(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)))
                                r_refdef.viewcache.entityvisible[i] = true;
                }
        }
@@ -4249,7 +4200,7 @@ static void R_View_SetFrustum(const int *scissor)
        int i;
        double fpx = +1, fnx = -1, fpy = +1, fny = -1;
        vec3_t forward, left, up, origin, v;
-       if(r_lockvisibility.integer || r_lockpvs.integer)
+       if(r_lockvisibility.integer)
                return;
        if(scissor)
        {
@@ -4429,7 +4380,7 @@ static void R_View_SetFrustum(const int *scissor)
        //PlaneClassify(&frustum[4]);
 }
 
-static void R_View_UpdateWithScissor(const int *myscissor)
+static void R_View_Update(const int *myscissor)
 {
        R_Main_ResizeViewCache();
        R_View_SetFrustum(myscissor);
@@ -4437,21 +4388,13 @@ static void R_View_UpdateWithScissor(const int *myscissor)
        R_View_UpdateEntityVisible();
 }
 
-static void R_View_Update(void)
-{
-       R_Main_ResizeViewCache();
-       R_View_SetFrustum(NULL);
-       R_View_WorldVisibility(!r_refdef.view.usevieworiginculling);
-       R_View_UpdateEntityVisible();
-}
-
 float viewscalefpsadjusted = 1.0f;
 
 void R_SetupView(qbool allowwaterclippingplane, int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight)
 {
        const float *customclipplane = NULL;
        float plane[4];
-       int /*rtwidth,*/ rtheight;
+       int viewy_adjusted;
        if (r_refdef.view.useclipplane && allowwaterclippingplane)
        {
                // LadyHavoc: couldn't figure out how to make this approach work the same in DPSOFTRAST
@@ -4466,15 +4409,16 @@ void R_SetupView(qbool allowwaterclippingplane, int viewfbo, rtexture_t *viewdep
                customclipplane = plane;
        }
 
-       //rtwidth = viewfbo ? R_TextureWidth(viewdepthtexture ? viewdepthtexture : viewcolortexture) : vid.width;
-       rtheight = viewfbo ? R_TextureHeight(viewdepthtexture ? viewdepthtexture : viewcolortexture) : vid.height;
+       // GL is weird because it's bottom to top, r_refdef.view.y is top to bottom.
+       // Unless the render target is a FBO...
+       viewy_adjusted = viewfbo ? viewy : vid.mode.height - viewheight - viewy;
 
        if (!r_refdef.view.useperspective)
-               R_Viewport_InitOrtho3D(&r_refdef.view.viewport, &r_refdef.view.matrix, viewx, rtheight - viewheight - viewy, viewwidth, viewheight, r_refdef.view.ortho_x, r_refdef.view.ortho_y, -r_refdef.farclip, r_refdef.farclip, customclipplane);
+               R_Viewport_InitOrtho3D(&r_refdef.view.viewport, &r_refdef.view.matrix, viewx, viewy_adjusted, viewwidth, viewheight, 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, viewx, rtheight - viewheight - viewy, viewwidth, viewheight, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, customclipplane);
+               R_Viewport_InitPerspectiveInfinite(&r_refdef.view.viewport, &r_refdef.view.matrix, viewx, viewy_adjusted, viewwidth, viewheight, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, customclipplane);
        else
-               R_Viewport_InitPerspective(&r_refdef.view.viewport, &r_refdef.view.matrix, viewx, rtheight - viewheight - viewy, viewwidth, viewheight, 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, viewx, viewy_adjusted, viewwidth, viewheight, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, r_refdef.farclip, customclipplane);
        R_Mesh_SetRenderTargets(viewfbo, viewdepthtexture, viewcolortexture, NULL, NULL, NULL);
        R_SetViewport(&r_refdef.view.viewport);
 }
@@ -4504,11 +4448,15 @@ void R_EntityMatrix(const matrix4x4_t *matrix)
 void R_ResetViewRendering2D_Common(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight, float x2, float y2)
 {
        r_viewport_t viewport;
+       int viewy_adjusted;
 
        CHECKGLERROR
 
-       // GL is weird because it's bottom to top, r_refdef.view.y is top to bottom
-       R_Viewport_InitOrtho(&viewport, &identitymatrix, viewx, vid.height - viewheight - viewy, viewwidth, viewheight, 0, 0, x2, y2, -10, 100, NULL);
+       // GL is weird because it's bottom to top, r_refdef.view.y is top to bottom.
+       // Unless the render target is a FBO...
+       viewy_adjusted = viewfbo ? viewy : vid.mode.height - viewheight - viewy;
+
+       R_Viewport_InitOrtho(&viewport, &identitymatrix, viewx, viewy_adjusted, viewwidth, viewheight, 0, 0, x2, y2, -10, 100, NULL);
        R_Mesh_SetRenderTargets(viewfbo, viewdepthtexture, viewcolortexture, NULL, NULL, NULL);
        R_SetViewport(&viewport);
        GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height);
@@ -4912,10 +4860,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
                        GL_ScissorTest(false);
                        R_ClearScreen(r_refdef.fogenabled);
                        GL_ScissorTest(true);
-                       if(r_water_scissormode.integer & 2)
-                               R_View_UpdateWithScissor(myscissor);
-                       else
-                               R_View_Update();
+                       R_View_Update(r_water_scissormode.integer & 2 ? myscissor : NULL);
                        R_AnimCache_CacheVisibleEntities();
                        if(r_water_scissormode.integer & 1)
                                GL_Scissor(myscissor[0], myscissor[1], myscissor[2], myscissor[3]);
@@ -4980,10 +4925,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
                        GL_ScissorTest(false);
                        R_ClearScreen(r_refdef.fogenabled);
                        GL_ScissorTest(true);
-                       if(r_water_scissormode.integer & 2)
-                               R_View_UpdateWithScissor(myscissor);
-                       else
-                               R_View_Update();
+                       R_View_Update(r_water_scissormode.integer & 2 ? myscissor : NULL);
                        R_AnimCache_CacheVisibleEntities();
                        if(r_water_scissormode.integer & 1)
                                GL_Scissor(myscissor[0], myscissor[1], myscissor[2], myscissor[3]);
@@ -5030,7 +4972,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
                                r_refdef.view.usecustompvs = true;
                                r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, visorigin, 2, r_refdef.viewcache.world_pvsbits, (r_refdef.viewcache.world_numclusters+7)>>3, false);
                        }
-                       
+
                        // camera needs no clipplane
                        r_refdef.view.useclipplane = false;
                        // TODO: is the camera origin always valid?  if so we don't need to clear this
@@ -5044,7 +4986,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
                        GL_ScissorTest(false);
                        R_ClearScreen(r_refdef.fogenabled);
                        GL_ScissorTest(true);
-                       R_View_Update();
+                       R_View_Update(NULL);
                        R_AnimCache_CacheVisibleEntities();
                        R_RenderScene(rt->fbo, rt->depthtexture, rt->colortexture[0], 0, 0, rt->texturewidth, rt->textureheight);
 
@@ -5056,7 +4998,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
        r_fb.water.renderingscene = false;
        r_refdef.view = originalview;
        R_ResetViewRendering3D(fbo, depthtexture, colortexture, viewx, viewy, viewwidth, viewheight);
-       R_View_Update();
+       R_View_Update(NULL);
        R_AnimCache_CacheVisibleEntities();
        goto finish;
 error:
@@ -5128,8 +5070,8 @@ static void R_Bloom_StartFrame(void)
                viewscalefpsadjusted = 1.0f;
 
        scale = r_viewscale.value * sqrt(viewscalefpsadjusted);
-       if (vid.samples)
-               scale *= sqrt(vid.samples); // supersampling
+       if (vid.mode.samples)
+               scale *= sqrt(vid.mode.samples); // supersampling
        scale = bound(0.03125f, scale, 4.0f);
        screentexturewidth = (int)ceil(r_refdef.view.width * scale);
        screentextureheight = (int)ceil(r_refdef.view.height * scale);
@@ -5288,35 +5230,57 @@ static void R_Bloom_MakeTexture(void)
        r_fb.rt_bloom = cur;
 }
 
-static void R_BlendView(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight)
+static qbool R_BlendView_IsTrivial(int viewwidth, int viewheight, int width, int height)
 {
-       uint64_t permutation;
-       float uservecs[4][4];
-       rtexture_t *viewtexture;
-       rtexture_t *bloomtexture;
+       // Scaling requested?
+       if (viewwidth != width || viewheight != height)
+               return false;
+       // Higher bit depth or explicit FBO requested?
+       if (r_viewfbo.integer)
+               return false;
+       // Non-trivial postprocessing shader permutation?
+       if (r_fb.bloomwidth
+       || r_refdef.viewblend[3] > 0
+       || !vid_gammatables_trivial
+       || r_glsl_postprocess.integer
+       || ((!R_Stereo_ColorMasking() && r_glsl_saturation.value != 1)))
+               return false;
+       // Other reasons for a non-trivial default postprocessing shader?
+       // (See R_CompileShader_CheckStaticParms but only those relevant for MODE_POSTPROCESS in shader_glsl.h)
+       // Skip: if (r_glsl_saturation_redcompensate.integer) (already covered by saturation above).
+       // Skip: if (r_glsl_postprocess.integer) (already covered by r_glsl_postprocess above).
+       // Skip: if (r_glsl_postprocess_uservec1_enable.integer) (already covered by r_glsl_postprocessing above).
+       if (r_fxaa.integer)
+               return false;
+       if (r_colorfringe.value)
+               return false;
+       return true;
+}
 
+static void R_MotionBlurView(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight)
+{
        R_EntityMatrix(&identitymatrix);
 
-       if(r_refdef.view.ismain && !R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0) && r_fb.ghosttexture)
+       if(r_refdef.view.ismain && !R_Stereo_Active() && (r_motionblur.value > 0 || (r_damageblur.value > 0 && cl.cshifts[CSHIFT_DAMAGE].percent != 0)) && r_fb.ghosttexture)
        {
                // declare variables
                float blur_factor, blur_mouseaccel, blur_velocity;
-               static float blur_average; 
+               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) 
+               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) 
+               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_factor = ((blur_velocity * r_motionblur_velocityfactor.value)
                        + (blur_mouseaccel * r_motionblur_mousefactor.value));
 
                // from the goal, pick an averaged value between goal and last value
                cl.motionbluralpha = bound(0, (cl.time - cl.oldtime) / max(0.001, r_motionblur_averaging.value), 1);
                blur_average = blur_average * (1 - cl.motionbluralpha) + blur_factor * cl.motionbluralpha;
 
-               // enforce minimum amount of blur 
+               // enforce minimum amount of blur
                blur_factor = blur_average * (1 - r_motionblur_minblur.value) + r_motionblur_minblur.value;
 
                //Con_Printf("motionblur: direct factor: %f, averaged factor: %f, velocity: %f, mouse accel: %f \n", blur_factor, blur_average, blur_velocity, blur_mouseaccel);
@@ -5336,7 +5300,7 @@ static void R_BlendView(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *v
                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
+               // apply the blur on top of the current view
                R_ResetViewRendering2D(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
                if (cl.motionbluralpha > 0 && !r_refdef.envmap && r_fb.ghosttexture_valid)
                {
@@ -5357,6 +5321,16 @@ static void R_BlendView(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *v
                r_refdef.stats[r_stat_bloom_copypixels] += viewwidth * viewheight;
                r_fb.ghosttexture_valid = true;
        }
+}
+
+static void R_BlendView(rtexture_t *viewcolortexture, int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, int x, int y, int width, int height)
+{
+       uint64_t permutation;
+       float uservecs[4][4];
+       rtexture_t *viewtexture;
+       rtexture_t *bloomtexture;
+
+       R_EntityMatrix(&identitymatrix);
 
        if (r_fb.bloomwidth)
        {
@@ -5378,11 +5352,11 @@ static void R_BlendView(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *v
                sscanf(r_glsl_postprocess_uservec4.string, "%f %f %f %f", &uservecs[3][0], &uservecs[3][1], &uservecs[3][2], &uservecs[3][3]);
 
        // render to the screen fbo
-       R_ResetViewRendering2D(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
+       R_ResetViewRendering2D(fbo, depthtexture, colortexture, x, y, width, height);
        GL_Color(1, 1, 1, 1);
        GL_BlendFunc(GL_ONE, GL_ZERO);
 
-       viewtexture = r_fb.rt_screen->colortexture[0];
+       viewtexture = viewcolortexture;
        bloomtexture = r_fb.rt_bloom ? r_fb.rt_bloom->colortexture[0] : NULL;
 
        if (r_rendertarget_debug.integer >= 0)
@@ -5539,6 +5513,11 @@ void R_UpdateVariables(void)
        if (r_refdef.scene.worldmodel)
        {
                r_refdef.scene.lightmapintensity *= r_refdef.scene.worldmodel->lightmapscale;
+
+               // Apply the default lightstyle to the lightmap even on q3bsp
+               if (cl.worldmodel && cl.worldmodel->type == mod_brushq3) {
+                       r_refdef.scene.lightmapintensity *= r_refdef.scene.rtlightstylevalue[0];
+               }
        }
        if (r_showsurfaces.integer)
        {
@@ -5577,7 +5556,7 @@ void R_UpdateVariables(void)
                                }
                                if (r_texture_gammaramps)
                                {
-                                       R_UpdateTexture(r_texture_gammaramps, &rampbgr[0][0], 0, 0, 0, RAMPWIDTH, 1, 1);
+                                       R_UpdateTexture(r_texture_gammaramps, &rampbgr[0][0], 0, 0, 0, RAMPWIDTH, 1, 1, 0);
                                }
                                else
                                {
@@ -5671,6 +5650,7 @@ void R_RenderView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, i
        rtexture_t *viewdepthtexture = NULL;
        rtexture_t *viewcolortexture = NULL;
        int viewx = r_refdef.view.x, viewy = r_refdef.view.y, viewwidth = r_refdef.view.width, viewheight = r_refdef.view.height;
+       qbool skipblend;
 
        // finish any 2D rendering that was queued
        DrawQ_Finish();
@@ -5681,7 +5661,7 @@ void R_RenderView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, i
        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveModelEntity
 
        if(R_CompileShader_CheckStaticParms())
-               R_GLSL_Restart_f(&cmd_local);
+               R_GLSL_Restart_f(cmd_local);
 
        if (!r_drawentities.integer)
                r_refdef.scene.numentities = 0;
@@ -5745,9 +5725,21 @@ void R_RenderView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, i
        if(r_fb.rt_bloom)
                r_refdef.view.colorscale *= r_bloom_scenebrightness.value;
 
-       // R_Bloom_StartFrame probably set up an fbo for us to render into, it will be rendered to the window later in R_BlendView
-       if (r_fb.rt_screen)
+       skipblend = R_BlendView_IsTrivial(r_fb.rt_screen->texturewidth, r_fb.rt_screen->textureheight, width, height);
+       if (skipblend)
+       {
+               // Render to the screen right away.
+               viewfbo = fbo;
+               viewdepthtexture = depthtexture;
+               viewcolortexture = colortexture;
+               viewx = x;
+               viewy = y;
+               viewwidth = width;
+               viewheight = height;
+       }
+       else if (r_fb.rt_screen)
        {
+               // R_Bloom_StartFrame probably set up an fbo for us to render into, it will be rendered to the window later in R_BlendView
                viewfbo = r_fb.rt_screen->fbo;
                viewdepthtexture = r_fb.rt_screen->depthtexture;
                viewcolortexture = r_fb.rt_screen->colortexture[0];
@@ -5777,7 +5769,7 @@ void R_RenderView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, i
 
        r_refdef.view.showdebug = true;
 
-       R_View_Update();
+       R_View_Update(NULL);
        if (r_timereport_active)
                R_TimeReport("visibility");
 
@@ -5796,14 +5788,16 @@ void R_RenderView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, i
        // test needs to be on
        if (r_fb.rt_screen)
                GL_ScissorTest(true);
-       GL_Scissor(viewx, viewy, viewwidth, viewheight);
+       GL_Scissor(r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
        R_RenderScene(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
        r_fb.water.numwaterplanes = 0;
 
        // postprocess uses textures that are not aligned with the viewport we're rendering, so no scissoring
        GL_ScissorTest(false);
 
-       R_BlendView(fbo, depthtexture, colortexture, x, y, width, height);
+       R_MotionBlurView(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
+       if (!skipblend)
+               R_BlendView(viewcolortexture, fbo, depthtexture, colortexture, x, y, width, height);
        if (r_timereport_active)
                R_TimeReport("blendview");
 
@@ -6053,7 +6047,7 @@ static const unsigned short bboxelements[36] =
 };
 
 #define BBOXEDGES 13
-static const float bboxedges[BBOXEDGES][6] = 
+static const float bboxedges[BBOXEDGES][6] =
 {
        // whole box
        { 0, 0, 0, 1, 1, 1 },
@@ -6172,7 +6166,7 @@ static void R_DrawEntityBBoxes(prvm_prog_t *prog)
        for (i = 0; i < prog->num_edicts; i++)
        {
                edict = PRVM_EDICT_NUM(i);
-               if (edict->priv.server->free)
+               if (edict->free)
                        continue;
                // exclude the following for now, as they don't live in world coordinate space and can't be solid:
                if (PRVM_gameedictedict(edict, tag_entity) != 0)
@@ -6568,7 +6562,7 @@ static void R_LoadQWSkin(r_qwskincache_t *cache, const char *skinname)
        char name[MAX_QPATH];
        skinframe_t *skinframe;
        unsigned char pixels[296*194];
-       strlcpy(cache->name, skinname, sizeof(cache->name));
+       dp_strlcpy(cache->name, skinname, sizeof(cache->name));
        dpsnprintf(name, sizeof(name), "skins/%s.pcx", cache->name);
        if (developer_loading.integer)
                Con_Printf("loading %s\n", name);
@@ -8616,14 +8610,14 @@ static int RSurf_FindWaterPlaneForSurface(const msurface_t *surface)
        // render multiple smaller batches
 }
 
-void RSurf_SetupDepthAndCulling(void)
+void RSurf_SetupDepthAndCulling(bool ui)
 {
        // submodels are biased to avoid z-fighting with world surfaces that they
        // may be exactly overlapping (avoids z-fighting artifacts on certain
        // doors and things in Quake maps)
        GL_DepthRange(0, (rsurface.texture->currentmaterialflags & MATERIALFLAG_SHORTDEPTHRANGE) ? 0.0625 : 1);
        GL_PolygonOffset(rsurface.basepolygonfactor + rsurface.texture->biaspolygonfactor, rsurface.basepolygonoffset + rsurface.texture->biaspolygonoffset);
-       GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
+       GL_DepthTest(!ui && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
        GL_CullFace((rsurface.texture->currentmaterialflags & MATERIALFLAG_NOCULLFACE) ? GL_NONE : r_refdef.view.cullface_back);
 }
 
@@ -8638,7 +8632,7 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_
                return;
        R_SetupShader_Generic_NoTexture(false, false);
        skyrenderlater = true;
-       RSurf_SetupDepthAndCulling();
+       RSurf_SetupDepthAndCulling(false);
        GL_DepthMask(true);
 
        // add the vertices of the surfaces to a world bounding box so we can scissor the sky render later
@@ -8734,7 +8728,7 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface
        {
                // render screenspace normalmap to texture
                GL_DepthMask(true);
-               R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_DEFERREDGEOMETRY, texturenumsurfaces, texturesurfacelist, NULL, false);
+               R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_DEFERREDGEOMETRY, texturenumsurfaces, texturesurfacelist, NULL, false, false);
                RSurf_DrawBatch();
                return;
        }
@@ -8762,18 +8756,18 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface
                        {
                                // render water or distortion background
                                GL_DepthMask(true);
-                               R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BACKGROUND, end-start, texturesurfacelist + start, (void *)(r_fb.water.waterplanes + startplaneindex), false);
+                               R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BACKGROUND, end-start, texturesurfacelist + start, (void *)(r_fb.water.waterplanes + startplaneindex), false, false);
                                RSurf_DrawBatch();
                                // blend surface on top
                                GL_DepthMask(false);
-                               R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BASE, end-start, texturesurfacelist + start, NULL, false);
+                               R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BASE, end-start, texturesurfacelist + start, NULL, false, false);
                                RSurf_DrawBatch();
                        }
                        else if ((rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION))
                        {
                                // render surface with reflection texture as input
                                GL_DepthMask(writedepth && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED));
-                               R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BASE, end-start, texturesurfacelist + start, (void *)(r_fb.water.waterplanes + startplaneindex), false);
+                               R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BASE, end-start, texturesurfacelist + start, (void *)(r_fb.water.waterplanes + startplaneindex), false, false);
                                RSurf_DrawBatch();
                        }
                }
@@ -8782,7 +8776,7 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface
 
        // render surface batch normally
        GL_DepthMask(writedepth && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED));
-       R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BASE, texturenumsurfaces, texturesurfacelist, NULL, (rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY) != 0 || ui);
+       R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BASE, texturenumsurfaces, texturesurfacelist, NULL, (rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY) != 0 || ui, ui);
        RSurf_DrawBatch();
 }
 
@@ -8794,6 +8788,8 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const
        int k;
        const msurface_t *surface;
        float surfacecolor4f[4];
+       float c[4];
+       texture_t *t = rsurface.texture;
 
 //     R_Mesh_ResetTextureState();
        R_SetupShader_Generic_NoTexture(false, false);
@@ -8801,18 +8797,70 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const
        GL_BlendFunc(GL_ONE, GL_ZERO);
        GL_DepthMask(writedepth);
 
-       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_VERTEXCOLOR | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_ALWAYSCOPY, texturenumsurfaces, texturesurfacelist);
-       vi = 0;
-       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+       switch (r_showsurfaces.integer)
        {
-               surface = texturesurfacelist[texturesurfaceindex];
-               k = (int)(((size_t)surface) / sizeof(msurface_t));
-               Vector4Set(surfacecolor4f, (k & 0xF) * (1.0f / 16.0f), (k & 0xF0) * (1.0f / 256.0f), (k & 0xF00) * (1.0f / 4096.0f), 1);
-               for (j = 0;j < surface->num_vertices;j++)
-               {
-                       Vector4Copy(surfacecolor4f, rsurface.batchlightmapcolor4f + 4 * vi);
-                       vi++;
-               }
+               case 1:
+               default:
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_VERTEXCOLOR | BATCHNEED_ALWAYSCOPY, texturenumsurfaces, texturesurfacelist);
+                       vi = 0;
+                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                       {
+                               surface = texturesurfacelist[texturesurfaceindex];
+                               k = (int)(((size_t)surface) / sizeof(msurface_t));
+                               Vector4Set(surfacecolor4f, (k & 0xF) * (1.0f / 16.0f), (k & 0xF0) * (1.0f / 256.0f), (k & 0xF00) * (1.0f / 4096.0f), 1);
+                               for (j = 0;j < surface->num_vertices;j++)
+                               {
+                                       Vector4Copy(surfacecolor4f, rsurface.batchlightmapcolor4f + 4 * vi);
+                                       vi++;
+                               }
+                       }
+                       break;
+               case 3:
+                       if(t && t->currentskinframe)
+                       {
+                               Vector4Copy(t->currentskinframe->avgcolor, c);
+                               c[3] *= t->currentalpha;
+                       }
+                       else
+                       {
+                               Vector4Set(c, 1, 0, 1, 1);
+                       }
+                       if (t && (t->pantstexture || t->shirttexture))
+                       {
+                               VectorMAM(0.7, t->render_colormap_pants, 0.3, t->render_colormap_shirt, c);
+                       }
+                       VectorScale(c, 2 * r_refdef.view.colorscale, c);
+                       if(t->currentmaterialflags & MATERIALFLAG_WATERALPHA)
+                               c[3] *= r_wateralpha.value;
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_VERTEXCOLOR | BATCHNEED_ALWAYSCOPY, texturenumsurfaces, texturesurfacelist);
+                       vi = 0;
+                       if (rsurface.modellightmapcolor4f)
+                       {
+                               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                               {
+                                       surface = texturesurfacelist[texturesurfaceindex];
+                                       for (j = 0;j < surface->num_vertices;j++)
+                                       {
+                                               float *ptr = rsurface.batchlightmapcolor4f + 4 * vi;
+                                               Vector4Multiply(ptr, c, ptr);
+                                               vi++;
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                               {
+                                       surface = texturesurfacelist[texturesurfaceindex];
+                                       for (j = 0;j < surface->num_vertices;j++)
+                                       {
+                                               float *ptr = rsurface.batchlightmapcolor4f + 4 * vi;
+                                               Vector4Copy(c, ptr);
+                                               vi++;
+                                       }
+                               }
+                       }
+                       break;
        }
        R_Mesh_PrepareVertices_Generic_Arrays(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchlightmapcolor4f, rsurface.batchtexcoordtexture2f);
        RSurf_DrawBatch();
@@ -8821,7 +8869,7 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const
 static void R_DrawModelTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, qbool writedepth, qbool prepass, qbool ui)
 {
        CHECKGLERROR
-       RSurf_SetupDepthAndCulling();
+       RSurf_SetupDepthAndCulling(ui);
        if (r_showsurfaces.integer && r_refdef.view.showdebug)
        {
                R_DrawTextureSurfaceList_ShowSurfaces(texturenumsurfaces, texturesurfacelist, writedepth);
@@ -8883,7 +8931,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                                GL_DepthMask(true);
 //                             R_Mesh_ResetTextureState();
                        }
-                       RSurf_SetupDepthAndCulling();
+                       RSurf_SetupDepthAndCulling(false);
                        RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist);
                        R_SetupShader_DepthOrShadow(false, false, !!rsurface.batchskeletaltransform3x4);
                        R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
@@ -8957,7 +9005,7 @@ static void R_DrawTextureSurfaceList_DepthOnly(int texturenumsurfaces, const msu
                return;
        if (r_fb.water.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)))
                return;
-       RSurf_SetupDepthAndCulling();
+       RSurf_SetupDepthAndCulling(false);
        RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist);
        R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
        R_SetupShader_DepthOrShadow(false, false, !!rsurface.batchskeletaltransform3x4);
@@ -9308,11 +9356,8 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor
        model_t *model;
        const msurface_t *surface;
        const msurface_t *surfaces;
-       const int *surfacelist;
        const texture_t *texture;
        int numtriangles;
-       int numsurfacelist;
-       int surfacelistindex;
        int surfaceindex;
        int triangleindex;
        float localorigin[3];
@@ -9405,8 +9450,6 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor
 #endif
 
        dynamic = model->surfmesh.isanimated;
-       numsurfacelist = model->nummodelsurfaces;
-       surfacelist = model->sortedmodelsurfaces;
        surfaces = model->data_surfaces;
 
        bih = NULL;
@@ -9442,9 +9485,8 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor
        }
        else
        {
-               for (surfacelistindex = 0;surfacelistindex < numsurfacelist;surfacelistindex++)
+               for (surfaceindex = model->submodelsurfaces_start;surfaceindex < model->submodelsurfaces_end;surfaceindex++)
                {
-                       surfaceindex = surfacelist[surfacelistindex];
                        surface = surfaces + surfaceindex;
                        // check cull box first because it rejects more than any other check
                        if (!dynamic && !BoxesOverlap(surface->mins, surface->maxs, localmins, localmaxs))
@@ -9770,7 +9812,7 @@ static void R_DrawModelDecals(void)
 static void R_DrawDebugModel(void)
 {
        entity_render_t *ent = rsurface.entity;
-       int i, j, flagsmask;
+       int j, flagsmask;
        const msurface_t *surface;
        model_t *model = ent->model;
 
@@ -9786,10 +9828,11 @@ static void R_DrawDebugModel(void)
                GL_DepthMask(false);
                GL_DepthRange(0, 1);
                GL_BlendFunc(GL_ONE, GL_ONE);
-               for (i = 0, j = model->firstmodelsurface, surface = model->data_surfaces + j;i < model->nummodelsurfaces;i++, j++, surface++)
+               for (j = model->submodelsurfaces_start;j < model->submodelsurfaces_end;j++)
                {
                        if (ent == r_refdef.scene.worldentity && !r_refdef.viewcache.world_surfacevisible[j])
                                continue;
+                       surface = model->data_surfaces + j;
                        rsurface.texture = R_GetCurrentTexture(surface->texture);
                        if ((rsurface.texture->currentmaterialflags & flagsmask) && surface->num_triangles)
                        {
@@ -9829,7 +9872,7 @@ static void R_DrawDebugModel(void)
                GL_PolygonOffset(r_refdef.polygonfactor + r_showcollisionbrushes_polygonfactor.value, r_refdef.polygonoffset + r_showcollisionbrushes_polygonoffset.value);
                for (bihleafindex = 0, bihleaf = bih->leafs;bihleafindex < bih->numleafs;bihleafindex++, bihleaf++)
                {
-                       if (cullbox && R_CullBox(bihleaf->mins, bihleaf->maxs))
+                       if (cullbox && R_CullFrustum(bihleaf->mins, bihleaf->maxs))
                                continue;
                        switch (bihleaf->type)
                        {
@@ -9880,10 +9923,11 @@ static void R_DrawDebugModel(void)
                        GL_DepthMask(true);
                }
                qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);CHECKGLERROR
-               for (i = 0, j = model->firstmodelsurface, surface = model->data_surfaces + j;i < model->nummodelsurfaces;i++, j++, surface++)
+               for (j = model->submodelsurfaces_start; j < model->submodelsurfaces_end; j++)
                {
                        if (ent == r_refdef.scene.worldentity && !r_refdef.viewcache.world_surfacevisible[j])
                                continue;
+                       surface = model->data_surfaces + j;
                        rsurface.texture = R_GetCurrentTexture(surface->texture);
                        if ((rsurface.texture->currentmaterialflags & flagsmask) && surface->num_triangles)
                        {
@@ -9918,10 +9962,11 @@ static void R_DrawDebugModel(void)
                        GL_BlendFunc(GL_ONE, GL_ZERO);
                        GL_DepthMask(true);
                }
-               for (i = 0, j = model->firstmodelsurface, surface = model->data_surfaces + j;i < model->nummodelsurfaces;i++, j++, surface++)
+               for (j = model->submodelsurfaces_start; j < model->submodelsurfaces_end; j++)
                {
                        if (ent == r_refdef.scene.worldentity && !r_refdef.viewcache.world_surfacevisible[j])
                                continue;
+                       surface = model->data_surfaces + j;
                        rsurface.texture = R_GetCurrentTexture(surface->texture);
                        if ((rsurface.texture->currentmaterialflags & flagsmask) && surface->num_triangles)
                        {
@@ -9989,7 +10034,7 @@ int r_maxsurfacelist = 0;
 const msurface_t **r_surfacelist = NULL;
 void R_DrawModelSurfaces(entity_render_t *ent, qbool skysurfaces, qbool writedepth, qbool depthonly, qbool debug, qbool prepass, qbool ui)
 {
-       int i, j, endj, flagsmask;
+       int i, j, flagsmask;
        model_t *model = ent->model;
        msurface_t *surfaces;
        unsigned char *update;
@@ -10017,26 +10062,6 @@ void R_DrawModelSurfaces(entity_render_t *ent, qbool skysurfaces, qbool writedep
        surfaces = model->data_surfaces;
        update = model->brushq1.lightmapupdateflags;
 
-       // update light styles
-       if (!skysurfaces && !depthonly && !prepass && model->brushq1.num_lightstyles && r_refdef.scene.lightmapintensity > 0)
-       {
-               model_brush_lightstyleinfo_t *style;
-               // Iterate over each active style
-               for (i = 0, style = model->brushq1.data_lightstyleinfo;i < model->brushq1.num_lightstyles;i++, style++)
-               {
-                       if (style->value != r_refdef.scene.lightstylevalue[style->style])
-                       {
-                               int *list = style->surfacelist;
-                               style->value = r_refdef.scene.lightstylevalue[style->style];
-                               // Iterate over every surface this style applies to
-                               for (j = 0;j < style->numsurfaces;j++)
-                                       // Update brush entities even if not visible otherwise they'll render solid black.
-                                       if(r_refdef.viewcache.world_surfacevisible[list[j]] || ent != r_refdef.scene.worldentity)
-                                               update[list[j]] = true;
-                       }
-               }
-       }
-
        flagsmask = skysurfaces ? MATERIALFLAG_SKY : MATERIALFLAG_WALL;
 
        if (debug)
@@ -10046,48 +10071,88 @@ void R_DrawModelSurfaces(entity_render_t *ent, qbool skysurfaces, qbool writedep
                return;
        }
 
+       // check if this is an empty model
+       if (model->submodelsurfaces_start >= model->submodelsurfaces_end)
+               return;
+
        rsurface.lightmaptexture = NULL;
        rsurface.deluxemaptexture = NULL;
        rsurface.uselightmaptexture = false;
        rsurface.texture = NULL;
        rsurface.rtlight = NULL;
        numsurfacelist = 0;
+
        // add visible surfaces to draw list
        if (ent == r_refdef.scene.worldentity)
        {
                // for the world entity, check surfacevisible
-               for (i = 0;i < model->nummodelsurfaces;i++)
+               for (i = model->submodelsurfaces_start;i < model->submodelsurfaces_end;i++)
                {
-                       j = model->sortedmodelsurfaces[i];
+                       j = model->modelsurfaces_sorted[i];
                        if (r_refdef.viewcache.world_surfacevisible[j])
                                r_surfacelist[numsurfacelist++] = surfaces + j;
                }
+
+               // don't do anything if there were no surfaces added (none of the world entity is visible)
+               if (!numsurfacelist)
+               {
+                       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveModelEntity
+                       return;
+               }
        }
        else if (ui)
        {
-               // for ui we have to preserve the order of surfaces
-               for (i = 0; i < model->nummodelsurfaces; i++)
-                       r_surfacelist[numsurfacelist++] = surfaces + model->firstmodelsurface + i;
+               // for ui we have to preserve the order of surfaces (not using modelsurfaces_sorted)
+               for (i = model->submodelsurfaces_start; i < model->submodelsurfaces_end; i++)
+                       r_surfacelist[numsurfacelist++] = surfaces + i;
        }
        else
        {
                // add all surfaces
-               for (i = 0; i < model->nummodelsurfaces; i++)
-                       r_surfacelist[numsurfacelist++] = surfaces + model->sortedmodelsurfaces[i];
+               for (i = model->submodelsurfaces_start; i < model->submodelsurfaces_end; i++)
+                       r_surfacelist[numsurfacelist++] = surfaces + model->modelsurfaces_sorted[i];
        }
-       // don't do anything if there were no surfaces
-       if (!numsurfacelist)
-       {
-               rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveModelEntity
-               return;
-       }
-       // update lightmaps if needed
-       if (update)
+
+       /*
+        * Mark lightmaps as dirty if their lightstyle's value changed. We do this by
+        * using style chains because most styles do not change on most frames, and most
+        * surfaces do not have styles on them. Mods like Arcane Dimensions (e.g. ad_necrokeep)
+        * break this rule and animate most surfaces.
+        */
+       if (update && !skysurfaces && !depthonly && !prepass && model->brushq1.num_lightstyles && r_refdef.scene.lightmapintensity > 0 && r_q1bsp_lightmap_updates_enabled.integer)
        {
-               for (j = model->firstmodelsurface, endj = model->firstmodelsurface + model->nummodelsurfaces;j < endj;j++)
+               model_brush_lightstyleinfo_t *style;
+
+               // For each lightstyle, check if its value changed and mark the lightmaps as dirty if so
+               for (i = 0, style = model->brushq1.data_lightstyleinfo; i < model->brushq1.num_lightstyles; i++, style++)
+               {
+                       if (style->value != r_refdef.scene.lightstylevalue[style->style])
+                       {
+                               int* list = style->surfacelist;
+                               style->value = r_refdef.scene.lightstylevalue[style->style];
+                               // Value changed - mark the surfaces belonging to this style chain as dirty
+                               for (j = 0; j < style->numsurfaces; j++)
+                                       update[list[j]] = true;
+                       }
+               }
+               // Now check if update flags are set on any surfaces that are visible
+               if (r_q1bsp_lightmap_updates_hidden_surfaces.integer)
+               {
+                       /*
+                        * We can do less frequent texture uploads (approximately 10hz for animated
+                        * lightstyles) by rebuilding lightmaps on surfaces that are not currently visible.
+                        * For optimal efficiency, this includes the submodels of the worldmodel, so we
+                        * use model->num_surfaces, not nummodelsurfaces.
+                        */
+                       for (i = 0; i < model->num_surfaces;i++)
+                               if (update[i])
+                                       R_BuildLightMap(ent, surfaces + i, r_q1bsp_lightmap_updates_combine.integer);
+               }
+               else
                {
-                       if (update[j])
-                               R_BuildLightMap(ent, surfaces + j);
+                       for (i = 0; i < numsurfacelist; i++)
+                               if (update[r_surfacelist[i] - surfaces])
+                                       R_BuildLightMap(ent, (msurface_t *)r_surfacelist[i], r_q1bsp_lightmap_updates_combine.integer);
                }
        }
 
@@ -10119,10 +10184,10 @@ void R_DebugLine(vec3_t start, vec3_t end)
        Vector4Set(w[1], end[0], end[1], end[2], 1);
        R_Viewport_TransformToScreen(&r_refdef.view.viewport, w[0], s[0]);
        R_Viewport_TransformToScreen(&r_refdef.view.viewport, w[1], s[1]);
-       x1 = s[0][0] * vid_conwidth.value / vid.width;
-       y1 = (vid.height - s[0][1]) * vid_conheight.value / vid.height;
-       x2 = s[1][0] * vid_conwidth.value / vid.width;
-       y2 = (vid.height - s[1][1]) * vid_conheight.value / vid.height;
+       x1 = s[0][0] * vid_conwidth.value / vid.mode.width;
+       y1 = (vid.mode.height - s[0][1]) * vid_conheight.value / vid.mode.height;
+       x2 = s[1][0] * vid_conwidth.value / vid.mode.width;
+       y2 = (vid.mode.height - s[1][1]) * vid_conheight.value / vid.mode.height;
        //Con_DPrintf("R_DebugLine: %.0f,%.0f to %.0f,%.0f\n", x1, y1, x2, y2);
 
        // add the line to the UI mesh for drawing later
@@ -10131,11 +10196,11 @@ void R_DebugLine(vec3_t start, vec3_t end)
        if (fabs(x2 - x1) > fabs(y2 - y1))
        {
                offsetx = 0;
-               offsety = 0.5f * width * vid_conheight.value / vid.height;
+               offsety = 0.5f * width * vid_conheight.value / vid.mode.height;
        }
        else
        {
-               offsetx = 0.5f * width * vid_conwidth.value / vid.width;
+               offsetx = 0.5f * width * vid_conwidth.value / vid.mode.width;
                offsety = 0;
        }
        surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, "white", 0, 0, MATERIALFLAG_WALL | MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX | MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW), true);