]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
added R_FrameData_Alloc and Store functions (a per-frame heap allocator
[xonotic/darkplaces.git] / gl_rmain.c
index 4edc0c5ab3200a475b5c737c8e9fef1c9b4a0fd4..671786c8893d2ab9831143e4f3bcaa8d05fe1bbd 100644 (file)
@@ -54,8 +54,6 @@ cvar_t r_equalize_entities_minambient = {CVAR_SAVE, "r_equalize_entities_minambi
 cvar_t r_equalize_entities_by = {CVAR_SAVE, "r_equalize_entities_by", "0.7", "light equalizing: exponent of dynamics compression (0 = no compression, 1 = full compression)"};
 cvar_t r_equalize_entities_to = {CVAR_SAVE, "r_equalize_entities_to", "0.8", "light equalizing: target light level"};
 
-cvar_t r_animcache = {CVAR_SAVE, "r_animcache", "1", "cache animation frames to save CPU usage, primarily optimizes shadows and reflections"};
-
 cvar_t r_depthfirst = {CVAR_SAVE, "r_depthfirst", "0", "renders a depth-only version of the scene before normal rendering begins to eliminate overdraw, values: 0 = off, 1 = world depth, 2 = world and model depth"};
 cvar_t r_useinfinitefarclip = {CVAR_SAVE, "r_useinfinitefarclip", "1", "enables use of a special kind of projection matrix that has an extremely large farclip"};
 cvar_t r_farclip_base = {0, "r_farclip_base", "65536", "farclip (furthest visible distance) for rendering when r_useinfinitefarclip is 0"};
@@ -107,9 +105,10 @@ cvar_t gl_fogstart = {0, "gl_fogstart", "0", "nehahra fog start distance (for Ne
 cvar_t gl_fogend = {0, "gl_fogend","0", "nehahra fog end distance (for Nehahra compatibility only)"};
 cvar_t gl_skyclip = {0, "gl_skyclip", "4608", "nehahra farclip distance - the real fog end (for Nehahra compatibility only)"};
 
-cvar_t r_textureunits = {0, "r_textureunits", "32", "number of hardware texture units reported by driver (note: setting this to 1 turns off gl_combine)"};
+cvar_t r_textureunits = {0, "r_textureunits", "32", "number of texture units to use in GL 1.1 and GL 1.3 rendering paths"};
+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_glsl = {CVAR_SAVE, "r_glsl", "1", "enables use of OpenGL 2.0 pixel shaders for lighting"};
 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_reliefmapping = {CVAR_SAVE, "r_glsl_offsetmapping_reliefmapping", "0", "relief mapping effect (higher quality)"};
@@ -119,7 +118,6 @@ cvar_t r_glsl_postprocess_uservec1 = {CVAR_SAVE, "r_glsl_postprocess_uservec1",
 cvar_t r_glsl_postprocess_uservec2 = {CVAR_SAVE, "r_glsl_postprocess_uservec2", "0 0 0 0", "a 4-component vector to pass as uservec2 to the postprocessing shader (only useful if default.glsl has been customized)"};
 cvar_t r_glsl_postprocess_uservec3 = {CVAR_SAVE, "r_glsl_postprocess_uservec3", "0 0 0 0", "a 4-component vector to pass as uservec3 to the postprocessing shader (only useful if default.glsl has been customized)"};
 cvar_t r_glsl_postprocess_uservec4 = {CVAR_SAVE, "r_glsl_postprocess_uservec4", "0 0 0 0", "a 4-component vector to pass as uservec4 to the postprocessing shader (only useful if default.glsl has been customized)"};
-cvar_t r_glsl_usegeneric = {CVAR_SAVE, "r_glsl_usegeneric", "1", "use shaders for rendering simple geometry (rather than conventional fixed-function rendering for this purpose)"};
 
 cvar_t r_water = {CVAR_SAVE, "r_water", "0", "whether to use reflections and refraction on water surfaces (note: r_wateralpha must be set below 1)"};
 cvar_t r_water_clippingplanebias = {CVAR_SAVE, "r_water_clippingplanebias", "1", "a rather technical setting which avoids black pixels around water edges"};
@@ -159,6 +157,8 @@ cvar_t r_track_sprites_scalew = {CVAR_SAVE, "r_track_sprites_scalew", "1", "widt
 cvar_t r_track_sprites_scaleh = {CVAR_SAVE, "r_track_sprites_scaleh", "1", "height scaling of tracked sprites"};
 cvar_t r_glsl_saturation = {CVAR_SAVE, "r_glsl_saturation", "1", "saturation multiplier (only working in glsl!)"};
 
+cvar_t r_framedatasize = {CVAR_SAVE, "r_framedatasize", "1", "size of renderer data cache used during one frame (for skeletal animation caching, light processing, etc)"};
+
 extern cvar_t v_glslgamma;
 
 extern qboolean v_flipped_state;
@@ -1877,7 +1877,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
                p->loc_Texture_ShadowMapRect      = qglGetUniformLocationARB(p->program, "Texture_ShadowMapRect");
                p->loc_Texture_ShadowMapCube      = qglGetUniformLocationARB(p->program, "Texture_ShadowMapCube");
                p->loc_Texture_ShadowMap2D        = qglGetUniformLocationARB(p->program, "Texture_ShadowMap2D");
-               p->loc_Texture_CubeProjection     = qglGetUniformLocationARB(p->program, "Texture_CubeProjection");  
+               p->loc_Texture_CubeProjection     = qglGetUniformLocationARB(p->program, "Texture_CubeProjection");
                p->loc_FogColor                   = qglGetUniformLocationARB(p->program, "FogColor");
                p->loc_LightPosition              = qglGetUniformLocationARB(p->program, "LightPosition");
                p->loc_EyePosition                = qglGetUniformLocationARB(p->program, "EyePosition");
@@ -2027,10 +2027,10 @@ void R_SetupShader_SetPermutation(unsigned int mode, unsigned int permutation)
                                }
                                if (i >= SHADERPERMUTATION_COUNT)
                                {
-                                       Con_Printf("OpenGL 2.0 shaders disabled - unable to find a working shader permutation fallback on this driver (set r_glsl 1 if you want to try again)\n");
-                                       Cvar_SetValueQuick(&r_glsl, 0);
-                                       R_GLSL_Restart_f(); // unload shaders
-                                       return; // no bit left to clear
+                                       Con_Printf("Could not find a working OpenGL 2.0 shader for permutation %s %s\n", shadermodeinfo[mode].vertexfilename, shadermodeinfo[mode].pretext);
+                                       r_glsl_permutation = R_GLSL_FindPermutation(mode, permutation);
+                                       qglUseProgramObjectARB(0);CHECKGLERROR
+                                       return; // no bit left to clear, entire mode is broken
                                }
                        }
                }
@@ -2041,63 +2041,56 @@ void R_SetupShader_SetPermutation(unsigned int mode, unsigned int permutation)
 
 void R_SetupGenericShader(qboolean usetexture)
 {
-       if (vid.support.arb_fragment_shader)
+       switch(vid.renderpath)
        {
-               if (r_glsl.integer && r_glsl_usegeneric.integer)
-                       R_SetupShader_SetPermutation(SHADERMODE_GENERIC, usetexture ? SHADERPERMUTATION_DIFFUSE : 0);
-               else if (r_glsl_permutation)
-               {
-                       r_glsl_permutation = NULL;
-                       qglUseProgramObjectARB(0);CHECKGLERROR
-               }
+       case RENDERPATH_GL20:
+               R_SetupShader_SetPermutation(SHADERMODE_GENERIC, usetexture ? SHADERPERMUTATION_DIFFUSE : 0);
+               break;
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               break;
        }
 }
 
 void R_SetupGenericTwoTextureShader(int texturemode)
 {
-       if (vid.support.arb_fragment_shader)
+       switch (vid.renderpath)
        {
-               if (r_glsl.integer && r_glsl_usegeneric.integer)
-                       R_SetupShader_SetPermutation(SHADERMODE_GENERIC, SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | (r_shadow_glossexact.integer ? SHADERPERMUTATION_EXACTSPECULARMATH : 0) | (texturemode == GL_MODULATE ? SHADERPERMUTATION_COLORMAPPING : (texturemode == GL_ADD ? SHADERPERMUTATION_GLOW : (texturemode == GL_DECAL ? SHADERPERMUTATION_VERTEXTEXTUREBLEND : 0))));
-               else if (r_glsl_permutation)
-               {
-                       r_glsl_permutation = NULL;
-                       qglUseProgramObjectARB(0);CHECKGLERROR
-               }
-       }
-       if (!r_glsl_permutation)
-       {
-               if (texturemode == GL_DECAL && gl_combine.integer)
-                       texturemode = GL_INTERPOLATE_ARB;
-               R_Mesh_TexCombine(1, texturemode, texturemode, 1, 1);
+       case RENDERPATH_GL20:
+               R_SetupShader_SetPermutation(SHADERMODE_GENERIC, SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | (r_shadow_glossexact.integer ? SHADERPERMUTATION_EXACTSPECULARMATH : 0) | (texturemode == GL_MODULATE ? SHADERPERMUTATION_COLORMAPPING : (texturemode == GL_ADD ? SHADERPERMUTATION_GLOW : (texturemode == GL_DECAL ? SHADERPERMUTATION_VERTEXTEXTUREBLEND : 0))));
+               break;
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               R_Mesh_TexCombine(1, GL_DECAL, GL_DECAL, 1, 1);
+               break;
        }
 }
 
 void R_SetupDepthOrShadowShader(void)
 {
-       if (vid.support.arb_fragment_shader)
+       switch (vid.renderpath)
        {
-               if (r_glsl.integer && r_glsl_usegeneric.integer)
-                       R_SetupShader_SetPermutation(SHADERMODE_DEPTH_OR_SHADOW, 0);
-               else if (r_glsl_permutation)
-               {
-                       r_glsl_permutation = NULL;
-                       qglUseProgramObjectARB(0);CHECKGLERROR
-               }
+       case RENDERPATH_GL20:
+               R_SetupShader_SetPermutation(SHADERMODE_DEPTH_OR_SHADOW, 0);
+               break;
+       case RENDERPATH_GL13:
+               break;
+       case RENDERPATH_GL11:
+               break;
        }
 }
 
 void R_SetupShowDepthShader(void)
 {
-       if (vid.support.arb_fragment_shader)
+       switch (vid.renderpath)
        {
-               if (r_glsl.integer && r_glsl_usegeneric.integer)
-                       R_SetupShader_SetPermutation(SHADERMODE_SHOWDEPTH, 0);
-               else if (r_glsl_permutation)
-               {
-                       r_glsl_permutation = NULL;
-                       qglUseProgramObjectARB(0);CHECKGLERROR
-               }
+       case RENDERPATH_GL20:
+               R_SetupShader_SetPermutation(SHADERMODE_SHOWDEPTH, 0);
+               break;
+       case RENDERPATH_GL13:
+               break;
+       case RENDERPATH_GL11:
+               break;
        }
 }
 
@@ -2951,8 +2944,36 @@ void R_Main_ResizeViewCache(void)
 
 void gl_main_start(void)
 {
-       r_loadnormalmap = r_loadgloss = vid.support.arb_texture_env_dot3 || vid.support.arb_fragment_shader;
-       r_loadfog = true;
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+               Cvar_SetValueQuick(&r_textureunits, vid.texunits);
+               Cvar_SetValueQuick(&gl_combine, 1);
+               Cvar_SetValueQuick(&r_glsl, 1);
+               r_loadnormalmap = true;
+               r_loadgloss = true;
+               r_loadfog = false;
+               break;
+       case RENDERPATH_GL13:
+               Cvar_SetValueQuick(&r_textureunits, vid.texunits);
+               Cvar_SetValueQuick(&gl_combine, 1);
+               Cvar_SetValueQuick(&r_glsl, 0);
+               r_loadnormalmap = false;
+               r_loadgloss = false;
+               r_loadfog = true;
+               break;
+       case RENDERPATH_GL11:
+               Cvar_SetValueQuick(&r_textureunits, vid.texunits);
+               Cvar_SetValueQuick(&gl_combine, 0);
+               Cvar_SetValueQuick(&r_glsl, 0);
+               r_loadnormalmap = false;
+               r_loadgloss = false;
+               r_loadfog = true;
+               break;
+       }
+
+       R_AnimCache_Free();
+       R_FrameData_Reset();
 
        r_numqueries = 0;
        r_maxqueries = 0;
@@ -2989,6 +3010,9 @@ void gl_main_start(void)
 extern rtexture_t *loadingscreentexture;
 void gl_main_shutdown(void)
 {
+       R_AnimCache_Free();
+       R_FrameData_Reset();
+
        R_Main_FreeViewCache();
 
        if (r_maxqueries)
@@ -3052,6 +3076,8 @@ void gl_main_newmap(void)
                        CL_ParseEntityLump(cl.worldmodel->brush.entities);
        }
        R_Main_FreeViewCache();
+
+       R_FrameData_Reset();
 }
 
 void GL_Main_Init(void)
@@ -3084,7 +3110,6 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_equalize_entities_minambient);
        Cvar_RegisterVariable(&r_equalize_entities_by);
        Cvar_RegisterVariable(&r_equalize_entities_to);
-       Cvar_RegisterVariable(&r_animcache);
        Cvar_RegisterVariable(&r_depthfirst);
        Cvar_RegisterVariable(&r_useinfinitefarclip);
        Cvar_RegisterVariable(&r_farclip_base);
@@ -3127,6 +3152,7 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_fog_exp2);
        Cvar_RegisterVariable(&r_drawfog);
        Cvar_RegisterVariable(&r_textureunits);
+       Cvar_RegisterVariable(&gl_combine);
        Cvar_RegisterVariable(&r_glsl);
        Cvar_RegisterVariable(&r_glsl_deluxemapping);
        Cvar_RegisterVariable(&r_glsl_offsetmapping);
@@ -3137,7 +3163,6 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_glsl_postprocess_uservec2);
        Cvar_RegisterVariable(&r_glsl_postprocess_uservec3);
        Cvar_RegisterVariable(&r_glsl_postprocess_uservec4);
-       Cvar_RegisterVariable(&r_glsl_usegeneric);
        Cvar_RegisterVariable(&r_water);
        Cvar_RegisterVariable(&r_water_resolutionmultiplier);
        Cvar_RegisterVariable(&r_water_clippingplanebias);
@@ -3164,6 +3189,7 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_test);
        Cvar_RegisterVariable(&r_batchmode);
        Cvar_RegisterVariable(&r_glsl_saturation);
+       Cvar_RegisterVariable(&r_framedatasize);
        if (gamemode == GAME_NEHAHRA || gamemode == GAME_TENEBRAE)
                Cvar_SetValue("r_fullbrights", 0);
        R_RegisterModule("GL_Main", gl_main_start, gl_main_shutdown, gl_main_newmap);
@@ -3337,6 +3363,69 @@ int R_CullBoxCustomPlanes(const vec3_t mins, const vec3_t maxs, int numplanes, c
 
 //==================================================================================
 
+// LordHavoc: this stores temporary data used within the same frame
+
+qboolean r_framedata_failed;
+static size_t r_framedata_size;
+static size_t r_framedata_current;
+static void *r_framedata_base;
+
+void R_FrameData_Reset(void)
+{
+       if (r_framedata_base);
+               Mem_Free(r_framedata_base);
+       r_framedata_base = NULL;
+       r_framedata_size = 0;
+       r_framedata_current = 0;
+}
+
+void R_FrameData_NewFrame(void)
+{
+       size_t wantedsize;
+       if (r_framedata_failed)
+               Cvar_SetValueQuick(&r_framedatasize, r_framedatasize.value * 1.25f);
+       wantedsize = (size_t)(r_framedatasize.value * 1024*1024);
+       wantedsize = bound(65536, wantedsize, 128*1024*1024);
+       if (r_framedata_size < wantedsize)
+       {
+               r_framedata_size = wantedsize;
+               if (!r_framedata_base)
+                       r_framedata_base = Mem_Alloc(r_main_mempool, r_framedata_size);
+       }
+       r_framedata_current = 0;
+       r_framedata_failed = false;
+}
+
+void *R_FrameData_Alloc(size_t size)
+{
+       void *data;
+
+       // align to 16 byte boundary
+       size = (size + 15) & ~15;
+       data = r_framedata_base + r_framedata_current;
+       r_framedata_current += size;
+
+       // check overflow
+       if (r_framedata_current > r_framedata_size)
+               r_framedata_failed = true;
+
+       // return NULL on everything after a failure
+       if (r_framedata_failed)
+               return NULL;
+
+       return data;
+}
+
+void *R_FrameData_Store(size_t size, void *data)
+{
+       void *d = R_FrameData_Alloc(size);
+       if (d)
+               memcpy(d, data, size);
+       return d;
+}
+
+//==================================================================================
+
 // LordHavoc: animcache written by Echon, refactored and reformatted by me
 
 /**
@@ -3351,9 +3440,6 @@ typedef struct r_animcache_entity_s
        float *normal3f;
        float *svector3f;
        float *tvector3f;
-       int maxvertices;
-       qboolean wantnormals;
-       qboolean wanttangents;
 }
 r_animcache_entity_t;
 
@@ -3369,115 +3455,108 @@ static r_animcache_t r_animcachestate;
 
 void R_AnimCache_Free(void)
 {
-       int idx;
-       for (idx=0 ; idx<r_animcachestate.maxindex ; idx++)
-       {
-               r_animcachestate.entity[idx].maxvertices = 0;
-               Mem_Free(r_animcachestate.entity[idx].vertex3f);
-               r_animcachestate.entity[idx].vertex3f = NULL;
-               r_animcachestate.entity[idx].normal3f = NULL;
-               r_animcachestate.entity[idx].svector3f = NULL;
-               r_animcachestate.entity[idx].tvector3f = NULL;
-       }
-       r_animcachestate.currentindex = 0;
-       r_animcachestate.maxindex = 0;
+       memset(&r_animcachestate, 0, sizeof(r_animcachestate));
 }
 
-void R_AnimCache_ResizeEntityCache(const int cacheIdx, const int numvertices)
-{
-       int arraySize;
-       float *base;
-       r_animcache_entity_t *cache = &r_animcachestate.entity[cacheIdx];
-
-       if (cache->maxvertices >= numvertices)
-               return;
-
-       // Release existing memory
-       if (cache->vertex3f)
-               Mem_Free(cache->vertex3f);
-
-       // Pad by 1024 verts
-       cache->maxvertices = (numvertices + 1023) & ~1023;
-       arraySize = cache->maxvertices * 3;
-
-       // Allocate, even if we don't need this memory in this instance it will get ignored and potentially used later
-       base = (float *)Mem_Alloc(r_main_mempool, arraySize * sizeof(float) * 4);
-       r_animcachestate.entity[cacheIdx].vertex3f = base;
-       r_animcachestate.entity[cacheIdx].normal3f = base + arraySize;
-       r_animcachestate.entity[cacheIdx].svector3f = base + arraySize*2;
-       r_animcachestate.entity[cacheIdx].tvector3f = base + arraySize*3;
-
-//     Con_Printf("allocated cache for %i (%f KB)\n", cacheIdx, (arraySize*sizeof(float)*4)/1024.0f);
-}
-
-void R_AnimCache_NewFrame(void)
+void R_AnimCache_ClearCache(void)
 {
        int i;
+       entity_render_t *ent;
 
-       if (r_animcache.integer && r_drawentities.integer)
-               r_animcachestate.maxindex = sizeof(r_animcachestate.entity) / sizeof(r_animcachestate.entity[0]);
-       else if (r_animcachestate.maxindex)
-               R_AnimCache_Free();
-
+       r_animcachestate.maxindex = sizeof(r_animcachestate.entity) / sizeof(r_animcachestate.entity[0]);
        r_animcachestate.currentindex = 0;
 
        for (i = 0;i < r_refdef.scene.numentities;i++)
-               r_refdef.scene.entities[i]->animcacheindex = -1;
+       {
+               ent = r_refdef.scene.entities[i];
+               ent->animcacheindex = -1;
+       }
 }
 
 qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qboolean wanttangents)
 {
        dp_model_t *model = ent->model;
        r_animcache_entity_t *c;
+       int numvertices;
        // see if it's already cached this frame
        if (ent->animcacheindex >= 0)
        {
                // add normals/tangents if needed
-               c = r_animcachestate.entity + ent->animcacheindex;
-               if (c->wantnormals)
-                       wantnormals = false;
-               if (c->wanttangents)
-                       wanttangents = false;
                if (wantnormals || wanttangents)
-                       model->AnimateVertices(model, ent->frameblend, ent->skeleton, NULL, wantnormals ? c->normal3f : NULL, wanttangents ? c->svector3f : NULL, wanttangents ? c->tvector3f : NULL);
+               {
+                       c = r_animcachestate.entity + ent->animcacheindex;
+                       if (c->normal3f)
+                               wantnormals = false;
+                       if (c->svector3f)
+                               wanttangents = false;
+                       if (wantnormals || wanttangents)
+                       {
+                               numvertices = model->surfmesh.num_vertices;
+                               if (wantnormals)
+                                       c->normal3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
+                               if (wanttangents)
+                               {
+                                       c->svector3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
+                                       c->tvector3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
+                               }
+                               if (!r_framedata_failed)
+                                       model->AnimateVertices(model, ent->frameblend, ent->skeleton, NULL, wantnormals ? c->normal3f : NULL, wanttangents ? c->svector3f : NULL, wanttangents ? c->tvector3f : NULL);
+                       }
+               }
        }
        else
        {
                // see if this ent is worth caching
                if (r_animcachestate.maxindex <= r_animcachestate.currentindex)
                        return false;
-               if (!model || !model->Draw || !model->surfmesh.isanimated || !model->AnimateVertices || (ent->frameblend[0].lerp == 1 && ent->frameblend[0].subframe == 0))
+               if (!model || !model->Draw || !model->surfmesh.isanimated || !model->AnimateVertices || (ent->frameblend[0].lerp == 1 && ent->frameblend[0].subframe == 0 && !ent->skeleton))
                        return false;
-               // assign it a cache entry and make sure the arrays are big enough
-               R_AnimCache_ResizeEntityCache(r_animcachestate.currentindex, model->surfmesh.num_vertices);
+               // assign it a cache entry and get some temp memory
                ent->animcacheindex = r_animcachestate.currentindex++;
                c = r_animcachestate.entity + ent->animcacheindex;
-               c->wantnormals = wantnormals;
-               c->wanttangents = wanttangents;
-               model->AnimateVertices(model, ent->frameblend, ent->skeleton, c->vertex3f, wantnormals ? c->normal3f : NULL, wanttangents ? c->svector3f : NULL, wanttangents ? c->tvector3f : NULL);
+               numvertices = model->surfmesh.num_vertices;
+               memset(c, 0, sizeof(*c));
+               c->vertex3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
+               if (wantnormals)
+                       c->normal3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
+               if (wanttangents)
+               {
+                       c->svector3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
+                       c->tvector3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
+               }
+               if (!r_framedata_failed)
+                       model->AnimateVertices(model, ent->frameblend, ent->skeleton, c->vertex3f, c->normal3f, c->svector3f, c->tvector3f);
        }
-       return true;
+       return !r_framedata_failed;
 }
 
 void R_AnimCache_CacheVisibleEntities(void)
 {
        int i;
-       qboolean wantnormals;
-       qboolean wanttangents;
-
-       if (!r_animcachestate.maxindex)
-               return;
+       entity_render_t *ent;
+       qboolean wantnormals = !r_showsurfaces.integer;
+       qboolean wanttangents = !r_showsurfaces.integer;
 
-       wantnormals = !r_showsurfaces.integer;
-       wanttangents = !r_showsurfaces.integer && (r_glsl.integer || r_refdef.scene.rtworld || r_refdef.scene.rtdlight);
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+               break;
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               wanttangents = false;
+               break;
+       }
 
-       // TODO: thread this?
+       // TODO: thread this
 
        for (i = 0;i < r_refdef.scene.numentities;i++)
        {
                if (!r_refdef.viewcache.entityvisible[i])
                        continue;
-               R_AnimCache_GetEntity(r_refdef.scene.entities[i], wantnormals, wanttangents);
+               ent = r_refdef.scene.entities[i];
+               if (ent->animcacheindex >= 0)
+                       continue;
+               R_AnimCache_GetEntity(ent, wantnormals, wanttangents);
        }
 }
 
@@ -4017,6 +4096,18 @@ static void R_Water_StartFrame(void)
        int waterwidth, waterheight, texturewidth, textureheight;
        r_waterstate_waterplane_t *p;
 
+       if (vid.width > (int)vid.maxtexturesize_2d || vid.height > (int)vid.maxtexturesize_2d)
+               return;
+
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+               break;
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               return;
+       }
+
        // set waterwidth and waterheight to the water resolution that will be
        // used (often less than the screen resolution for faster rendering)
        waterwidth = (int)bound(1, vid.width * r_water_resolutionmultiplier.value, vid.width);
@@ -4024,7 +4115,7 @@ static void R_Water_StartFrame(void)
 
        // calculate desired texture sizes
        // can't use water if the card does not support the texture size
-       if (!r_water.integer || !r_glsl.integer || !vid.support.arb_fragment_shader || waterwidth > (int)vid.maxtexturesize_2d || waterheight > (int)vid.maxtexturesize_2d || r_showsurfaces.integer)
+       if (!r_water.integer || r_showsurfaces.integer)
                texturewidth = textureheight = waterwidth = waterheight = 0;
        else if (vid.support.arb_texture_non_power_of_two)
        {
@@ -4250,6 +4341,15 @@ void R_Bloom_StartFrame(void)
 {
        int bloomtexturewidth, bloomtextureheight, screentexturewidth, screentextureheight;
 
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+               break;
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               return;
+       }
+
        // set bloomwidth and bloomheight to the bloom resolution that will be
        // used (often less than the screen resolution for faster rendering)
        r_bloomstate.bloomwidth = bound(1, r_bloom_resolution.integer, vid.height);
@@ -4282,7 +4382,7 @@ void R_Bloom_StartFrame(void)
                Cvar_SetValueQuick(&r_damageblur, 0);
        }
 
-       if (!(r_glsl.integer && (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)))
                screentexturewidth = screentextureheight = 0;
        if (!r_hdr.integer && !r_bloom.integer)
                bloomtexturewidth = bloomtextureheight = 0;
@@ -4552,72 +4652,77 @@ void R_HDR_RenderBloomTexture(void)
 
 static void R_BlendView(void)
 {
-       if (r_bloomstate.texture_screen)
-       {
-               // make sure the buffer is available
-               if (r_bloom_blur.value < 1) { Cvar_SetValueQuick(&r_bloom_blur, 1); }
-
-               R_ResetViewRendering2D();
-               R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen));
-               GL_ActiveTexture(0);CHECKGLERROR
-
-               if(!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0))
-               {  
-                       // declare variables
-                       float speed;
-                       static float avgspeed;
-
-                       speed = VectorLength(cl.movement_velocity);
-
-                       cl.motionbluralpha = bound(0, (cl.time - cl.oldtime) / max(0.001, r_motionblur_vcoeff.value), 1);
-                       avgspeed = avgspeed * (1 - cl.motionbluralpha) + speed * cl.motionbluralpha;
-
-                       speed = (avgspeed - r_motionblur_vmin.value) / max(1, r_motionblur_vmax.value - r_motionblur_vmin.value);
-                       speed = bound(0, speed, 1);
-                       speed = speed * (1 - r_motionblur_bmin.value) + r_motionblur_bmin.value;
-
-                       // calculate values into a standard alpha
-                       cl.motionbluralpha = 1 - exp(-
-                                       (
-                                        (r_motionblur.value * speed / 80)
-                                        +
-                                        (r_damageblur.value * (cl.cshifts[CSHIFT_DAMAGE].percent / 1600))
-                                       )
-                                       /
-                                       max(0.0001, cl.time - cl.oldtime) // fps independent
-                                  );
-
-                       cl.motionbluralpha *= lhrandom(1 - r_motionblur_randomize.value, 1 + r_motionblur_randomize.value);
-                       cl.motionbluralpha = bound(0, cl.motionbluralpha, r_motionblur_maxblur.value);
-                       // apply the blur
-                       if (cl.motionbluralpha > 0)
-                       {
-                               R_SetupGenericShader(true);
-                               GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-                               GL_Color(1, 1, 1, cl.motionbluralpha);
-                               R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen));
-                               R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
-                               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
-                               r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
-                       }
-               }
-
-               // copy view into the screen texture
-               qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);CHECKGLERROR
-               r_refdef.stats.bloom_copypixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
-       }
+       unsigned int permutation;
 
-       if (r_glsl.integer && vid.support.arb_fragment_shader && (r_bloomstate.texture_screen || r_bloomstate.texture_bloom))
+       switch (vid.renderpath)
        {
-               unsigned int permutation =
+       case RENDERPATH_GL20:
+               permutation =
                          (r_bloomstate.texture_bloom ? SHADERPERMUTATION_BLOOM : 0)
                        | (r_refdef.viewblend[3] > 0 ? SHADERPERMUTATION_VIEWTINT : 0)
                        | ((v_glslgamma.value && !vid_gammatables_trivial) ? SHADERPERMUTATION_GAMMARAMPS : 0)
                        | (r_glsl_postprocess.integer ? SHADERPERMUTATION_POSTPROCESSING : 0)
                        | ((!R_Stereo_ColorMasking() && r_glsl_saturation.value != 1) ? SHADERPERMUTATION_SATURATION : 0);
 
+               if (r_bloomstate.texture_screen)
+               {
+                       // make sure the buffer is available
+                       if (r_bloom_blur.value < 1) { Cvar_SetValueQuick(&r_bloom_blur, 1); }
+
+                       R_ResetViewRendering2D();
+                       R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
+                       R_Mesh_ColorPointer(NULL, 0, 0);
+                       R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen));
+                       GL_ActiveTexture(0);CHECKGLERROR
+
+                       if(!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0))
+                       {
+                               // declare variables
+                               float speed;
+                               static float avgspeed;
+
+                               speed = VectorLength(cl.movement_velocity);
+
+                               cl.motionbluralpha = bound(0, (cl.time - cl.oldtime) / max(0.001, r_motionblur_vcoeff.value), 1);
+                               avgspeed = avgspeed * (1 - cl.motionbluralpha) + speed * cl.motionbluralpha;
+
+                               speed = (avgspeed - r_motionblur_vmin.value) / max(1, r_motionblur_vmax.value - r_motionblur_vmin.value);
+                               speed = bound(0, speed, 1);
+                               speed = speed * (1 - r_motionblur_bmin.value) + r_motionblur_bmin.value;
+
+                               // calculate values into a standard alpha
+                               cl.motionbluralpha = 1 - exp(-
+                                               (
+                                                (r_motionblur.value * speed / 80)
+                                                +
+                                                (r_damageblur.value * (cl.cshifts[CSHIFT_DAMAGE].percent / 1600))
+                                               )
+                                               /
+                                               max(0.0001, cl.time - cl.oldtime) // fps independent
+                                          );
+
+                               cl.motionbluralpha *= lhrandom(1 - r_motionblur_randomize.value, 1 + r_motionblur_randomize.value);
+                               cl.motionbluralpha = bound(0, cl.motionbluralpha, r_motionblur_maxblur.value);
+                               // apply the blur
+                               if (cl.motionbluralpha > 0)
+                               {
+                                       R_SetupGenericShader(true);
+                                       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                                       GL_Color(1, 1, 1, cl.motionbluralpha);
+                                       R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen));
+                                       R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
+                                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+                                       r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
+                               }
+                       }
+
+                       // copy view into the screen texture
+                       qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);CHECKGLERROR
+                       r_refdef.stats.bloom_copypixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
+               }
+               else if (!r_bloomstate.texture_bloom)
+                       break; // no screen processing, no bloom, skip it
+
                if (r_bloomstate.texture_bloom && !r_bloomstate.hdr)
                {
                        // render simple bloom effect
@@ -4676,73 +4781,21 @@ static void R_BlendView(void)
                        qglUniform1fARB(r_glsl_permutation->loc_Saturation, r_glsl_saturation.value);
                R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
                r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
-               return;
-       }
-
-
-
-       if (r_bloomstate.texture_bloom && r_bloomstate.hdr)
-       {
-               // render high dynamic range bloom effect
-               // the bloom texture was made earlier this render, so we just need to
-               // blend it onto the screen...
-               R_ResetViewRendering2D();
-               R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               R_SetupGenericShader(true);
-               GL_Color(1, 1, 1, 1);
-               GL_BlendFunc(GL_ONE, GL_ONE);
-               R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
-               R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
-               r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
-       }
-       else if (r_bloomstate.texture_bloom)
-       {
-               // render simple bloom effect
-               // copy the screen and shrink it and darken it for the bloom process
-               R_Bloom_CopyBloomTexture(r_bloom_colorscale.value);
-               // make the bloom texture
-               R_Bloom_MakeTexture();
-               // put the original screen image back in place and blend the bloom
-               // texture on it
-               R_ResetViewRendering2D();
-               R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               GL_Color(1, 1, 1, 1);
-               GL_BlendFunc(GL_ONE, GL_ZERO);
-               // do both in one pass if possible
-               R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
-               R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
-               if (r_textureunits.integer >= 2 && gl_combine.integer)
-               {
-                       R_SetupGenericTwoTextureShader(GL_ADD);
-                       R_Mesh_TexBind(1, R_GetTexture(r_bloomstate.texture_screen));
-                       R_Mesh_TexCoordPointer(1, 2, r_bloomstate.screentexcoord2f, 0, 0);
-               }
-               else
+               break;
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               if (r_refdef.viewblend[3] >= (1.0f / 256.0f))
                {
-                       R_SetupGenericShader(true);
+                       // apply a color tint to the whole view
+                       R_ResetViewRendering2D();
+                       R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
+                       R_Mesh_ColorPointer(NULL, 0, 0);
+                       R_SetupGenericShader(false);
+                       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                       GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
                        R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
-                       r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
-                       // now blend on the bloom texture
-                       GL_BlendFunc(GL_ONE, GL_ONE);
-                       R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen));
-                       R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
                }
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
-               r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
-       }
-       if (r_refdef.viewblend[3] >= (1.0f / 256.0f))
-       {
-               // apply a color tint to the whole view
-               R_ResetViewRendering2D();
-               R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               R_SetupGenericShader(false);
-               GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-               GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+               break;
        }
 }
 
@@ -4862,39 +4915,47 @@ void R_UpdateVariables(void)
        else
                r_refdef.fogenabled = false;
 
-       if(r_glsl.integer && v_glslgamma.integer && !vid_gammatables_trivial)
+       switch(vid.renderpath)
        {
-               if(!r_texture_gammaramps || vid_gammatables_serial != r_texture_gammaramps_serial)
+       case RENDERPATH_GL20:
+               if(v_glslgamma.integer && !vid_gammatables_trivial)
                {
-                       // build GLSL gamma texture
+                       if(!r_texture_gammaramps || vid_gammatables_serial != r_texture_gammaramps_serial)
+                       {
+                               // build GLSL gamma texture
 #define RAMPWIDTH 256
-                       unsigned short ramp[RAMPWIDTH * 3];
-                       unsigned char rampbgr[RAMPWIDTH][4];
-                       int i;
+                               unsigned short ramp[RAMPWIDTH * 3];
+                               unsigned char rampbgr[RAMPWIDTH][4];
+                               int i;
 
-                       r_texture_gammaramps_serial = vid_gammatables_serial;
+                               r_texture_gammaramps_serial = vid_gammatables_serial;
 
-                       VID_BuildGammaTables(&ramp[0], RAMPWIDTH);
-                       for(i = 0; i < RAMPWIDTH; ++i)
-                       {
-                               rampbgr[i][0] = (unsigned char) (ramp[i + 2 * RAMPWIDTH] * 255.0 / 65535.0 + 0.5);
-                               rampbgr[i][1] = (unsigned char) (ramp[i + RAMPWIDTH] * 255.0 / 65535.0 + 0.5);
-                               rampbgr[i][2] = (unsigned char) (ramp[i] * 255.0 / 65535.0 + 0.5);
-                               rampbgr[i][3] = 0;
-                       }
-                       if (r_texture_gammaramps)
-                       {
-                               R_UpdateTexture(r_texture_gammaramps, &rampbgr[0][0], 0, 0, RAMPWIDTH, 1);
-                       }
-                       else
-                       {
-                               r_texture_gammaramps = R_LoadTexture2D(r_main_texturepool, "gammaramps", RAMPWIDTH, 1, &rampbgr[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT, NULL);
+                               VID_BuildGammaTables(&ramp[0], RAMPWIDTH);
+                               for(i = 0; i < RAMPWIDTH; ++i)
+                               {
+                                       rampbgr[i][0] = (unsigned char) (ramp[i + 2 * RAMPWIDTH] * 255.0 / 65535.0 + 0.5);
+                                       rampbgr[i][1] = (unsigned char) (ramp[i + RAMPWIDTH] * 255.0 / 65535.0 + 0.5);
+                                       rampbgr[i][2] = (unsigned char) (ramp[i] * 255.0 / 65535.0 + 0.5);
+                                       rampbgr[i][3] = 0;
+                               }
+                               if (r_texture_gammaramps)
+                               {
+                                       R_UpdateTexture(r_texture_gammaramps, &rampbgr[0][0], 0, 0, RAMPWIDTH, 1);
+                               }
+                               else
+                               {
+                                       r_texture_gammaramps = R_LoadTexture2D(r_main_texturepool, "gammaramps", RAMPWIDTH, 1, &rampbgr[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT, NULL);
+                               }
                        }
                }
-       }
-       else
-       {
-               // remove GLSL gamma texture
+               else
+               {
+                       // remove GLSL gamma texture
+               }
+               break;
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               break;
        }
 }
 
@@ -4942,7 +5003,8 @@ void R_RenderView(void)
        r_frame++; // used only by R_GetCurrentTexture
        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 
-       R_AnimCache_NewFrame();
+       R_AnimCache_ClearCache();
+       R_FrameData_NewFrame();
 
        if (r_refdef.view.isoverlay)
        {
@@ -5094,6 +5156,7 @@ void R_RenderScene(void)
        }
 
        R_AnimCache_CacheVisibleEntities();
+       R_PrepareRTLights();
 
        if (r_depthfirst.integer >= 1 && cl.csqc_vidvars.drawworld && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->DrawDepth)
        {
@@ -7897,13 +7960,22 @@ static void R_DrawWorldTextureSurfaceList(int texturenumsurfaces, const msurface
        CHECKGLERROR
        RSurf_SetupDepthAndCulling();
        if (r_showsurfaces.integer == 3)
+       {
                R_DrawTextureSurfaceList_ShowSurfaces3(texturenumsurfaces, texturesurfacelist, writedepth);
-       else if (r_glsl.integer && vid.support.arb_fragment_shader)
+               return;
+       }
+       switch (vid.renderpath)
+       {
+       case RENDERPATH_GL20:
                R_DrawTextureSurfaceList_GL20(texturenumsurfaces, texturesurfacelist, writedepth);
-       else if (gl_combine.integer && r_textureunits.integer >= 2)
+               break;
+       case RENDERPATH_GL13:
                R_DrawTextureSurfaceList_GL13(texturenumsurfaces, texturesurfacelist, writedepth);
-       else
+               break;
+       case RENDERPATH_GL11:
                R_DrawTextureSurfaceList_GL11(texturenumsurfaces, texturesurfacelist, writedepth);
+               break;
+       }
        CHECKGLERROR
 }
 
@@ -7912,13 +7984,22 @@ static void R_DrawModelTextureSurfaceList(int texturenumsurfaces, const msurface
        CHECKGLERROR
        RSurf_SetupDepthAndCulling();
        if (r_showsurfaces.integer == 3)
+       {
                R_DrawTextureSurfaceList_ShowSurfaces3(texturenumsurfaces, texturesurfacelist, writedepth);
-       else if (r_glsl.integer && vid.support.arb_fragment_shader)
+               return;
+       }
+       switch (vid.renderpath)
+       {
+       case RENDERPATH_GL20:
                R_DrawTextureSurfaceList_GL20(texturenumsurfaces, texturesurfacelist, writedepth);
-       else if (gl_combine.integer && r_textureunits.integer >= 2)
+               break;
+       case RENDERPATH_GL13:
                R_DrawTextureSurfaceList_GL13(texturenumsurfaces, texturesurfacelist, writedepth);
-       else
+               break;
+       case RENDERPATH_GL11:
                R_DrawTextureSurfaceList_GL11(texturenumsurfaces, texturesurfacelist, writedepth);
+               break;
+       }
        CHECKGLERROR
 }
 
@@ -7938,7 +8019,18 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
        else if (r_showsurfaces.integer && r_showsurfaces.integer != 3)
                RSurf_ActiveModelEntity(ent, false, false);
        else
-               RSurf_ActiveModelEntity(ent, true, r_glsl.integer && vid.support.arb_fragment_shader);
+       {
+               switch (vid.renderpath)
+               {
+               case RENDERPATH_GL20:
+                       RSurf_ActiveModelEntity(ent, true, true);
+                       break;
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL11:
+                       RSurf_ActiveModelEntity(ent, true, false);
+                       break;
+               }
+       }
 
        for (i = 0;i < numsurfaces;i = j)
        {
@@ -9108,7 +9200,18 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
        else if (depthonly)
                RSurf_ActiveModelEntity(ent, false, false);
        else
-               RSurf_ActiveModelEntity(ent, true, r_glsl.integer && vid.support.arb_fragment_shader);
+       {
+               switch (vid.renderpath)
+               {
+               case RENDERPATH_GL20:
+                       RSurf_ActiveModelEntity(ent, true, true);
+                       break;
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL11:
+                       RSurf_ActiveModelEntity(ent, true, false);
+                       break;
+               }
+       }
 
        surfaces = model->data_surfaces;
        update = model->brushq1.lightmapupdateflags;