merged static and dynamic rtlight handling almost entirely (even uses DrawShadowVolum...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 13 Mar 2004 23:30:20 +0000 (23:30 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 13 Mar 2004 23:30:20 +0000 (23:30 +0000)
added a surfacelist to model_t containing surface indices for each submodel
merged R_Shadow_DiffuseLighting and R_Shadow_SpecularLighting into R_Shadow_RenderLighting (which takes a lighting parameter containing LIGHTING_DIFFUSE and LIGHTING_SPECULAR flags)
commented out cullradius/cullradius2 in rtlight structs because they weren't used

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@4015 d7cf8633-e32d-0410-b094-e92efae38249

client.h
gl_models.c
gl_rsurf.c
model_alias.c
model_brush.c
model_shared.h
r_shadow.c
r_shadow.h
todo

index 870e612..bd4689b 100644 (file)
--- a/client.h
+++ b/client.h
@@ -104,9 +104,9 @@ typedef struct rtlight_s
        vec3_t cullmins;
        vec3_t cullmaxs;
        // culling
-       vec_t cullradius;
+       //vec_t cullradius;
        // squared cullradius
-       vec_t cullradius2;
+       //vec_t cullradius2;
 
        // lightmap renderer stuff (remove someday!)
        // the size of the light
@@ -128,7 +128,9 @@ typedef struct rtlight_s
        shadowmesh_t *static_meshchain_light;
        // used for visibility testing (more exact than bbox)
        int static_numclusters;
-       int *static_clusterindices;
+       int static_numclusterpvsbytes;
+       int *static_clusterlist;
+       qbyte *static_clusterpvs;
 }
 rtlight_t;
 
index 6670647..223be1b 100644 (file)
@@ -380,7 +380,7 @@ void R_Model_Alias_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, v
                        if (layer->flags & ALIASLAYER_SPECULAR)
                        {
                                c_alias_polys += mesh->num_triangles;
-                               R_Shadow_SpecularLighting(mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, aliasvert_vertex3f, aliasvert_svector3f, aliasvert_tvector3f, aliasvert_normal3f, mesh->data_texcoord2f, relativelightorigin, relativeeyeorigin, lightcolor2, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, layer->texture, layer->nmap, lightcubemap);
+                               R_Shadow_RenderLighting(mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, aliasvert_vertex3f, aliasvert_svector3f, aliasvert_tvector3f, aliasvert_normal3f, mesh->data_texcoord2f, relativelightorigin, relativeeyeorigin, lightcolor2, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, layer->texture, layer->nmap, layer->texture, lightcubemap, LIGHTING_SPECULAR);
                        }
                        else if (layer->flags & ALIASLAYER_DIFFUSE)
                        {
@@ -409,7 +409,7 @@ void R_Model_Alias_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, v
                                        lightcolor2[2] *= bcolor[2] * (1.0f / 255.0f);
                                }
                                c_alias_polys += mesh->num_triangles;
-                               R_Shadow_DiffuseLighting(mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, aliasvert_vertex3f, aliasvert_svector3f, aliasvert_tvector3f, aliasvert_normal3f, mesh->data_texcoord2f, relativelightorigin, lightcolor2, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, layer->texture, layer->nmap, lightcubemap);
+                               R_Shadow_RenderLighting(mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, aliasvert_vertex3f, aliasvert_svector3f, aliasvert_tvector3f, aliasvert_normal3f, mesh->data_texcoord2f, relativelightorigin, relativeeyeorigin, lightcolor2, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, layer->texture, layer->nmap, layer->texture, lightcubemap, LIGHTING_DIFFUSE);
                        }
                }
        }
index cabf4bb..ffd263d 100644 (file)
@@ -1833,105 +1833,72 @@ void R_Model_Brush_Draw(entity_render_t *ent)
        R_DrawSurfaces(ent, SHADERSTAGE_NORMAL, ent->model->brushq1.pvstexturechains);
 }
 
-void R_Model_Brush_DrawShadowVolume (entity_render_t *ent, vec3_t relativelightorigin, float lightradius)
+void R_Model_Brush_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outclusterlist, qbyte *outclusterpvs, int *outnumclusterspointer, int *outsurfacelist, qbyte *outsurfacepvs, int *outnumsurfacespointer)
 {
-#if 0
-       int i;
-       msurface_t *surf;
-       float projectdistance, f, temp[3], lightradius2;
-       if (ent->model == NULL)
-               return;
-       R_Mesh_Matrix(&ent->matrix);
-       lightradius2 = lightradius * lightradius;
-       R_UpdateTextureInfo(ent);
-       projectdistance = lightradius + ent->model->radius;//projectdistance = 1000000000.0f;//lightradius + ent->model->radius;
-       //projectdistance = 1000000000.0f;//lightradius + ent->model->radius;
-       for (i = 0, surf = ent->model->brushq1.surfaces + ent->model->brushq1.firstmodelsurface;i < ent->model->brushq1.nummodelsurfaces;i++, surf++)
-       {
-               if (surf->texinfo->texture->rendertype == SURFRENDER_OPAQUE && surf->flags & SURF_SHADOWCAST)
-               {
-                       f = PlaneDiff(relativelightorigin, surf->plane);
-                       if (surf->flags & SURF_PLANEBACK)
-                               f = -f;
-                       // draw shadows only for frontfaces and only if they are close
-                       if (f >= 0.1 && f < lightradius)
-                       {
-                               temp[0] = bound(surf->poly_mins[0], relativelightorigin[0], surf->poly_maxs[0]) - relativelightorigin[0];
-                               temp[1] = bound(surf->poly_mins[1], relativelightorigin[1], surf->poly_maxs[1]) - relativelightorigin[1];
-                               temp[2] = bound(surf->poly_mins[2], relativelightorigin[2], surf->poly_maxs[2]) - relativelightorigin[2];
-                               if (DotProduct(temp, temp) < lightradius2)
-                                       R_Shadow_VolumeFromSphere(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_vertex3f, surf->mesh.data_element3i, surf->mesh.data_neighbor3i, relativelightorigin, projectdistance, lightradius);
-                       }
-               }
-       }
-#else
-       int t, leafnum, marksurfnum, trianglenum;
+       model_t *model = ent->model;
+       vec3_t lightmins, lightmaxs;
+       int t, leafindex, marksurfaceindex, surfaceindex, triangleindex, outnumclusters = 0, outnumsurfaces = 0;
        const int *e;
-       msurface_t *surf;
+       const float *v[3];
+       msurface_t *surface;
        mleaf_t *leaf;
        const qbyte *pvs;
-       float projectdistance;
-       const float *v[3];
-       vec3_t lightmins, lightmaxs;
-       if (ent->model == NULL)
-               return;
-       R_Mesh_Matrix(&ent->matrix);
-       R_UpdateTextureInfo(ent);
-       projectdistance = lightradius + ent->model->radius;//projectdistance = 1000000000.0f;//lightradius + ent->model->radius;
        lightmins[0] = relativelightorigin[0] - lightradius;
        lightmins[1] = relativelightorigin[1] - lightradius;
        lightmins[2] = relativelightorigin[2] - lightradius;
        lightmaxs[0] = relativelightorigin[0] + lightradius;
        lightmaxs[1] = relativelightorigin[1] + lightradius;
        lightmaxs[2] = relativelightorigin[2] + lightradius;
-       /*
-       R_Shadow_PrepareShadowMark(ent->model->brush.shadowmesh->numtriangles);
-       maxmarksurfaces = sizeof(surfacelist) / sizeof(surfacelist[0]);
-       ent->model->brushq1.GetVisible(ent->model, relativelightorigin, lightmins, lightmaxs, 0, NULL, NULL, maxmarkleafs, markleaf, &nummarkleafs);
-       for (marksurfacenum = 0;marksurfacenum < nummarksurfaces;marksurfacenum++)
+       *outnumclusterspointer = 0;
+       *outnumsurfacespointer = 0;
+       memset(outclusterpvs, 0, model->brush.num_pvsclusterbytes);
+       memset(outsurfacepvs, 0, (model->numsurfaces + 7) >> 3);
+       if (model == NULL)
        {
-               surf = marksurface[marksurfacenum];
-               if (surf->shadowmark != shadowmarkcount)
-               {
-                       surf->shadowmark = shadowmarkcount;
-                       if (BoxesOverlap(lightmins, lightmaxs, surf->poly_mins, surf->poly_maxs) && surf->texinfo->texture->rendertype == SURFRENDER_OPAQUE && (surf->flags & SURF_SHADOWCAST))
+               VectorCopy(lightmins, outmins);
+               VectorCopy(lightmaxs, outmaxs);
+               return;
+       }
+       VectorCopy(relativelightorigin, outmins);
+       VectorCopy(relativelightorigin, outmaxs);
+       if (model->brush.GetPVS)
+               pvs = model->brush.GetPVS(model, relativelightorigin);
+       else
+               pvs = NULL;
+       // FIXME: use BSP recursion as lights are often small
+       for (leafindex = 0, leaf = model->brushq1.data_leafs;leafindex < model->brushq1.num_leafs;leafindex++, leaf++)
+       {
+               if (BoxesOverlap(lightmins, lightmaxs, leaf->mins, leaf->maxs) && (pvs == NULL || CHECKPVSBIT(pvs, leaf->clusterindex)))
+               {
+                       outmins[0] = min(outmins[0], leaf->mins[0]);
+                       outmins[1] = min(outmins[1], leaf->mins[1]);
+                       outmins[2] = min(outmins[2], leaf->mins[2]);
+                       outmaxs[0] = max(outmaxs[0], leaf->maxs[0]);
+                       outmaxs[1] = max(outmaxs[1], leaf->maxs[1]);
+                       outmaxs[2] = max(outmaxs[2], leaf->maxs[2]);
+                       if (!CHECKPVSBIT(outclusterpvs, leaf->clusterindex))
                        {
-                               for (trianglenum = 0, t = surf->num_firstshadowmeshtriangle, e = ent->model->brush.shadowmesh->element3i + t * 3;trianglenum < surf->mesh.num_triangles;trianglenum++, t++, e += 3)
-                               {
-                                       v[0] = ent->model->brush.shadowmesh->vertex3f + e[0] * 3;
-                                       v[1] = ent->model->brush.shadowmesh->vertex3f + e[1] * 3;
-                                       v[2] = ent->model->brush.shadowmesh->vertex3f + e[2] * 3;
-                                       if (PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]) && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
-                                               shadowmarklist[numshadowmark++] = t;
-                               }
+                               SETPVSBIT(outclusterpvs, leaf->clusterindex);
+                               outclusterlist[outnumclusters++] = leaf->clusterindex;
                        }
-               }
-       }
-       */
-       R_Shadow_PrepareShadowMark(ent->model->brush.shadowmesh->numtriangles);
-       if (ent->model->brush.GetPVS && (pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin)))
-       {
-               pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin);
-               // FIXME: use BSP recursion in q1bsp as dlights are often small
-               for (leafnum = 0, leaf = ent->model->brushq1.data_leafs;leafnum < ent->model->brushq1.num_leafs;leafnum++, leaf++)
-               {
-                       if (BoxesOverlap(lightmins, lightmaxs, leaf->mins, leaf->maxs) && CHECKPVSBIT(pvs, leaf->clusterindex))
+                       for (marksurfaceindex = 0;marksurfaceindex < leaf->nummarksurfaces;marksurfaceindex++)
                        {
-                               for (marksurfnum = 0;marksurfnum < leaf->nummarksurfaces;marksurfnum++)
+                               surfaceindex = leaf->firstmarksurface[marksurfaceindex];
+                               if (!CHECKPVSBIT(outsurfacepvs, surfaceindex))
                                {
-                                       surf = ent->model->brushq1.surfaces + leaf->firstmarksurface[marksurfnum];
-                                       if (surf->shadowmark != shadowmarkcount)
+                                       surface = model->brushq1.surfaces + surfaceindex;
+                                       if (BoxesOverlap(lightmins, lightmaxs, surface->poly_mins, surface->poly_maxs))
                                        {
-                                               surf->shadowmark = shadowmarkcount;
-                                               if (BoxesOverlap(lightmins, lightmaxs, surf->poly_mins, surf->poly_maxs) && surf->texinfo->texture->rendertype == SURFRENDER_OPAQUE && (surf->flags & SURF_SHADOWCAST))
+                                               for (triangleindex = 0, t = surface->num_firstshadowmeshtriangle, e = model->brush.shadowmesh->element3i + t * 3;triangleindex < surface->mesh.num_triangles;triangleindex++, t++, e += 3)
                                                {
-                                                       for (trianglenum = 0, t = surf->num_firstshadowmeshtriangle, e = ent->model->brush.shadowmesh->element3i + t * 3;trianglenum < surf->mesh.num_triangles;trianglenum++, t++, e += 3)
+                                                       v[0] = model->brush.shadowmesh->vertex3f + e[0] * 3;
+                                                       v[1] = model->brush.shadowmesh->vertex3f + e[1] * 3;
+                                                       v[2] = model->brush.shadowmesh->vertex3f + e[2] * 3;
+                                                       if (PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]) && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
                                                        {
-                                                               v[0] = ent->model->brush.shadowmesh->vertex3f + e[0] * 3;
-                                                               v[1] = ent->model->brush.shadowmesh->vertex3f + e[1] * 3;
-                                                               v[2] = ent->model->brush.shadowmesh->vertex3f + e[2] * 3;
-                                                               if (PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]) && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
-                                                                       shadowmarklist[numshadowmark++] = t;
+                                                               SETPVSBIT(outsurfacepvs, surfaceindex);
+                                                               outsurfacelist[outnumsurfaces++] = surfaceindex;
+                                                               break;
                                                        }
                                                }
                                        }
@@ -1939,85 +1906,83 @@ void R_Model_Brush_DrawShadowVolume (entity_render_t *ent, vec3_t relativelighto
                        }
                }
        }
-       else
+
+       // limit combined leaf box to light boundaries
+       outmins[0] = max(outmins[0], lightmins[0]);
+       outmins[1] = max(outmins[1], lightmins[1]);
+       outmins[2] = max(outmins[2], lightmins[2]);
+       outmaxs[0] = min(outmaxs[0], lightmaxs[0]);
+       outmaxs[1] = min(outmaxs[1], lightmaxs[1]);
+       outmaxs[2] = min(outmaxs[2], lightmaxs[2]);
+
+       *outnumclusterspointer = outnumclusters;
+       *outnumsurfacespointer = outnumsurfaces;
+}
+
+void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist)
+{
+       model_t *model = ent->model;
+       vec3_t lightmins, lightmaxs;
+       msurface_t *surface;
+       int surfacelistindex, j, t;
+       const int *e;
+       const float *v[3];
+       if (r_drawcollisionbrushes.integer < 2)
        {
-               for (marksurfnum = 0, surf = ent->model->brushq1.surfaces + ent->model->brushq1.firstmodelsurface;marksurfnum < ent->model->brushq1.nummodelsurfaces;marksurfnum++, surf++)
+               lightmins[0] = relativelightorigin[0] - lightradius;
+               lightmins[1] = relativelightorigin[1] - lightradius;
+               lightmins[2] = relativelightorigin[2] - lightradius;
+               lightmaxs[0] = relativelightorigin[0] + lightradius;
+               lightmaxs[1] = relativelightorigin[1] + lightradius;
+               lightmaxs[2] = relativelightorigin[2] + lightradius;
+               R_Mesh_Matrix(&ent->matrix);
+               R_Shadow_PrepareShadowMark(model->brush.shadowmesh->numtriangles);
+               for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
                {
-                       if (BoxesOverlap(lightmins, lightmaxs, surf->poly_mins, surf->poly_maxs) && surf->texinfo->texture->rendertype == SURFRENDER_OPAQUE && (surf->flags & SURF_SHADOWCAST))
+                       surface = model->brushq1.surfaces + surfacelist[surfacelistindex];
+                       for (j = 0, t = surface->num_firstshadowmeshtriangle, e = model->brush.shadowmesh->element3i + t * 3;j < surface->mesh.num_triangles;j++, t++, e += 3)
                        {
-                               for (trianglenum = 0, t = surf->num_firstshadowmeshtriangle, e = ent->model->brush.shadowmesh->element3i + t * 3;trianglenum < surf->mesh.num_triangles;trianglenum++, t++, e += 3)
-                               {
-                                       v[0] = ent->model->brush.shadowmesh->vertex3f + e[0] * 3;
-                                       v[1] = ent->model->brush.shadowmesh->vertex3f + e[1] * 3;
-                                       v[2] = ent->model->brush.shadowmesh->vertex3f + e[2] * 3;
-                                       if (PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]) && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
-                                               shadowmarklist[numshadowmark++] = t;
-                               }
+                               v[0] = model->brush.shadowmesh->vertex3f + e[0] * 3;
+                               v[1] = model->brush.shadowmesh->vertex3f + e[1] * 3;
+                               v[2] = model->brush.shadowmesh->vertex3f + e[2] * 3;
+                               if (PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]) && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
+                                       shadowmarklist[numshadowmark++] = t;
                        }
                }
+               R_Shadow_VolumeFromList(model->brush.shadowmesh->numverts, model->brush.shadowmesh->numtriangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, model->brush.shadowmesh->neighbor3i, relativelightorigin, lightradius + model->radius, numshadowmark, shadowmarklist);
        }
-       R_Shadow_VolumeFromList(ent->model->brush.shadowmesh->numverts, ent->model->brush.shadowmesh->numtriangles, ent->model->brush.shadowmesh->vertex3f, ent->model->brush.shadowmesh->element3i, ent->model->brush.shadowmesh->neighbor3i, relativelightorigin, projectdistance, numshadowmark, shadowmarklist);
-#endif
 }
 
-void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap)
+void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap, int numsurfaces, const int *surfacelist)
 {
-       int leafnum, marksurfnum;
-       msurface_t *surf;
-       mleaf_t *leaf;
-       const qbyte *pvs;
+       model_t *model = ent->model;
+       vec3_t lightmins, lightmaxs, modelorg;
+       msurface_t *surface;
        texture_t *t;
-       float lightmins[3], lightmaxs[3];
-       if (ent->model == NULL)
-               return;
-       R_Mesh_Matrix(&ent->matrix);
-       lightmins[0] = relativelightorigin[0] - lightradius;
-       lightmins[1] = relativelightorigin[1] - lightradius;
-       lightmins[2] = relativelightorigin[2] - lightradius;
-       lightmaxs[0] = relativelightorigin[0] + lightradius;
-       lightmaxs[1] = relativelightorigin[1] + lightradius;
-       lightmaxs[2] = relativelightorigin[2] + lightradius;
-       R_UpdateTextureInfo(ent);
-       shadowmarkcount++;
-       if (ent->model->brush.GetPVS && (pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin)))
+       int surfacelistindex;
+       if (r_drawcollisionbrushes.integer < 2)
        {
-               pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin);
-               for (leafnum = 0, leaf = ent->model->brushq1.data_leafs;leafnum < ent->model->brushq1.num_leafs;leafnum++, leaf++)
+               lightmins[0] = relativelightorigin[0] - lightradius;
+               lightmins[1] = relativelightorigin[1] - lightradius;
+               lightmins[2] = relativelightorigin[2] - lightradius;
+               lightmaxs[0] = relativelightorigin[0] + lightradius;
+               lightmaxs[1] = relativelightorigin[1] + lightradius;
+               lightmaxs[2] = relativelightorigin[2] + lightradius;
+               R_Mesh_Matrix(&ent->matrix);
+               Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
+               for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
                {
-                       if (BoxesOverlap(lightmins, lightmaxs, leaf->mins, leaf->maxs) && CHECKPVSBIT(pvs, leaf->clusterindex))
+                       surface = model->brushq1.surfaces + surfacelist[surfacelistindex];
+                       if (r_shadow_compilingrtlight)
                        {
-                               for (marksurfnum = 0;marksurfnum < leaf->nummarksurfaces;marksurfnum++)
-                               {
-                                       surf = ent->model->brushq1.surfaces + leaf->firstmarksurface[marksurfnum];
-                                       if (surf->shadowmark != shadowmarkcount)
-                                       {
-                                               surf->shadowmark = shadowmarkcount;
-                                               if (BoxesOverlap(lightmins, lightmaxs, surf->poly_mins, surf->poly_maxs) && (ent != &cl_entities[0].render || surf->visframe == r_framecount))
-                                               {
-                                                       t = surf->texinfo->texture->currentframe;
-                                                       if (t->rendertype == SURFRENDER_OPAQUE && t->flags & SURF_SHADOWLIGHT)
-                                                       {
-                                                               R_Shadow_DiffuseLighting(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, relativelightorigin, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, t->skin.base, t->skin.nmap, lightcubemap);
-                                                               R_Shadow_SpecularLighting(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, relativelightorigin, relativeeyeorigin, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, t->skin.gloss, t->skin.nmap, lightcubemap);
-                                                       }
-                                               }
-                                       }
-                               }
+                               // if compiling an rtlight, capture the mesh
+                               Mod_ShadowMesh_AddMesh(r_shadow_mempool, r_shadow_compilingrtlight->static_meshchain_light, surface->texinfo->texture->skin.base, surface->texinfo->texture->skin.gloss, surface->texinfo->texture->skin.nmap, surface->mesh.data_vertex3f, surface->mesh.data_svector3f, surface->mesh.data_tvector3f, surface->mesh.data_normal3f, surface->mesh.data_texcoordtexture2f, surface->mesh.num_triangles, surface->mesh.data_element3i);
                        }
-               }
-       }
-       else
-       {
-               for (marksurfnum = 0, surf = ent->model->brushq1.surfaces + ent->model->brushq1.firstmodelsurface;marksurfnum < ent->model->brushq1.nummodelsurfaces;marksurfnum++, surf++)
-               {
-                       if (BoxesOverlap(lightmins, lightmaxs, surf->poly_mins, surf->poly_maxs) && (ent != &cl_entities[0].render || surf->visframe == r_framecount))
+                       else if (ent != &cl_entities[0].render || surface->visframe == r_framecount)
                        {
-                               t = surf->texinfo->texture->currentframe;
+                               t = surface->texinfo->texture->currentframe;
                                if (t->rendertype == SURFRENDER_OPAQUE && t->flags & SURF_SHADOWLIGHT)
-                               {
-                                       R_Shadow_DiffuseLighting(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, relativelightorigin, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, t->skin.base, t->skin.nmap, lightcubemap);
-                                       R_Shadow_SpecularLighting(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, relativelightorigin, relativeeyeorigin, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, t->skin.gloss, t->skin.nmap, lightcubemap);
-                               }
+                                               R_Shadow_RenderLighting(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i, surface->mesh.data_vertex3f, surface->mesh.data_svector3f, surface->mesh.data_tvector3f, surface->mesh.data_normal3f, surface->mesh.data_texcoordtexture2f, relativelightorigin, relativeeyeorigin, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, t->skin.base, t->skin.nmap, t->skin.gloss, lightcubemap, LIGHTING_DIFFUSE | LIGHTING_SPECULAR);
                        }
                }
        }
@@ -2375,7 +2340,6 @@ void R_Q3BSP_DrawFace(entity_render_t *ent, q3mface_t *face)
                        return;
        }
        c_faces++;
-       face->visframe = r_framecount;
        if ((face->texture->surfaceparms & Q3SURFACEPARM_TRANS) || ent->alpha < 1 || (ent->effects & EF_ADDITIVE))
        {
                vec3_t facecenter, center;
@@ -2477,13 +2441,12 @@ void R_Q3BSP_DrawSky(entity_render_t *ent)
        q3mface_t *face;
        vec3_t modelorg;
        model_t *model;
-       qbyte *pvs;
        R_Mesh_Matrix(&ent->matrix);
        model = ent->model;
        if (r_drawcollisionbrushes.integer < 2)
        {
                Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
-               if (ent == &cl_entities[0].render && model->brush.num_pvsclusters && !r_novis.integer && (pvs = model->brush.GetPVS(model, modelorg)))
+               if (ent == &cl_entities[0].render)
                {
                        if (r_q3bsp_framecount != r_framecount)
                        {
@@ -2514,8 +2477,9 @@ void R_Q3BSP_Draw(entity_render_t *ent)
        if (r_drawcollisionbrushes.integer < 2)
        {
                Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
-               if (ent == &cl_entities[0].render && model->brush.num_pvsclusters && !r_novis.integer && (pvs = model->brush.GetPVS(model, modelorg)))
+               if (ent == &cl_entities[0].render)
                {
+                       if (model->brush.num_pvsclusters && !r_novis.integer && (pvs = model->brush.GetPVS(model, modelorg)))
                        if (r_q3bsp_framecount != r_framecount)
                        {
                                r_q3bsp_framecount = r_framecount;
@@ -2546,132 +2510,156 @@ void R_Q3BSP_Draw(entity_render_t *ent)
        }
 }
 
-void R_Q3BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius)
+void R_Q3BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outclusterlist, qbyte *outclusterpvs, int *outnumclusterspointer, int *outsurfacelist, qbyte *outsurfacepvs, int *outnumsurfacespointer)
 {
-       int j, t, leafnum, marksurfnum;
+       model_t *model = ent->model;
+       vec3_t lightmins, lightmaxs;
+       int t, leafindex, marksurfaceindex, surfaceindex, triangleindex, outnumclusters = 0, outnumsurfaces = 0;
        const int *e;
-       const qbyte *pvs;
        const float *v[3];
-       q3mface_t *face;
+       q3mface_t *surface;
        q3mleaf_t *leaf;
-       vec3_t modelorg, lightmins, lightmaxs;
-       model_t *model;
-       float projectdistance;
-       projectdistance = lightradius + ent->model->radius;//projectdistance = 1000000000.0f;//lightradius + ent->model->radius;
-       if (r_drawcollisionbrushes.integer < 2)
+       const qbyte *pvs;
+       lightmins[0] = relativelightorigin[0] - lightradius;
+       lightmins[1] = relativelightorigin[1] - lightradius;
+       lightmins[2] = relativelightorigin[2] - lightradius;
+       lightmaxs[0] = relativelightorigin[0] + lightradius;
+       lightmaxs[1] = relativelightorigin[1] + lightradius;
+       lightmaxs[2] = relativelightorigin[2] + lightradius;
+       *outnumclusterspointer = 0;
+       *outnumsurfacespointer = 0;
+       memset(outclusterpvs, 0, model->brush.num_pvsclusterbytes);
+       memset(outsurfacepvs, 0, (model->numsurfaces + 7) >> 3);
+       if (model == NULL)
        {
-               model = ent->model;
-               R_Mesh_Matrix(&ent->matrix);
-               Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
-               lightmins[0] = relativelightorigin[0] - lightradius;
-               lightmins[1] = relativelightorigin[1] - lightradius;
-               lightmins[2] = relativelightorigin[2] - lightradius;
-               lightmaxs[0] = relativelightorigin[0] + lightradius;
-               lightmaxs[1] = relativelightorigin[1] + lightradius;
-               lightmaxs[2] = relativelightorigin[2] + lightradius;
-               R_Shadow_PrepareShadowMark(model->brush.shadowmesh->numtriangles);
-               if (ent->model->brush.GetPVS && (pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin)))
-               {       
-                       for (leafnum = 0, leaf = ent->model->brushq3.data_leafs;leafnum < ent->model->brushq3.num_leafs;leafnum++, leaf++)
+               VectorCopy(lightmins, outmins);
+               VectorCopy(lightmaxs, outmaxs);
+               return;
+       }
+       VectorCopy(relativelightorigin, outmins);
+       VectorCopy(relativelightorigin, outmaxs);
+       if (model->brush.GetPVS)
+               pvs = model->brush.GetPVS(model, relativelightorigin);
+       else
+               pvs = NULL;
+       // FIXME: use BSP recursion as lights are often small
+       for (leafindex = 0, leaf = model->brushq3.data_leafs;leafindex < model->brushq3.num_leafs;leafindex++, leaf++)
+       {
+               if (BoxesOverlap(lightmins, lightmaxs, leaf->mins, leaf->maxs) && (pvs == NULL || CHECKPVSBIT(pvs, leaf->clusterindex)))
+               {
+                       outmins[0] = min(outmins[0], leaf->mins[0]);
+                       outmins[1] = min(outmins[1], leaf->mins[1]);
+                       outmins[2] = min(outmins[2], leaf->mins[2]);
+                       outmaxs[0] = max(outmaxs[0], leaf->maxs[0]);
+                       outmaxs[1] = max(outmaxs[1], leaf->maxs[1]);
+                       outmaxs[2] = max(outmaxs[2], leaf->maxs[2]);
+                       if (!CHECKPVSBIT(outclusterpvs, leaf->clusterindex))
+                       {
+                               SETPVSBIT(outclusterpvs, leaf->clusterindex);
+                               outclusterlist[outnumclusters++] = leaf->clusterindex;
+                       }
+                       for (marksurfaceindex = 0;marksurfaceindex < leaf->numleaffaces;marksurfaceindex++)
                        {
-                               if (BoxesOverlap(lightmins, lightmaxs, leaf->mins, leaf->maxs) && CHECKPVSBIT(pvs, leaf->clusterindex))
+                               surface = leaf->firstleafface[marksurfaceindex];
+                               surfaceindex = surface - model->brushq3.data_faces;
+                               if (!CHECKPVSBIT(outsurfacepvs, surfaceindex))
                                {
-                                       for (marksurfnum = 0;marksurfnum < leaf->numleaffaces;marksurfnum++)
+                                       if (BoxesOverlap(lightmins, lightmaxs, surface->mins, surface->maxs))
                                        {
-                                               face = leaf->firstleafface[marksurfnum];
-                                               if (face->shadowmark != shadowmarkcount)
+                                               for (triangleindex = 0, t = surface->num_firstshadowmeshtriangle, e = model->brush.shadowmesh->element3i + t * 3;triangleindex < surface->num_triangles;triangleindex++, t++, e += 3)
                                                {
-                                                       face->shadowmark = shadowmarkcount;
-                                                       if (BoxesOverlap(lightmins, lightmaxs, face->mins, face->maxs))
+                                                       v[0] = model->brush.shadowmesh->vertex3f + e[0] * 3;
+                                                       v[1] = model->brush.shadowmesh->vertex3f + e[1] * 3;
+                                                       v[2] = model->brush.shadowmesh->vertex3f + e[2] * 3;
+                                                       if (PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]) && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
                                                        {
-                                                               for (j = 0, t = face->num_firstshadowmeshtriangle, e = model->brush.shadowmesh->element3i + t * 3;j < face->num_triangles;j++, t++, e += 3)
-                                                               {
-                                                                       v[0] = model->brush.shadowmesh->vertex3f + e[0] * 3;
-                                                                       v[1] = model->brush.shadowmesh->vertex3f + e[1] * 3;
-                                                                       v[2] = model->brush.shadowmesh->vertex3f + e[2] * 3;
-                                                                       if (PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]) && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
-                                                                               shadowmarklist[numshadowmark++] = t;
-                                                               }
+                                                               SETPVSBIT(outsurfacepvs, surfaceindex);
+                                                               outsurfacelist[outnumsurfaces++] = surfaceindex;
+                                                               break;
                                                        }
                                                }
                                        }
                                }
                        }
                }
-               else
+       }
+
+       // limit combined leaf box to light boundaries
+       outmins[0] = max(outmins[0], lightmins[0]);
+       outmins[1] = max(outmins[1], lightmins[1]);
+       outmins[2] = max(outmins[2], lightmins[2]);
+       outmaxs[0] = min(outmaxs[0], lightmaxs[0]);
+       outmaxs[1] = min(outmaxs[1], lightmaxs[1]);
+       outmaxs[2] = min(outmaxs[2], lightmaxs[2]);
+
+       *outnumclusterspointer = outnumclusters;
+       *outnumsurfacespointer = outnumsurfaces;
+}
+
+void R_Q3BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist)
+{
+       model_t *model = ent->model;
+       vec3_t lightmins, lightmaxs;
+       q3mface_t *surface;
+       int surfacelistindex, j, t;
+       const int *e;
+       const float *v[3];
+       if (r_drawcollisionbrushes.integer < 2)
+       {
+               lightmins[0] = relativelightorigin[0] - lightradius;
+               lightmins[1] = relativelightorigin[1] - lightradius;
+               lightmins[2] = relativelightorigin[2] - lightradius;
+               lightmaxs[0] = relativelightorigin[0] + lightradius;
+               lightmaxs[1] = relativelightorigin[1] + lightradius;
+               lightmaxs[2] = relativelightorigin[2] + lightradius;
+               R_Mesh_Matrix(&ent->matrix);
+               R_Shadow_PrepareShadowMark(model->brush.shadowmesh->numtriangles);
+               for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
                {
-                       for (marksurfnum = 0, face = model->brushq3.data_thismodel->firstface;marksurfnum < model->brushq3.data_thismodel->numfaces;marksurfnum++, face++)
+                       surface = model->brushq3.data_faces + surfacelist[surfacelistindex];
+                       // FIXME: check some manner of face->rendermode here?
+                       if (!(surface->texture->surfaceflags & Q3SURFACEFLAG_NODRAW) && surface->num_triangles && !surface->texture->skin.fog)
                        {
-                               if (BoxesOverlap(lightmins, lightmaxs, face->mins, face->maxs))
+                               for (j = 0, t = surface->num_firstshadowmeshtriangle, e = model->brush.shadowmesh->element3i + t * 3;j < surface->num_triangles;j++, t++, e += 3)
                                {
-                                       for (j = 0, t = face->num_firstshadowmeshtriangle, e = model->brush.shadowmesh->element3i + t * 3;j < face->num_triangles;j++, t++, e += 3)
-                                       {
-                                               v[0] = model->brush.shadowmesh->vertex3f + e[0] * 3;
-                                               v[1] = model->brush.shadowmesh->vertex3f + e[1] * 3;
-                                               v[2] = model->brush.shadowmesh->vertex3f + e[2] * 3;
-                                               if (PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]) && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
-                                                       shadowmarklist[numshadowmark++] = t;
-                                       }
+                                       v[0] = model->brush.shadowmesh->vertex3f + e[0] * 3;
+                                       v[1] = model->brush.shadowmesh->vertex3f + e[1] * 3;
+                                       v[2] = model->brush.shadowmesh->vertex3f + e[2] * 3;
+                                       if (PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]) && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
+                                               shadowmarklist[numshadowmark++] = t;
                                }
                        }
                }
-               R_Shadow_VolumeFromList(model->brush.shadowmesh->numverts, model->brush.shadowmesh->numtriangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, model->brush.shadowmesh->neighbor3i, relativelightorigin, projectdistance, numshadowmark, shadowmarklist);
+               R_Shadow_VolumeFromList(model->brush.shadowmesh->numverts, model->brush.shadowmesh->numtriangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, model->brush.shadowmesh->neighbor3i, relativelightorigin, lightradius + model->radius, numshadowmark, shadowmarklist);
        }
 }
 
-void R_Q3BSP_DrawFaceLight(entity_render_t *ent, q3mface_t *face, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap)
+void R_Q3BSP_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap, int numsurfaces, const int *surfacelist)
 {
-       if ((face->texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || !face->num_triangles)
-               return;
-       R_Shadow_DiffuseLighting(face->num_vertices, face->num_triangles, face->data_element3i, face->data_vertex3f, face->data_svector3f, face->data_tvector3f, face->data_normal3f, face->data_texcoordtexture2f, relativelightorigin, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, face->texture->skin.base, face->texture->skin.nmap, lightcubemap);
-       R_Shadow_SpecularLighting(face->num_vertices, face->num_triangles, face->data_element3i, face->data_vertex3f, face->data_svector3f, face->data_tvector3f, face->data_normal3f, face->data_texcoordtexture2f, relativelightorigin, relativeeyeorigin, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, face->texture->skin.gloss, face->texture->skin.nmap, lightcubemap);
-}
-
-void R_Q3BSP_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap)
-{
-       int leafnum, marksurfnum;
-       const qbyte *pvs;
-       q3mface_t *face;
-       q3mleaf_t *leaf;
-       vec3_t modelorg, lightmins, lightmaxs;
-       model_t *model;
+       model_t *model = ent->model;
+       vec3_t lightmins, lightmaxs, modelorg;
+       q3mface_t *surface;
+       int surfacelistindex;
        if (r_drawcollisionbrushes.integer < 2)
        {
-               model = ent->model;
-               R_Mesh_Matrix(&ent->matrix);
-               Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
                lightmins[0] = relativelightorigin[0] - lightradius;
                lightmins[1] = relativelightorigin[1] - lightradius;
                lightmins[2] = relativelightorigin[2] - lightradius;
                lightmaxs[0] = relativelightorigin[0] + lightradius;
                lightmaxs[1] = relativelightorigin[1] + lightradius;
                lightmaxs[2] = relativelightorigin[2] + lightradius;
-
-               if (ent->model->brush.GetPVS && (pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin)))
-               {       
-                       pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin);
-                       for (leafnum = 0, leaf = ent->model->brushq3.data_leafs;leafnum < ent->model->brushq3.num_leafs;leafnum++, leaf++)
+               R_Mesh_Matrix(&ent->matrix);
+               Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
+               for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+               {
+                       surface = model->brushq3.data_faces + surfacelist[surfacelistindex];
+                       if (r_shadow_compilingrtlight)
                        {
-                               if (BoxesOverlap(lightmins, lightmaxs, leaf->mins, leaf->maxs) && CHECKPVSBIT(pvs, leaf->clusterindex))
-                               {
-                                       for (marksurfnum = 0;marksurfnum < leaf->numleaffaces;marksurfnum++)
-                                       {
-                                               face = leaf->firstleafface[marksurfnum];
-                                               if (face->shadowmark != shadowmarkcount)
-                                               {
-                                                       face->shadowmark = shadowmarkcount;
-                                                       if (BoxesOverlap(lightmins, lightmaxs, face->mins, face->maxs) && (ent != &cl_entities[0].render || face->visframe == r_framecount))
-                                                               R_Q3BSP_DrawFaceLight(ent, face, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, lightcubemap);
-                                               }
-                                       }
-                               }
+                               // if compiling an rtlight, capture the mesh
+                               Mod_ShadowMesh_AddMesh(r_shadow_mempool, r_shadow_compilingrtlight->static_meshchain_light, surface->texture->skin.base, surface->texture->skin.gloss, surface->texture->skin.nmap, surface->data_vertex3f, surface->data_svector3f, surface->data_tvector3f, surface->data_normal3f, surface->data_texcoordtexture2f, surface->num_triangles, surface->data_element3i);
                        }
-               }
-               else
-               {
-                       for (marksurfnum = 0, face = model->brushq3.data_thismodel->firstface;marksurfnum < model->brushq3.data_thismodel->numfaces;marksurfnum++, face++)
-                               if (BoxesOverlap(lightmins, lightmaxs, face->mins, face->maxs) && (ent != &cl_entities[0].render || face->visframe == r_framecount))
-                                       R_Q3BSP_DrawFaceLight(ent, face, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, lightcubemap);
+                       else if ((ent != &cl_entities[0].render || surface->visframe == r_framecount) && !(surface->texture->surfaceflags & Q3SURFACEFLAG_NODRAW) && surface->num_triangles)
+                               R_Shadow_RenderLighting(surface->num_vertices, surface->num_triangles, surface->data_element3i, surface->data_vertex3f, surface->data_svector3f, surface->data_tvector3f, surface->data_normal3f, surface->data_texcoordtexture2f, relativelightorigin, relativeeyeorigin, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, surface->texture->skin.base, surface->texture->skin.nmap, surface->texture->skin.gloss, lightcubemap, LIGHTING_DIFFUSE | LIGHTING_SPECULAR);
                }
        }
 }
index f8625a8..0a92545 100644 (file)
@@ -311,8 +311,8 @@ void Mod_BuildAliasSkinsFromSkinFiles(aliasskin_t *skin, skinfile_t *skinfile, c
 #define BOUNDI(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid ##VALUE (%d exceeds %d - %d)\n", loadmodel->name, VALUE, MIN, MAX);
 #define BOUNDF(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid ##VALUE (%f exceeds %f - %f)\n", loadmodel->name, VALUE, MIN, MAX);
 extern void R_Model_Alias_Draw(entity_render_t *ent);
-extern void R_Model_Alias_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
-extern void R_Model_Alias_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap);
+extern void R_Model_Alias_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
+extern void R_Model_Alias_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap, int numsurfaces, const int *surfacelist);
 void Mod_IDP0_Load(model_t *mod, void *buffer)
 {
        int i, j, version, totalskins, skinwidth, skinheight, groupframes, groupskins, numverts;
@@ -969,8 +969,8 @@ void Mod_IDP3_Load(model_t *mod, void *buffer)
 
 extern void R_Model_Zymotic_DrawSky(entity_render_t *ent);
 extern void R_Model_Zymotic_Draw(entity_render_t *ent);
-extern void R_Model_Zymotic_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
-extern void R_Model_Zymotic_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap);
+extern void R_Model_Zymotic_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
+extern void R_Model_Zymotic_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap, int numsurfaces, const int *surfacelist);
 void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer)
 {
        zymtype1header_t *pinmodel, *pheader;
index 3d15e6f..a56f53c 100644 (file)
@@ -2878,8 +2878,9 @@ void Mod_Q1BSP_GetVisible(model_t *model, const vec3_t point, const vec3_t mins,
 
 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
 extern void R_Model_Brush_Draw(entity_render_t *ent);
-extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
-extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap);
+extern void R_Model_Brush_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outclusterlist, qbyte *outclusterpvs, int *outnumclusterspointer, int *outsurfacelist, qbyte *outsurfacepvs, int *outnumsurfacespointer);
+extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
+extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap, int numsurfaces, const int *surfacelist);
 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
 {
        int i, j, k;
@@ -2996,9 +2997,16 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
                mod->brushq1.firstmodelsurface = bm->firstface;
                mod->brushq1.nummodelsurfaces = bm->numfaces;
 
+               // make the model surface list (used by shadowing/lighting)
+               mod->numsurfaces = mod->brushq1.nummodelsurfaces;
+               mod->surfacelist = Mem_Alloc(originalloadmodel->mempool, mod->numsurfaces * sizeof(*mod->surfacelist));
+               for (j = 0;j < mod->numsurfaces;j++)
+                       mod->surfacelist[j] = mod->brushq1.firstmodelsurface + j;
+
                // this gets altered below if sky is used
                mod->DrawSky = NULL;
                mod->Draw = R_Model_Brush_Draw;
+               mod->GetLightInfo = R_Model_Brush_GetLightInfo;
                mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
                mod->DrawLight = R_Model_Brush_DrawLight;
                if (i != 0)
@@ -5518,8 +5526,9 @@ void Mod_Q3BSP_GetVisible(model_t *model, const vec3_t point, const vec3_t mins,
 
 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
-extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
-extern void R_Q3BSP_DrawLight(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap);
+extern void R_Q3BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outclusterlist, qbyte *outclusterpvs, int *outnumclusterspointer, int *outsurfacelist, qbyte *outsurfacepvs, int *outnumsurfacespointer);
+extern void R_Q3BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
+extern void R_Q3BSP_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap, int numsurfaces, const int *surfacelist);
 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
 {
        int i, j, numshadowmeshtriangles;
@@ -5554,6 +5563,7 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
        mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
        //mod->DrawSky = R_Q3BSP_DrawSky;
        mod->Draw = R_Q3BSP_Draw;
+       mod->GetLightInfo = R_Q3BSP_GetLightInfo;
        mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
        mod->DrawLight = R_Q3BSP_DrawLight;
 
@@ -5623,6 +5633,12 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
                mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
                mod->brushq3.submodel = i;
 
+               // make the model surface list (used by shadowing/lighting)
+               mod->numsurfaces = mod->brushq3.data_thismodel->numfaces;
+               mod->surfacelist = Mem_Alloc(loadmodel->mempool, mod->numsurfaces * sizeof(*mod->surfacelist));
+               for (j = 0;j < mod->numsurfaces;j++)
+                       mod->surfacelist[j] = (mod->brushq3.data_thismodel->firstface - mod->brushq3.data_faces) + j;
+
                VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
                VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
                corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
index ebf24a7..3d91791 100644 (file)
@@ -586,14 +586,20 @@ typedef struct model_s
        animscene_t             *skinscenes; // [numskins]
        // skin animation info
        animscene_t             *animscenes; // [numframes]
+       // how many surfaces this (sub)model has
+       int                             numsurfaces;
+       // list of surface numbers in this (sub)model
+       int                             *surfacelist;
        // draw the model's sky polygons (only used by brush models)
        void(*DrawSky)(struct entity_render_s *ent);
        // draw the model using lightmap/dlight shading
        void(*Draw)(struct entity_render_s *ent);
+       // gathers info on which clusters and surfaces are lit by light, as well as calculating a bounding box
+       void(*GetLightInfo)(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outclusterlist, qbyte *outclusterpvs, int *outnumclusterspointer, int *outsurfacelist, qbyte *outsurfacepvs, int *outnumsurfacespointer);
        // draw a shadow volume for the model based on light source
-       void(*DrawShadowVolume)(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
+       void(*DrawShadowVolume)(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
        // draw the lighting on a model (through stencil)
-       void(*DrawLight)(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap);
+       void(*DrawLight)(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap, int numsurfaces, const int *surfacelist);
        // trace a box against this model
        void (*TraceBox)(struct model_s *model, int frame, struct trace_s *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask);
        // fields belonging to each type of model
index b469908..2c077ff 100644 (file)
@@ -143,6 +143,14 @@ int *vertexupdate;
 int *vertexremap;
 int vertexupdatenum;
 
+int r_shadow_buffer_numclusterpvsbytes;
+qbyte *r_shadow_buffer_clusterpvs;
+int *r_shadow_buffer_clusterlist;
+
+int r_shadow_buffer_numsurfacepvsbytes;
+qbyte *r_shadow_buffer_surfacepvs;
+int *r_shadow_buffer_surfacelist;
+
 rtexturepool_t *r_shadow_texturepool;
 rtexture_t *r_shadow_normalcubetexture;
 rtexture_t *r_shadow_attenuation2dtexture;
@@ -205,6 +213,12 @@ void r_shadow_start(void)
        shadowmark = NULL;
        shadowmarklist = NULL;
        shadowmarkcount = 0;
+       r_shadow_buffer_numclusterpvsbytes = 0;
+       r_shadow_buffer_clusterpvs = NULL;
+       r_shadow_buffer_clusterlist = NULL;
+       r_shadow_buffer_numsurfacepvsbytes = 0;
+       r_shadow_buffer_surfacepvs = NULL;
+       r_shadow_buffer_surfacelist = NULL;
        r_shadow_normalcubetexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
@@ -240,6 +254,12 @@ void r_shadow_shutdown(void)
        shadowmark = NULL;
        shadowmarklist = NULL;
        shadowmarkcount = 0;
+       r_shadow_buffer_numclusterpvsbytes = 0;
+       r_shadow_buffer_clusterpvs = NULL;
+       r_shadow_buffer_clusterlist = NULL;
+       r_shadow_buffer_numsurfacepvsbytes = 0;
+       r_shadow_buffer_surfacepvs = NULL;
+       r_shadow_buffer_surfacelist = NULL;
        Mem_FreePool(&r_shadow_mempool);
 }
 
@@ -351,6 +371,36 @@ int *R_Shadow_ResizeShadowElements(int numtris)
        return shadowelements;
 }
 
+void R_Shadow_EnlargeClusterBuffer(int numclusters)
+{
+       int numclusterpvsbytes = (((numclusters + 7) >> 3) + 255) & ~255;
+       if (r_shadow_buffer_numclusterpvsbytes < numclusterpvsbytes)
+       {
+               if (r_shadow_buffer_clusterpvs)
+                       Mem_Free(r_shadow_buffer_clusterpvs);
+               if (r_shadow_buffer_clusterlist)
+                       Mem_Free(r_shadow_buffer_clusterlist);
+               r_shadow_buffer_numclusterpvsbytes = numclusterpvsbytes;
+               r_shadow_buffer_clusterpvs = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numclusterpvsbytes);
+               r_shadow_buffer_clusterlist = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numclusterpvsbytes * 8 * sizeof(*r_shadow_buffer_clusterlist));
+       }
+}
+
+void R_Shadow_EnlargeSurfaceBuffer(int numsurfaces)
+{
+       int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
+       if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
+       {
+               if (r_shadow_buffer_surfacepvs)
+                       Mem_Free(r_shadow_buffer_surfacepvs);
+               if (r_shadow_buffer_surfacelist)
+                       Mem_Free(r_shadow_buffer_surfacelist);
+               r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
+               r_shadow_buffer_surfacepvs = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes);
+               r_shadow_buffer_surfacelist = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
+       }
+}
+
 void R_Shadow_PrepareShadowMark(int numtris)
 {
        // make sure shadowmark is big enough for this volume
@@ -539,21 +589,27 @@ void R_Shadow_VolumeFromSphere(int numverts, int numtris, const float *invertex3
 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
 {
        rmeshstate_t m;
+       if (r_shadow_compilingrtlight)
+       {
+               // if we're compiling an rtlight, capture the mesh
+               Mod_ShadowMesh_AddMesh(r_shadow_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
+               return;
+       }
        memset(&m, 0, sizeof(m));
        m.pointer_vertex = vertex3f;
        R_Mesh_State(&m);
        GL_LockArrays(0, numvertices);
        if (r_shadowstage == SHADOWSTAGE_STENCIL)
        {
-               // decrement stencil if frontface is behind depthbuffer
-               qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
-               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
-               R_Mesh_Draw(numvertices, numtriangles, element3i);
-               c_rt_shadowmeshes++;
-               c_rt_shadowtris += numtriangles;
                // increment stencil if backface is behind depthbuffer
                qglCullFace(GL_BACK); // quake is backwards, this culls front faces
                qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
+               R_Mesh_Draw(numvertices, numtriangles, element3i);
+               c_rt_shadowmeshes++;
+               c_rt_shadowtris += numtriangles;
+               // decrement stencil if frontface is behind depthbuffer
+               qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
+               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
        }
        R_Mesh_Draw(numvertices, numtriangles, element3i);
        c_rt_shadowmeshes++;
@@ -561,35 +617,6 @@ void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *verte
        GL_LockArrays(0, 0);
 }
 
-void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
-{
-       shadowmesh_t *mesh;
-       rmeshstate_t m;
-       memset(&m, 0, sizeof(m));
-       for (mesh = firstmesh;mesh;mesh = mesh->next)
-       {
-               m.pointer_vertex = mesh->vertex3f;
-               R_Mesh_State(&m);
-               GL_LockArrays(0, mesh->numverts);
-               if (r_shadowstage == SHADOWSTAGE_STENCIL)
-               {
-                       // decrement stencil if frontface is behind depthbuffer
-                       qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
-                       qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
-                       R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
-                       c_rtcached_shadowmeshes++;
-                       c_rtcached_shadowtris += mesh->numtriangles;
-                       // increment stencil if backface is behind depthbuffer
-                       qglCullFace(GL_BACK); // quake is backwards, this culls front faces
-                       qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
-               }
-               R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
-               c_rtcached_shadowmeshes++;
-               c_rtcached_shadowtris += mesh->numtriangles;
-               GL_LockArrays(0, 0);
-       }
-}
-
 float r_shadow_attenpower, r_shadow_attenscale;
 static void R_Shadow_MakeTextures(void)
 {
@@ -741,6 +768,7 @@ void R_Shadow_Stage_Begin(void)
        R_Mesh_State(&m);
        GL_Color(0, 0, 0, 1);
        qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
+       qglEnable(GL_CULL_FACE);
        GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
        r_shadowstage = SHADOWSTAGE_NONE;
 
@@ -790,18 +818,21 @@ void R_Shadow_Stage_ShadowVolumes(void)
        if (gl_ext_stenciltwoside.integer)
        {
                r_shadowstage = SHADOWSTAGE_STENCILTWOSIDE;
+               qglDisable(GL_CULL_FACE);
                qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
-        qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
-               qglStencilMask(~0);
-               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
-        qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
+               qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
                qglStencilMask(~0);
                qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+               qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
+               qglStencilMask(~0);
+               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
        }
        else
        {
                r_shadowstage = SHADOWSTAGE_STENCIL;
+               qglEnable(GL_CULL_FACE);
                qglStencilMask(~0);
+               // this is changed by every shadow render so its value here is unimportant
                qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        }
        GL_Clear(GL_STENCIL_BUFFER_BIT);
@@ -829,12 +860,13 @@ void R_Shadow_Stage_LightWithoutShadows(void)
        GL_ColorMask(1, 1, 1, 1);
        qglDepthFunc(GL_EQUAL);
        qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
+       qglEnable(GL_CULL_FACE);
        qglDisable(GL_STENCIL_TEST);
        if (gl_support_stenciltwoside)
                qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
        qglStencilMask(~0);
        qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
-       qglStencilFunc(GL_EQUAL, 128, 0xFF);
+       qglStencilFunc(GL_EQUAL, 128, ~0);
        r_shadowstage = SHADOWSTAGE_LIGHT;
        c_rt_lights++;
 }
@@ -860,7 +892,7 @@ void R_Shadow_Stage_LightWithShadows(void)
        qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        // only draw light where this geometry was already rendered AND the
        // stencil is 128 (values other than this mean shadow)
-       qglStencilFunc(GL_EQUAL, 128, 0xFF);
+       qglStencilFunc(GL_EQUAL, 128, ~0);
        r_shadowstage = SHADOWSTAGE_LIGHT;
        c_rt_lights++;
 }
@@ -885,7 +917,7 @@ void R_Shadow_Stage_End(void)
        if (gl_support_stenciltwoside)
                qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
        qglStencilMask(~0);
-       qglStencilFunc(GL_ALWAYS, 128, 0xFF);
+       qglStencilFunc(GL_ALWAYS, 128, ~0);
        r_shadowstage = SHADOWSTAGE_NONE;
 }
 
@@ -1182,568 +1214,563 @@ void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numverts, co
        }
 }
 
-void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, const float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
+void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, const float *relativeeyeorigin, const float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *glosstexture, rtexture_t *lightcubemap, int lighting)
 {
        int renders;
-       float color[3], color2[3];
+       float color[3], color2[3], colorscale;
        rmeshstate_t m;
+       if (!bumptexture)
+               bumptexture = r_shadow_blankbumptexture;
+       if (!glosstexture)
+               glosstexture = r_shadow_blankglosstexture;
        if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
        {
-               if (!bumptexture)
-                       bumptexture = r_shadow_blankbumptexture;
-               GL_Color(1,1,1,1);
-               // colorscale accounts for how much we multiply the brightness during combine
-               // mult is how many times the final pass of the lighting will be
-               // performed to get more brightness than otherwise possible
-               // limit mult to 64 for sanity sake
-               if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
+               if (lighting & LIGHTING_DIFFUSE)
                {
-                       // 3/2 3D combine path (Geforce3, Radeon 8500)
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(bumptexture);
-                       m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
-                       m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
-                       m.texcombinergb[0] = GL_REPLACE;
-                       m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                       m.pointer_texcoord[0] = texcoord2f;
-                       m.pointer_texcoord[1] = varray_texcoord3f[1];
-                       m.pointer_texcoord[2] = varray_texcoord3f[2];
-                       R_Mesh_State(&m);
-                       GL_ColorMask(0,0,0,1);
-                       GL_BlendFunc(GL_ONE, GL_ZERO);
-                       R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
-                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
-                       GL_LockArrays(0, numverts);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       GL_LockArrays(0, 0);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(basetexture);
-                       m.pointer_texcoord[0] = texcoord2f;
-                       if (lightcubemap)
+                       GL_Color(1,1,1,1);
+                       // colorscale accounts for how much we multiply the brightness during combine
+                       // mult is how many times the final pass of the lighting will be
+                       // performed to get more brightness than otherwise possible
+                       // limit mult to 64 for sanity sake
+                       if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
                        {
-                               m.texcubemap[1] = R_GetTexture(lightcubemap);
+                               // 3/2 3D combine path (Geforce3, Radeon 8500)
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(bumptexture);
+                               m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                               m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
+                               m.texcombinergb[0] = GL_REPLACE;
+                               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+                               m.pointer_texcoord[0] = texcoord2f;
                                m.pointer_texcoord[1] = varray_texcoord3f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
-                       }
-                       R_Mesh_State(&m);
-                       GL_LockArrays(0, numverts);
-                       GL_ColorMask(1,1,1,0);
-                       GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
-                       for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
-                       {
-                               color[0] = bound(0, color2[0], 1);
-                               color[1] = bound(0, color2[1], 1);
-                               color[2] = bound(0, color2[2], 1);
-                               GL_Color(color[0], color[1], color[2], 1);
+                               m.pointer_texcoord[2] = varray_texcoord3f[2];
+                               R_Mesh_State(&m);
+                               GL_ColorMask(0,0,0,1);
+                               GL_BlendFunc(GL_ONE, GL_ZERO);
+                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
+                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               GL_LockArrays(0, numverts);
                                R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(basetexture);
+                               m.pointer_texcoord[0] = texcoord2f;
+                               if (lightcubemap)
+                               {
+                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
+                                       m.pointer_texcoord[1] = varray_texcoord3f[1];
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                               }
+                               R_Mesh_State(&m);
+                               GL_LockArrays(0, numverts);
+                               GL_ColorMask(1,1,1,0);
+                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+                               VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
+                               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                               {
+                                       color[0] = bound(0, color2[0], 1);
+                                       color[1] = bound(0, color2[1], 1);
+                                       color[2] = bound(0, color2[2], 1);
+                                       GL_Color(color[0], color[1], color[2], 1);
+                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       c_rt_lightmeshes++;
+                                       c_rt_lighttris += numtriangles;
+                               }
+                               GL_LockArrays(0, 0);
                        }
-                       GL_LockArrays(0, 0);
-               }
-               else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
-               {
-                       // 1/2/2 3D combine path (original Radeon)
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
-                       m.pointer_texcoord[0] = varray_texcoord3f[0];
-                       R_Mesh_State(&m);
-                       GL_ColorMask(0,0,0,1);
-                       GL_BlendFunc(GL_ONE, GL_ZERO);
-                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
-                       GL_LockArrays(0, numverts);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       GL_LockArrays(0, 0);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(bumptexture);
-                       m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
-                       m.texcombinergb[0] = GL_REPLACE;
-                       m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                       m.pointer_texcoord[0] = texcoord2f;
-                       m.pointer_texcoord[1] = varray_texcoord3f[1];
-                       R_Mesh_State(&m);
-                       GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-                       R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
-                       GL_LockArrays(0, numverts);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       GL_LockArrays(0, 0);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(basetexture);
-                       m.pointer_texcoord[0] = texcoord2f;
-                       if (lightcubemap)
+                       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
                        {
-                               m.texcubemap[1] = R_GetTexture(lightcubemap);
+                               // 1/2/2 3D combine path (original Radeon)
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
+                               m.pointer_texcoord[0] = varray_texcoord3f[0];
+                               R_Mesh_State(&m);
+                               GL_ColorMask(0,0,0,1);
+                               GL_BlendFunc(GL_ONE, GL_ZERO);
+                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               GL_LockArrays(0, numverts);
+                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(0, 0);
+                               c_rt_lightmeshes++;
+                               c_rt_lighttris += numtriangles;
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(bumptexture);
+                               m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                               m.texcombinergb[0] = GL_REPLACE;
+                               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+                               m.pointer_texcoord[0] = texcoord2f;
                                m.pointer_texcoord[1] = varray_texcoord3f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
-                       }
-                       R_Mesh_State(&m);
-                       GL_LockArrays(0, numverts);
-                       GL_ColorMask(1,1,1,0);
-                       GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
-                       for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
-                       {
-                               color[0] = bound(0, color2[0], 1);
-                               color[1] = bound(0, color2[1], 1);
-                               color[2] = bound(0, color2[2], 1);
-                               GL_Color(color[0], color[1], color[2], 1);
+                               R_Mesh_State(&m);
+                               GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
+                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
+                               GL_LockArrays(0, numverts);
                                R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(basetexture);
+                               m.pointer_texcoord[0] = texcoord2f;
+                               if (lightcubemap)
+                               {
+                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
+                                       m.pointer_texcoord[1] = varray_texcoord3f[1];
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                               }
+                               R_Mesh_State(&m);
+                               GL_LockArrays(0, numverts);
+                               GL_ColorMask(1,1,1,0);
+                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+                               VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
+                               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                               {
+                                       color[0] = bound(0, color2[0], 1);
+                                       color[1] = bound(0, color2[1], 1);
+                                       color[2] = bound(0, color2[2], 1);
+                                       GL_Color(color[0], color[1], color[2], 1);
+                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       c_rt_lightmeshes++;
+                                       c_rt_lighttris += numtriangles;
+                               }
+                               GL_LockArrays(0, 0);
                        }
-                       GL_LockArrays(0, 0);
-               }
-               else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
-               {
-                       // 2/2 3D combine path (original Radeon)
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(bumptexture);
-                       m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
-                       m.texcombinergb[0] = GL_REPLACE;
-                       m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                       m.pointer_texcoord[0] = texcoord2f;
-                       m.pointer_texcoord[1] = varray_texcoord3f[1];
-                       R_Mesh_State(&m);
-                       GL_ColorMask(0,0,0,1);
-                       GL_BlendFunc(GL_ONE, GL_ZERO);
-                       R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
-                       GL_LockArrays(0, numverts);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       GL_LockArrays(0, 0);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(basetexture);
-                       m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
-                       m.pointer_texcoord[0] = texcoord2f;
-                       m.pointer_texcoord[1] = varray_texcoord3f[1];
-                       R_Mesh_State(&m);
-                       GL_LockArrays(0, numverts);
-                       GL_ColorMask(1,1,1,0);
-                       GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
-                       VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
-                       for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
                        {
-                               color[0] = bound(0, color2[0], 1);
-                               color[1] = bound(0, color2[1], 1);
-                               color[2] = bound(0, color2[2], 1);
-                               GL_Color(color[0], color[1], color[2], 1);
+                               // 2/2 3D combine path (original Radeon)
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(bumptexture);
+                               m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                               m.texcombinergb[0] = GL_REPLACE;
+                               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+                               m.pointer_texcoord[0] = texcoord2f;
+                               m.pointer_texcoord[1] = varray_texcoord3f[1];
+                               R_Mesh_State(&m);
+                               GL_ColorMask(0,0,0,1);
+                               GL_BlendFunc(GL_ONE, GL_ZERO);
+                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
+                               GL_LockArrays(0, numverts);
                                R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
-                       }
-                       GL_LockArrays(0, 0);
-               }
-               else if (r_textureunits.integer >= 4)
-               {
-                       // 4/2 2D combine path (Geforce3, Radeon 8500)
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(bumptexture);
-                       m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
-                       m.texcombinergb[0] = GL_REPLACE;
-                       m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                       m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
-                       m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
-                       m.pointer_texcoord[0] = texcoord2f;
-                       m.pointer_texcoord[1] = varray_texcoord3f[1];
-                       m.pointer_texcoord[2] = varray_texcoord2f[2];
-                       m.pointer_texcoord[3] = varray_texcoord2f[3];
-                       R_Mesh_State(&m);
-                       GL_ColorMask(0,0,0,1);
-                       GL_BlendFunc(GL_ONE, GL_ZERO);
-                       R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
-                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
-                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
-                       GL_LockArrays(0, numverts);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       GL_LockArrays(0, 0);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(basetexture);
-                       m.pointer_texcoord[0] = texcoord2f;
-                       if (lightcubemap)
-                       {
-                               m.texcubemap[1] = R_GetTexture(lightcubemap);
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(basetexture);
+                               m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
+                               m.pointer_texcoord[0] = texcoord2f;
                                m.pointer_texcoord[1] = varray_texcoord3f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                               R_Mesh_State(&m);
+                               GL_LockArrays(0, numverts);
+                               GL_ColorMask(1,1,1,0);
+                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
+                               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                               {
+                                       color[0] = bound(0, color2[0], 1);
+                                       color[1] = bound(0, color2[1], 1);
+                                       color[2] = bound(0, color2[2], 1);
+                                       GL_Color(color[0], color[1], color[2], 1);
+                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       c_rt_lightmeshes++;
+                                       c_rt_lighttris += numtriangles;
+                               }
+                               GL_LockArrays(0, 0);
                        }
-                       R_Mesh_State(&m);
-                       GL_LockArrays(0, numverts);
-                       GL_ColorMask(1,1,1,0);
-                       GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
-                       for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                       else if (r_textureunits.integer >= 4)
                        {
-                               color[0] = bound(0, color2[0], 1);
-                               color[1] = bound(0, color2[1], 1);
-                               color[2] = bound(0, color2[2], 1);
-                               GL_Color(color[0], color[1], color[2], 1);
+                               // 4/2 2D combine path (Geforce3, Radeon 8500)
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(bumptexture);
+                               m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                               m.texcombinergb[0] = GL_REPLACE;
+                               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+                               m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
+                               m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
+                               m.pointer_texcoord[0] = texcoord2f;
+                               m.pointer_texcoord[1] = varray_texcoord3f[1];
+                               m.pointer_texcoord[2] = varray_texcoord2f[2];
+                               m.pointer_texcoord[3] = varray_texcoord2f[3];
+                               R_Mesh_State(&m);
+                               GL_ColorMask(0,0,0,1);
+                               GL_BlendFunc(GL_ONE, GL_ZERO);
+                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
+                               GL_LockArrays(0, numverts);
                                R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(basetexture);
+                               m.pointer_texcoord[0] = texcoord2f;
+                               if (lightcubemap)
+                               {
+                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
+                                       m.pointer_texcoord[1] = varray_texcoord3f[1];
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                               }
+                               R_Mesh_State(&m);
+                               GL_LockArrays(0, numverts);
+                               GL_ColorMask(1,1,1,0);
+                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+                               VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
+                               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                               {
+                                       color[0] = bound(0, color2[0], 1);
+                                       color[1] = bound(0, color2[1], 1);
+                                       color[2] = bound(0, color2[2], 1);
+                                       GL_Color(color[0], color[1], color[2], 1);
+                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       c_rt_lightmeshes++;
+                                       c_rt_lighttris += numtriangles;
+                               }
+                               GL_LockArrays(0, 0);
                        }
-                       GL_LockArrays(0, 0);
-               }
-               else
-               {
-                       // 2/2/2 2D combine path (any dot3 card)
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
-                       m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-                       m.pointer_texcoord[0] = varray_texcoord2f[0];
-                       m.pointer_texcoord[1] = varray_texcoord2f[1];
-                       R_Mesh_State(&m);
-                       GL_ColorMask(0,0,0,1);
-                       GL_BlendFunc(GL_ONE, GL_ZERO);
-                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
-                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
-                       GL_LockArrays(0, numverts);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       GL_LockArrays(0, 0);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(bumptexture);
-                       m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
-                       m.texcombinergb[0] = GL_REPLACE;
-                       m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                       m.pointer_texcoord[0] = texcoord2f;
-                       m.pointer_texcoord[1] = varray_texcoord3f[1];
-                       R_Mesh_State(&m);
-                       GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-                       R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
-                       GL_LockArrays(0, numverts);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       GL_LockArrays(0, 0);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(basetexture);
-                       m.pointer_texcoord[0] = texcoord2f;
-                       if (lightcubemap)
+                       else
                        {
-                               m.texcubemap[1] = R_GetTexture(lightcubemap);
+                               // 2/2/2 2D combine path (any dot3 card)
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
+                               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+                               m.pointer_texcoord[0] = varray_texcoord2f[0];
+                               m.pointer_texcoord[1] = varray_texcoord2f[1];
+                               R_Mesh_State(&m);
+                               GL_ColorMask(0,0,0,1);
+                               GL_BlendFunc(GL_ONE, GL_ZERO);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
+                               GL_LockArrays(0, numverts);
+                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(0, 0);
+                               c_rt_lightmeshes++;
+                               c_rt_lighttris += numtriangles;
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(bumptexture);
+                               m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                               m.texcombinergb[0] = GL_REPLACE;
+                               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+                               m.pointer_texcoord[0] = texcoord2f;
                                m.pointer_texcoord[1] = varray_texcoord3f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
-                       }
-                       R_Mesh_State(&m);
-                       GL_LockArrays(0, numverts);
-                       GL_ColorMask(1,1,1,0);
-                       GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
-                       for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
-                       {
-                               color[0] = bound(0, color2[0], 1);
-                               color[1] = bound(0, color2[1], 1);
-                               color[2] = bound(0, color2[2], 1);
-                               GL_Color(color[0], color[1], color[2], 1);
+                               R_Mesh_State(&m);
+                               GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
+                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
+                               GL_LockArrays(0, numverts);
                                R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(basetexture);
+                               m.pointer_texcoord[0] = texcoord2f;
+                               if (lightcubemap)
+                               {
+                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
+                                       m.pointer_texcoord[1] = varray_texcoord3f[1];
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                               }
+                               R_Mesh_State(&m);
+                               GL_LockArrays(0, numverts);
+                               GL_ColorMask(1,1,1,0);
+                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+                               VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
+                               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                               {
+                                       color[0] = bound(0, color2[0], 1);
+                                       color[1] = bound(0, color2[1], 1);
+                                       color[2] = bound(0, color2[2], 1);
+                                       GL_Color(color[0], color[1], color[2], 1);
+                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       c_rt_lightmeshes++;
+                                       c_rt_lighttris += numtriangles;
+                               }
+                               GL_LockArrays(0, 0);
                        }
-                       GL_LockArrays(0, 0);
-               }
-       }
-       else
-       {
-               GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
-               GL_DepthMask(false);
-               GL_DepthTest(true);
-               VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
-               memset(&m, 0, sizeof(m));
-               m.pointer_vertex = vertex3f;
-               m.pointer_color = varray_color4f;
-               m.tex[0] = R_GetTexture(basetexture);
-               m.pointer_texcoord[0] = texcoord2f;
-               if (r_textureunits.integer >= 2)
-               {
-                       // voodoo2
-                       m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-                       m.pointer_texcoord[1] = varray_texcoord2f[1];
-                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
-               }
-               R_Mesh_State(&m);
-               GL_LockArrays(0, numverts);
-               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
-               {
-                       color[0] = bound(0, color2[0], 1);
-                       color[1] = bound(0, color2[1], 1);
-                       color[2] = bound(0, color2[2], 1);
-                       if (r_textureunits.integer >= 2)
-                               R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltolight);
-                       else
-                               R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltolight);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
                }
-               GL_LockArrays(0, 0);
-       }
-}
-
-void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, const float *relativeeyeorigin, const float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
-{
-       int renders;
-       float color[3], color2[3], colorscale;
-       rmeshstate_t m;
-       if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
-               return;
-       if (!glosstexture)
-               glosstexture = r_shadow_blankglosstexture;
-       if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
-       {
-               colorscale = r_shadow_glossintensity.value;
-               if (!bumptexture)
-                       bumptexture = r_shadow_blankbumptexture;
-               if (glosstexture == r_shadow_blankglosstexture)
-                       colorscale *= r_shadow_gloss2intensity.value;
-               GL_Color(1,1,1,1);
-               if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
+               if ((lighting & LIGHTING_SPECULAR) && (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture)))
                {
-                       // 2/0/0/1/2 3D combine blendsquare path
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(bumptexture);
-                       m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
-                       m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                       m.pointer_texcoord[0] = texcoord2f;
-                       m.pointer_texcoord[1] = varray_texcoord3f[1];
-                       R_Mesh_State(&m);
-                       GL_ColorMask(0,0,0,1);
-                       // this squares the result
-                       GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
-                       R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
-                       GL_LockArrays(0, numverts);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       GL_LockArrays(0, 0);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       R_Mesh_State(&m);
-                       GL_LockArrays(0, numverts);
-                       // square alpha in framebuffer a few times to make it shiny
-                       GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
-                       // these comments are a test run through this math for intensity 0.5
-                       // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
-                       // 0.25 * 0.25 = 0.0625 (this is another pass)
-                       // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-                       GL_LockArrays(0, 0);
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
-                       m.pointer_texcoord[0] = varray_texcoord3f[0];
-                       R_Mesh_State(&m);
-                       GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
-                       GL_LockArrays(0, numverts);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       GL_LockArrays(0, 0);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(glosstexture);
-                       if (lightcubemap)
+                       colorscale = r_shadow_glossintensity.value;
+                       if (glosstexture == r_shadow_blankglosstexture)
+                               colorscale *= r_shadow_gloss2intensity.value;
+                       GL_Color(1,1,1,1);
+                       if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
                        {
-                               m.texcubemap[1] = R_GetTexture(lightcubemap);
+                               // 2/0/0/1/2 3D combine blendsquare path
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(bumptexture);
+                               m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+                               m.pointer_texcoord[0] = texcoord2f;
                                m.pointer_texcoord[1] = varray_texcoord3f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                               R_Mesh_State(&m);
+                               GL_ColorMask(0,0,0,1);
+                               // this squares the result
+                               GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
+                               R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
+                               GL_LockArrays(0, numverts);
+                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(0, 0);
+                               c_rt_lightmeshes++;
+                               c_rt_lighttris += numtriangles;
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               R_Mesh_State(&m);
+                               GL_LockArrays(0, numverts);
+                               // square alpha in framebuffer a few times to make it shiny
+                               GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
+                               // these comments are a test run through this math for intensity 0.5
+                               // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
+                               // 0.25 * 0.25 = 0.0625 (this is another pass)
+                               // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
+                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               c_rt_lightmeshes++;
+                               c_rt_lighttris += numtriangles;
+                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               c_rt_lightmeshes++;
+                               c_rt_lighttris += numtriangles;
+                               GL_LockArrays(0, 0);
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
+                               m.pointer_texcoord[0] = varray_texcoord3f[0];
+                               R_Mesh_State(&m);
+                               GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
+                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               GL_LockArrays(0, numverts);
+                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(0, 0);
+                               c_rt_lightmeshes++;
+                               c_rt_lighttris += numtriangles;
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(glosstexture);
+                               if (lightcubemap)
+                               {
+                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
+                                       m.pointer_texcoord[1] = varray_texcoord3f[1];
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                               }
+                               m.pointer_texcoord[0] = texcoord2f;
+                               R_Mesh_State(&m);
+                               GL_LockArrays(0, numverts);
+                               GL_ColorMask(1,1,1,0);
+                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+                               VectorScale(lightcolor, colorscale, color2);
+                               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                               {
+                                       color[0] = bound(0, color2[0], 1);
+                                       color[1] = bound(0, color2[1], 1);
+                                       color[2] = bound(0, color2[2], 1);
+                                       GL_Color(color[0], color[1], color[2], 1);
+                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       c_rt_lightmeshes++;
+                                       c_rt_lighttris += numtriangles;
+                               }
+                               GL_LockArrays(0, 0);
                        }
-                       m.pointer_texcoord[0] = texcoord2f;
-                       R_Mesh_State(&m);
-                       GL_LockArrays(0, numverts);
-                       GL_ColorMask(1,1,1,0);
-                       GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       VectorScale(lightcolor, colorscale, color2);
-                       for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
                        {
-                               color[0] = bound(0, color2[0], 1);
-                               color[1] = bound(0, color2[1], 1);
-                               color[2] = bound(0, color2[2], 1);
-                               GL_Color(color[0], color[1], color[2], 1);
+                               // 2/0/0/2 3D combine blendsquare path
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(bumptexture);
+                               m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+                               m.pointer_texcoord[0] = texcoord2f;
+                               m.pointer_texcoord[1] = varray_texcoord3f[1];
+                               R_Mesh_State(&m);
+                               GL_ColorMask(0,0,0,1);
+                               // this squares the result
+                               GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
+                               R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
+                               GL_LockArrays(0, numverts);
+                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(0, 0);
+                               c_rt_lightmeshes++;
+                               c_rt_lighttris += numtriangles;
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               R_Mesh_State(&m);
+                               GL_LockArrays(0, numverts);
+                               // square alpha in framebuffer a few times to make it shiny
+                               GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
+                               // these comments are a test run through this math for intensity 0.5
+                               // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
+                               // 0.25 * 0.25 = 0.0625 (this is another pass)
+                               // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
                                R_Mesh_Draw(numverts, numtriangles, elements);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
+                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               c_rt_lightmeshes++;
+                               c_rt_lighttris += numtriangles;
+                               GL_LockArrays(0, 0);
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(glosstexture);
+                               m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
+                               m.pointer_texcoord[0] = texcoord2f;
+                               m.pointer_texcoord[1] = varray_texcoord3f[1];
+                               R_Mesh_State(&m);
+                               GL_LockArrays(0, numverts);
+                               GL_ColorMask(1,1,1,0);
+                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               VectorScale(lightcolor, colorscale, color2);
+                               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                               {
+                                       color[0] = bound(0, color2[0], 1);
+                                       color[1] = bound(0, color2[1], 1);
+                                       color[2] = bound(0, color2[2], 1);
+                                       GL_Color(color[0], color[1], color[2], 1);
+                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       c_rt_lightmeshes++;
+                                       c_rt_lighttris += numtriangles;
+                               }
+                               GL_LockArrays(0, 0);
                        }
-                       GL_LockArrays(0, 0);
-               }
-               else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
-               {
-                       // 2/0/0/2 3D combine blendsquare path
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(bumptexture);
-                       m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
-                       m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                       m.pointer_texcoord[0] = texcoord2f;
-                       m.pointer_texcoord[1] = varray_texcoord3f[1];
-                       R_Mesh_State(&m);
-                       GL_ColorMask(0,0,0,1);
-                       // this squares the result
-                       GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
-                       R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
-                       GL_LockArrays(0, numverts);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       GL_LockArrays(0, 0);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       R_Mesh_State(&m);
-                       GL_LockArrays(0, numverts);
-                       // square alpha in framebuffer a few times to make it shiny
-                       GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
-                       // these comments are a test run through this math for intensity 0.5
-                       // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
-                       // 0.25 * 0.25 = 0.0625 (this is another pass)
-                       // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-                       GL_LockArrays(0, 0);
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(glosstexture);
-                       m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
-                       m.pointer_texcoord[0] = texcoord2f;
-                       m.pointer_texcoord[1] = varray_texcoord3f[1];
-                       R_Mesh_State(&m);
-                       GL_LockArrays(0, numverts);
-                       GL_ColorMask(1,1,1,0);
-                       GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
-                       VectorScale(lightcolor, colorscale, color2);
-                       for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                       else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
                        {
-                               color[0] = bound(0, color2[0], 1);
-                               color[1] = bound(0, color2[1], 1);
-                               color[2] = bound(0, color2[2], 1);
-                               GL_Color(color[0], color[1], color[2], 1);
+                               // 2/0/0/2/2 2D combine blendsquare path
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(bumptexture);
+                               m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+                               m.pointer_texcoord[0] = texcoord2f;
+                               m.pointer_texcoord[1] = varray_texcoord3f[1];
+                               R_Mesh_State(&m);
+                               GL_ColorMask(0,0,0,1);
+                               // this squares the result
+                               GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
+                               R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
+                               GL_LockArrays(0, numverts);
                                R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               R_Mesh_State(&m);
+                               GL_LockArrays(0, numverts);
+                               // square alpha in framebuffer a few times to make it shiny
+                               GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
+                               // these comments are a test run through this math for intensity 0.5
+                               // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
+                               // 0.25 * 0.25 = 0.0625 (this is another pass)
+                               // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
+                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               c_rt_lightmeshes++;
+                               c_rt_lighttris += numtriangles;
+                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               c_rt_lightmeshes++;
+                               c_rt_lighttris += numtriangles;
+                               GL_LockArrays(0, 0);
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
+                               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+                               m.pointer_texcoord[0] = varray_texcoord2f[0];
+                               m.pointer_texcoord[1] = varray_texcoord2f[1];
+                               R_Mesh_State(&m);
+                               GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
+                               GL_LockArrays(0, numverts);
+                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(0, 0);
+                               c_rt_lightmeshes++;
+                               c_rt_lighttris += numtriangles;
+       
+                               memset(&m, 0, sizeof(m));
+                               m.pointer_vertex = vertex3f;
+                               m.tex[0] = R_GetTexture(glosstexture);
+                               if (lightcubemap)
+                               {
+                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
+                                       m.pointer_texcoord[1] = varray_texcoord3f[1];
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                               }
+                               m.pointer_texcoord[0] = texcoord2f;
+                               R_Mesh_State(&m);
+                               GL_LockArrays(0, numverts);
+                               GL_ColorMask(1,1,1,0);
+                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+                               VectorScale(lightcolor, colorscale, color2);
+                               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                               {
+                                       color[0] = bound(0, color2[0], 1);
+                                       color[1] = bound(0, color2[1], 1);
+                                       color[2] = bound(0, color2[2], 1);
+                                       GL_Color(color[0], color[1], color[2], 1);
+                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       c_rt_lightmeshes++;
+                                       c_rt_lighttris += numtriangles;
+                               }
+                               GL_LockArrays(0, 0);
                        }
-                       GL_LockArrays(0, 0);
                }
-               else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
+       }
+       else
+       {
+               if (lighting & LIGHTING_DIFFUSE)
                {
-                       // 2/0/0/2/2 2D combine blendsquare path
+                       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+                       GL_DepthMask(false);
+                       GL_DepthTest(true);
+                       VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
                        memset(&m, 0, sizeof(m));
                        m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(bumptexture);
-                       m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
-                       m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+                       m.pointer_color = varray_color4f;
+                       m.tex[0] = R_GetTexture(basetexture);
                        m.pointer_texcoord[0] = texcoord2f;
-                       m.pointer_texcoord[1] = varray_texcoord3f[1];
-                       R_Mesh_State(&m);
-                       GL_ColorMask(0,0,0,1);
-                       // this squares the result
-                       GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
-                       R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
-                       GL_LockArrays(0, numverts);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       GL_LockArrays(0, 0);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       R_Mesh_State(&m);
-                       GL_LockArrays(0, numverts);
-                       // square alpha in framebuffer a few times to make it shiny
-                       GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
-                       // these comments are a test run through this math for intensity 0.5
-                       // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
-                       // 0.25 * 0.25 = 0.0625 (this is another pass)
-                       // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-                       GL_LockArrays(0, 0);
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
-                       m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-                       m.pointer_texcoord[0] = varray_texcoord2f[0];
-                       m.pointer_texcoord[1] = varray_texcoord2f[1];
-                       R_Mesh_State(&m);
-                       GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
-                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
-                       GL_LockArrays(0, numverts);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-                       GL_LockArrays(0, 0);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
-
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(glosstexture);
-                       if (lightcubemap)
+                       if (r_textureunits.integer >= 2)
                        {
-                               m.texcubemap[1] = R_GetTexture(lightcubemap);
-                               m.pointer_texcoord[1] = varray_texcoord3f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                               // voodoo2
+                               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+                               m.pointer_texcoord[1] = varray_texcoord2f[1];
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
                        }
-                       m.pointer_texcoord[0] = texcoord2f;
                        R_Mesh_State(&m);
                        GL_LockArrays(0, numverts);
-                       GL_ColorMask(1,1,1,0);
-                       GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       VectorScale(lightcolor, colorscale, color2);
                        for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
                        {
                                color[0] = bound(0, color2[0], 1);
                                color[1] = bound(0, color2[1], 1);
                                color[2] = bound(0, color2[2], 1);
-                               GL_Color(color[0], color[1], color[2], 1);
+                               if (r_textureunits.integer >= 2)
+                                       R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltolight);
+                               else
+                                       R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltolight);
                                R_Mesh_Draw(numverts, numtriangles, elements);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
@@ -1763,14 +1790,14 @@ void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int i
        VectorCopy(light->origin, rtlight->shadoworigin);
        VectorCopy(light->color, rtlight->color);
        rtlight->radius = light->radius;
-       rtlight->cullradius = rtlight->radius;
-       rtlight->cullradius2 = rtlight->cullradius * rtlight->cullradius;
-       rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->cullradius;
-       rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->cullradius;
-       rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->cullradius;
-       rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->cullradius;
-       rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->cullradius;
-       rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->cullradius;
+       //rtlight->cullradius = rtlight->radius;
+       //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
+       rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
+       rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
+       rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
+       rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
+       rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
+       rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
        rtlight->cubemapname[0] = 0;
        if (light->cubemapname[0])
                strcpy(rtlight->cubemapname, light->cubemapname);
@@ -1796,253 +1823,91 @@ void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int i
        rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
 }
 
+rtlight_t *r_shadow_compilingrtlight;
+
 // compiles rtlight geometry
 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
 void R_RTLight_Compile(rtlight_t *rtlight)
 {
-       int i, j, k, l, maxverts = 256, tris;
-       float *vertex3f = NULL, mins[3], maxs[3];
-       shadowmesh_t *mesh, *castmesh = NULL;
-       int lightpvsbytes;
-       qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
-       qbyte lightfullpvs[(MAX_MAP_LEAFS + 7)/ 8];
+       int shadowmeshes, shadowtris, lightmeshes, lighttris, numclusters, numsurfaces;
+       entity_render_t *ent = &cl_entities[0].render;
+       model_t *model = ent->model;
 
        // compile the light
        rtlight->compiled = true;
-       VectorCopy(rtlight->cullmins, mins);
-       VectorCopy(rtlight->cullmaxs, maxs);
-       if (rtlight->shadow)
-               castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
-       rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
-       if (cl.worldmodel)
-       {
-               lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, rtlight->shadoworigin, 0, lightfullpvs, sizeof(lightfullpvs));
-               memset(lightpvs, 0, lightpvsbytes);
-               if (cl.worldmodel->brushq3.num_leafs)
+       rtlight->static_numclusters = 0;
+       rtlight->static_numclusterpvsbytes = 0;
+       rtlight->static_clusterlist = NULL;
+       rtlight->static_clusterpvs = NULL;
+       rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
+       rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
+       rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
+       rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
+       rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
+       rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
+
+       if (model && model->GetLightInfo)
+       {
+               // this variable directs the DrawShadowVolume and DrawLight code to capture into the mesh chain instead of rendering
+               r_shadow_compilingrtlight = rtlight;
+               R_Shadow_EnlargeClusterBuffer(model->brush.num_pvsclusters);
+               R_Shadow_EnlargeSurfaceBuffer(model->numsurfaces); 
+               model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_clusterlist, r_shadow_buffer_clusterpvs, &numclusters, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
+               if (numclusters)
                {
-                       q3mleaf_t *leaf;
-                       q3mface_t *face;
-
-                       // make a pvs that only includes things within the box
-                       for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
-                               if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
-                                       SETPVSBIT(lightpvs, leaf->clusterindex);
-
-                       // make a cluster list for fast visibility checking during rendering
-                       for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
-                               if (CHECKPVSBIT(lightpvs, i))
-                                       rtlight->static_numclusters++;
-                       rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
-                       for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
-                               if (CHECKPVSBIT(lightpvs, i))
-                                       rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
-
-                       VectorCopy(rtlight->shadoworigin, rtlight->cullmins);
-                       VectorCopy(rtlight->shadoworigin, rtlight->cullmaxs);
-                       for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
-                               face->lighttemp_castshadow = false;
-                       for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
-                       {
-                               if (CHECKPVSBIT(lightpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
-                               {
-                                       for (k = 0;k < 3;k++)
-                                       {
-                                               if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
-                                               if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
-                                       }
-                                       for (j = 0;j < leaf->numleaffaces;j++)
-                                       {
-                                               face = leaf->firstleafface[j];
-                                               if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
-                                                       face->lighttemp_castshadow = true;
-                                       }
-                               }
-                       }
-
-                       // add surfaces to shadow casting mesh and light mesh
-                       for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
-                       {
-                               if (face->lighttemp_castshadow)
-                               {
-                                       face->lighttemp_castshadow = false;
-                                       if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
-                                       {
-                                               if (rtlight->shadow)
-                                                       if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
-                                                               Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
-                                               if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
-                                                       Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_light, face->texture->skin.base, face->texture->skin.gloss, face->texture->skin.nmap, face->data_vertex3f, face->data_svector3f, face->data_tvector3f, face->data_normal3f, face->data_texcoordtexture2f, face->num_triangles, face->data_element3i);
-                                       }
-                               }
-                       }
+                       rtlight->static_numclusters = numclusters;
+                       rtlight->static_numclusterpvsbytes = (model->brush.num_pvsclusters + 7) >> 3;
+                       rtlight->static_clusterlist = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
+                       rtlight->static_clusterpvs = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusterpvsbytes);
+                       memcpy(rtlight->static_clusterlist, r_shadow_buffer_clusterlist, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
+                       memcpy(rtlight->static_clusterpvs, r_shadow_buffer_clusterpvs, rtlight->static_numclusterpvsbytes);
                }
-               else if (cl.worldmodel->brushq1.num_leafs)
+               if (model->DrawShadowVolume && rtlight->shadow)
                {
-                       mleaf_t *leaf;
-                       msurface_t *surf;
-                       VectorCopy(rtlight->shadoworigin, rtlight->cullmins);
-                       VectorCopy(rtlight->shadoworigin, rtlight->cullmaxs);
-                       i = CL_PointQ1Contents(rtlight->shadoworigin);
-
-                       for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
-                               surf->lighttemp_castshadow = false;
-
-                       if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
-                       {
-                               qbyte *byteleafpvs;
-                               qbyte *bytesurfacepvs;
-
-                               byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.num_leafs);
-                               bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
-
-                               Portal_Visibility(cl.worldmodel, rtlight->shadoworigin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, rtlight->cullmins, rtlight->cullmaxs);
-
-                               // make a pvs that only includes things within the box
-                               for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
-                               {
-                                       if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
-                                       {
-                                               SETPVSBIT(lightpvs, leaf->clusterindex);
-                                               for (k = 0;k < 3;k++)
-                                               {
-                                                       if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
-                                                       if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
-                                               }
-                                       }
-                               }
-       
-                               for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
-                                       if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
-                                               surf->lighttemp_castshadow = true;
-
-                               Mem_Free(byteleafpvs);
-                               Mem_Free(bytesurfacepvs);
-       
-                               // make a cluster list for fast visibility checking during rendering
-                               for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
-                                       if (CHECKPVSBIT(lightpvs, i))
-                                               rtlight->static_numclusters++;
-                               rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
-                               for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
-                                       if (CHECKPVSBIT(lightpvs, i))
-                                               rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
-                       }
-                       else
-                       {
-                               for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
-                               {
-                                       if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
-                                       {
-                                               // make a pvs that only includes things within the box
-                                               SETPVSBIT(lightpvs, leaf->clusterindex);
-                                               for (k = 0;k < 3;k++)
-                                               {
-                                                       if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
-                                                       if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
-                                               }
-                                               for (j = 0;j < leaf->nummarksurfaces;j++)
-                                               {
-                                                       surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
-                                                       if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
-                                                               surf->lighttemp_castshadow = true;
-                                               }
-                                       }
-                               }
-
-                               // make a pvs that only includes things within the box
-                               for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
-                                       if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
-                                               SETPVSBIT(lightpvs, leaf->clusterindex);
-
-                               // make a cluster list for fast visibility checking during rendering
-                               for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
-                                       if (CHECKPVSBIT(lightpvs, i))
-                                               rtlight->static_numclusters++;
-                               rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
-                               for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
-                                       if (CHECKPVSBIT(lightpvs, i))
-                                               rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
-                       }
-
-                       // add surfaces to shadow casting mesh and light mesh
-                       for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
-                       {
-                               if (surf->lighttemp_castshadow)
-                               {
-                                       surf->lighttemp_castshadow = false;
-                                       if (rtlight->shadow && (surf->flags & SURF_SHADOWCAST))
-                                               Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, surf->mesh.data_vertex3f, NULL, NULL, NULL, NULL, surf->mesh.num_triangles, surf->mesh.data_element3i);
-                                       if (!(surf->flags & SURF_DRAWSKY))
-                                               Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_light, surf->texinfo->texture->skin.base, surf->texinfo->texture->skin.gloss, surf->texinfo->texture->skin.nmap, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, surf->mesh.num_triangles, surf->mesh.data_element3i);
-                               }
-                       }
+                       rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
+                       model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
+                       rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
+               }
+               if (model->DrawLight)
+               {
+                       rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
+                       model->DrawLight(ent, rtlight->shadoworigin, vec3_origin, rtlight->radius, vec3_origin, &r_identitymatrix, &r_identitymatrix, &r_identitymatrix, NULL, numsurfaces, r_shadow_buffer_surfacelist);
+                       rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
                }
+               // switch back to rendering when DrawShadowVolume or DrawLight is called
+               r_shadow_compilingrtlight = NULL;
        }
 
-       // limit box to light bounds (in case it grew larger)
-       for (k = 0;k < 3;k++)
-       {
-               if (rtlight->cullmins[k] < rtlight->shadoworigin[k] - rtlight->radius) rtlight->cullmins[k] = rtlight->shadoworigin[k] - rtlight->radius;
-               if (rtlight->cullmaxs[k] > rtlight->shadoworigin[k] + rtlight->radius) rtlight->cullmaxs[k] = rtlight->shadoworigin[k] + rtlight->radius;
-       }
-       rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
-       rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
 
-       // cast shadow volume from castmesh
-       castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
-       if (castmesh)
+       // use smallest available cullradius - box radius or light radius
+       //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
+       //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
+
+       shadowmeshes = 0;
+       shadowtris = 0;
+       if (rtlight->static_meshchain_shadow)
        {
-               maxverts = 0;
-               for (mesh = castmesh;mesh;mesh = mesh->next)
+               shadowmesh_t *mesh;
+               for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
                {
-                       R_Shadow_ResizeShadowElements(mesh->numtriangles);
-                       maxverts = max(maxverts, mesh->numverts * 2);
+                       shadowmeshes++;
+                       shadowtris += mesh->numtriangles;
                }
+       }
 
-               if (maxverts > 0)
+       lightmeshes = 0;
+       lighttris = 0;
+       if (rtlight->static_meshchain_light)
+       {
+               shadowmesh_t *mesh;
+               for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
                {
-                       vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
-                       // now that we have the buffers big enough, construct and add
-                       // the shadow volume mesh
-                       if (rtlight->shadow)
-                               rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
-                       for (mesh = castmesh;mesh;mesh = mesh->next)
-                       {
-                               Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
-                               R_Shadow_PrepareShadowMark(mesh->numtriangles);
-                               for (i = 0;i < mesh->numtriangles;i++)
-                               {
-                                       const float *v[3];
-                                       v[0] = mesh->vertex3f + mesh->element3i[i*3+0] * 3;
-                                       v[1] = mesh->vertex3f + mesh->element3i[i*3+1] * 3;
-                                       v[2] = mesh->vertex3f + mesh->element3i[i*3+2] * 3;
-                                       if (PointInfrontOfTriangle(rtlight->shadoworigin, v[0], v[1], v[2]) && rtlight->cullmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && rtlight->cullmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && rtlight->cullmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && rtlight->cullmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && rtlight->cullmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && rtlight->cullmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
-                                               shadowmarklist[numshadowmark++] = i;
-                               }
-                               if (maxshadowelements < numshadowmark * 24)
-                                       R_Shadow_ResizeShadowElements((numshadowmark + 256) * 24);
-                               if ((tris = R_Shadow_ConstructShadowVolume(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->neighbor3i, mesh->vertex3f, NULL, shadowelements, vertex3f, rtlight->shadoworigin, r_shadow_projectdistance.value, numshadowmark, shadowmarklist)))
-                                       Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
-                       }
-                       Mem_Free(vertex3f);
-                       vertex3f = NULL;
+                       lightmeshes++;
+                       lighttris += mesh->numtriangles;
                }
-               // we're done with castmesh now
-               Mod_ShadowMesh_Free(castmesh);
        }
 
-       rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
-       rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
-
-       k = 0;
-       if (rtlight->static_meshchain_shadow)
-               for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
-                       k += mesh->numtriangles;
-       l = 0;
-       if (rtlight->static_meshchain_light)
-               for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
-                       l += mesh->numtriangles;
-       Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles, %i light triangles\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], k, l);
+       Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles (in %i meshes), %i light triangles (in %i meshes)\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], shadowtris, shadowmeshes, lighttris, lightmeshes);
 }
 
 void R_RTLight_Uncompile(rtlight_t *rtlight)
@@ -2055,27 +1920,20 @@ void R_RTLight_Uncompile(rtlight_t *rtlight)
                if (rtlight->static_meshchain_light)
                        Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
                rtlight->static_meshchain_light = NULL;
-               if (rtlight->static_clusterindices)
-                       Mem_Free(rtlight->static_clusterindices);
-               rtlight->static_clusterindices = NULL;
+               if (rtlight->static_clusterlist)
+                       Mem_Free(rtlight->static_clusterlist);
+               rtlight->static_clusterlist = NULL;
+               if (rtlight->static_clusterpvs)
+                       Mem_Free(rtlight->static_clusterpvs);
+               rtlight->static_clusterpvs = NULL;
                rtlight->static_numclusters = 0;
+               rtlight->static_numclusterpvsbytes = 0;
                rtlight->compiled = false;
        }
 }
 
 int shadowframecount = 0;
 
-void R_TestAndDrawShadowVolume(entity_render_t *ent, vec3_t shadoworigin, vec_t shadowradius, vec3_t cullmins, vec3_t cullmaxs)
-{
-       // rough checks
-       if ((BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) || !r_shadow_cull.integer) && (ent->flags & RENDER_SHADOW) && ent->model && ent->model->DrawShadowVolume)
-       {
-               vec3_t relativeshadoworigin;
-               Matrix4x4_Transform(&ent->inversematrix, shadoworigin, relativeshadoworigin);
-               ent->model->DrawShadowVolume (ent, relativeshadoworigin, shadowradius);
-       }
-}
-
 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
 
 void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
@@ -2086,27 +1944,60 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
        vec3_t relativelightorigin, relativeeyeorigin, lightcolor;
        rtexture_t *cubemaptexture;
        matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
+       int numclusters, numsurfaces;
+       int *clusterlist, *surfacelist;
+       qbyte *clusterpvs;
+       vec3_t cullmins, cullmaxs;
+       shadowmesh_t *mesh;
+       rmeshstate_t m;
 
        if (d_lightstylevalue[rtlight->style] <= 0)
                return;
-       if (rtlight->compiled)
-       {
-               if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
-                       return;
-               for (i = 0;i < rtlight->static_numclusters;i++)
-                       if (CHECKPVSBIT(r_pvsbits, rtlight->static_clusterindices[i]))
+       cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
+       cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
+       cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
+       cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
+       cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
+       cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
+       if (R_CullBox(cullmins, cullmaxs))
+               return;
+       if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
+               R_RTLight_Compile(rtlight);
+       numclusters = 0;
+       clusterlist = NULL;
+       clusterpvs = NULL;
+       numsurfaces = 0;
+       surfacelist = NULL;
+       if (rtlight->compiled && r_shadow_staticworldlights.integer)
+       {
+               numclusters = rtlight->static_numclusters;
+               clusterlist = rtlight->static_clusterlist;
+               clusterpvs = rtlight->static_clusterpvs;
+               VectorCopy(rtlight->cullmins, cullmins);
+               VectorCopy(rtlight->cullmaxs, cullmaxs);
+       }
+       else if (cl.worldmodel && cl.worldmodel->GetLightInfo)
+       {
+               R_Shadow_EnlargeClusterBuffer(cl.worldmodel->brush.num_pvsclusters);
+               R_Shadow_EnlargeSurfaceBuffer(cl.worldmodel->numsurfaces); 
+               cl.worldmodel->GetLightInfo(&cl_entities[0].render, rtlight->shadoworigin, rtlight->radius, cullmins, cullmaxs, r_shadow_buffer_clusterlist, r_shadow_buffer_clusterpvs, &numclusters, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
+               clusterlist = r_shadow_buffer_clusterlist;
+               clusterpvs = r_shadow_buffer_clusterpvs;
+               surfacelist = r_shadow_buffer_surfacelist;
+       }
+       if (numclusters)
+       {
+               for (i = 0;i < numclusters;i++)
+                       if (CHECKPVSBIT(r_pvsbits, clusterlist[i]))
                                break;
-               if (i == rtlight->static_numclusters)
+               if (i == numclusters)
                        return;
        }
-       else if (VIS_CullBox(rtlight->cullmins, rtlight->cullmaxs))
+       if (R_CullBox(cullmins, cullmaxs))
                return;
-       if (R_Shadow_ScissorForBBox(rtlight->cullmins, rtlight->cullmaxs))
+       if (R_Shadow_ScissorForBBox(cullmins, cullmaxs))
                return;
 
-       if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
-               R_RTLight_Compile(rtlight);
-       
        f = d_lightstylevalue[rtlight->style] * (1.0f / 256.0f);
        VectorScale(rtlight->color, f, lightcolor);
        /*
@@ -2130,15 +2021,55 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
                ent = &cl_entities[0].render;
                if (r_shadow_staticworldlights.integer && rtlight->compiled)
                {
+                       memset(&m, 0, sizeof(m));
                        R_Mesh_Matrix(&ent->matrix);
-                       R_Shadow_RenderShadowMeshVolume(rtlight->static_meshchain_shadow);
+                       for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
+                       {
+                               m.pointer_vertex = mesh->vertex3f;
+                               R_Mesh_State(&m);
+                               GL_LockArrays(0, mesh->numverts);
+                               if (r_shadowstage == SHADOWSTAGE_STENCIL)
+                               {
+                                       // decrement stencil if frontface is behind depthbuffer
+                                       qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
+                                       qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+                                       R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
+                                       c_rtcached_shadowmeshes++;
+                                       c_rtcached_shadowtris += mesh->numtriangles;
+                                       // increment stencil if backface is behind depthbuffer
+                                       qglCullFace(GL_BACK); // quake is backwards, this culls front faces
+                                       qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
+                               }
+                               R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
+                               c_rtcached_shadowmeshes++;
+                               c_rtcached_shadowtris += mesh->numtriangles;
+                               GL_LockArrays(0, 0);
+                       }
                }
                else
-                       R_TestAndDrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs);
+               {
+                       Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
+                       ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, numsurfaces, surfacelist);
+               }
                if (r_drawentities.integer)
+               {
                        for (i = 0;i < r_refdef.numentities;i++)
-                               if (r_refdef.entities[i]->flags & RENDER_SHADOW)
-                                       R_TestAndDrawShadowVolume(r_refdef.entities[i], rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs);
+                       {
+                               ent = r_refdef.entities[i];
+                               // rough checks
+                               if (r_shadow_cull.integer)
+                               {
+                                       if (!BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs))
+                                               continue;
+                                       if (cl.worldmodel != NULL && cl.worldmodel->brush.BoxTouchingPVS != NULL && !cl.worldmodel->brush.BoxTouchingPVS(cl.worldmodel, clusterpvs, ent->mins, ent->maxs))
+                                               continue;
+                               }
+                               if (!(ent->flags & RENDER_SHADOW) || !ent->model || !ent->model->DrawShadowVolume)
+                                       continue;
+                               Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
+                               ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, ent->model->numsurfaces, ent->model->surfacelist);
+                       }
+               }
        }
 
        if (!visiblevolumes)
@@ -2158,33 +2089,26 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
                        Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
                        if (r_shadow_staticworldlights.integer && rtlight->compiled)
                        {
-                               //R_Shadow_DrawStaticWorldLight_Light(rtlight, &ent->matrix, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
-                               shadowmesh_t *mesh;
                                R_Mesh_Matrix(&ent->matrix);
                                for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
-                               {
-                                       R_Shadow_DiffuseLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, mesh->map_diffuse, mesh->map_normal, cubemaptexture);
-                                       R_Shadow_SpecularLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, mesh->map_specular, mesh->map_normal, cubemaptexture);
-                               }
+                                       R_Shadow_RenderLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, mesh->map_diffuse, mesh->map_normal, mesh->map_specular, cubemaptexture, LIGHTING_DIFFUSE | LIGHTING_SPECULAR);
                        }
                        else
-                               ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
+                               ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, numsurfaces, surfacelist);
                }
                if (r_drawentities.integer)
                {
                        for (i = 0;i < r_refdef.numentities;i++)
                        {
                                ent = r_refdef.entities[i];
-                               if (ent->visframe == r_framecount && ent->model && ent->model->DrawLight
-                                && BoxesOverlap(ent->mins, ent->maxs, rtlight->cullmins, rtlight->cullmaxs)
-                                && (ent->flags & RENDER_LIGHT))
+                               if (ent->visframe == r_framecount && BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) && ent->model && ent->model->DrawLight && (ent->flags & RENDER_LIGHT))
                                {
                                        Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
                                        Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
                                        Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
                                        Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
                                        Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
-                                       ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
+                                       ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, ent->model->numsurfaces, ent->model->surfacelist);
                                }
                        }
                }
index 580e67d..bd01cd4 100644 (file)
@@ -15,16 +15,18 @@ extern cvar_t r_shadow_bumpscale_basetexture;
 extern cvar_t r_shadow_worldshadows;
 extern cvar_t r_shadow_dlightshadows;
 
+extern mempool_t *r_shadow_mempool;
+
 void R_Shadow_Init(void);
 void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, int nummarktris, const int *marktris);
 void R_Shadow_VolumeFromBox(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, const vec3_t mins, const vec3_t maxs);
 void R_Shadow_VolumeFromSphere(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, float radius);
-void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *vertices, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, const float *lightcolor, const matrix4x4_t *matrix_worldtolight, const matrix4x4_t *matrix_worldtoattenuationxyz, const matrix4x4_t *matrix_worldtoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap);
-void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *vertices, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, const float *relativeeyeorigin, const float *lightcolor, const matrix4x4_t *matrix_worldtolight, const matrix4x4_t *matrix_worldtoattenuationxyz, const matrix4x4_t *matrix_worldtoattenuationz, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap);
+#define LIGHTING_DIFFUSE 1
+#define LIGHTING_SPECULAR 2
+void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements, const float *vertices, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, const float *relativeeyeorigin, const float *lightcolor, const matrix4x4_t *matrix_worldtolight, const matrix4x4_t *matrix_worldtoattenuationxyz, const matrix4x4_t *matrix_worldtoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *glosstexture, rtexture_t *lightcubemap, int lightingflags);
 void R_Shadow_ClearStencil(void);
 
 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i);
-void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *mesh);
 void R_Shadow_Stage_Begin(void);
 void R_Shadow_LoadWorldLightsIfNeeded(void);
 void R_Shadow_Stage_ShadowVolumes(void);
@@ -43,6 +45,8 @@ extern dlight_t *r_shadow_worldlightchain;
 
 void R_Shadow_UpdateWorldLightSelection(void);
 
+extern rtlight_t *r_shadow_compilingrtlight;
+
 void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic);
 void R_RTLight_Compile(rtlight_t *rtlight);
 void R_RTLight_Uncompile(rtlight_t *rtlight);
diff --git a/todo b/todo
index edc5605..5cb49ca 100644 (file)
--- a/todo
+++ b/todo
@@ -5,7 +5,7 @@
 d darkplaces: GAME_FNIGGIUM: console doesn't show unless you manually pull it down (Sajt)
 -n darkplaces: add -benchmark commandline option which plays a demo, appends the resulting min/max/avg fps to gamedir/benchmark.log with commandline so people know what settings were used, like +exec realtimelow.cfg, +exec realtimemed.cfg, etc (romi)
 -n darkplaces: add DP_EF_NOSHADOW extension (Urre)
--n darkplaces: add PF_copyentity error checking for copying to world (yummyluv)
+d darkplaces: add PF_copyentity error checking for copying to world (yummyluv)
 -n darkplaces: add a "edictset" command to console to set a single field of an edict to the specified value
 -n darkplaces: add a config saving command (Speeds)
 -n darkplaces: add a scr_screenshot_jpeg_quality cvar (Electro)
@@ -14,11 +14,11 @@ d darkplaces: GAME_FNIGGIUM: console doesn't show unless you manually pull it do
 -n darkplaces: add cvars for sbar alpha (background and foreground) (Throvold@uboot.com)
 -n darkplaces: add rate command (and _cl_rate cvar to save to config) to control client rate (send to server on connect as a command, like other properties) (Transfusion)
 -n darkplaces: add slowmo to options menu (Cristian Beltramo)
--n darkplaces: add stats to slist menu displaying how many masters/servers have been queried and replied (tell yummyluv)
+d darkplaces: add stats to slist menu displaying how many masters/servers have been queried and replied (tell yummyluv)
 -n darkplaces: add sv_maxrate cvar (limits total rate of the server - rather complicated rules to distribute rate between clients on the server, honoring their requests as best as possible) (Transfusion)
 -n darkplaces: check out qe1 textures and make sure they load in all the e1 maps, report of crashing in most but not all maps (Linny Amore)
 -n darkplaces: crashes if you type too long a command line in the console (SeienAbunae)
--n darkplaces: display "No servers found" instead of a cursor when there are none (yummyluv)
+d darkplaces: display "No servers found" instead of a cursor when there are none (yummyluv)
 -n darkplaces: examine the surface rendering code to make sure it has no bugs regarding texture selection for any of the passes (sublim3)
 -n darkplaces: figure out what's wrong with ctrl key in Linux, hitting character keys tends to do nothing, and holding a character key and then hitting ctrl tends to leave the character key stuck on, this sounds like a window manager issue, but somehow quake3 works around it (Baalz)
 -n darkplaces: fix a crash when changing level while using qe1 textures (Todd)
@@ -33,18 +33,21 @@ d darkplaces: GAME_FNIGGIUM: console doesn't show unless you manually pull it do
 -n darkplaces: make sure PR_SetString points NULL strings at pr_strings (which would be an offset of 0) (Fuh)
 -n darkplaces: make sure that sound engine does not remove sounds when volume drops to 0 due to going out of range - now spawns sounds even if out of range (SeienAbunae)
 -n darkplaces: make the reply receive code drop packets from servers not in the list (Willis)
--n darkplaces: net_slist and the server browser should show servers when they are queried, not just when they reply; which would replace the matching entry (yummyluv)
--n darkplaces: net_slist should print out "No network." if networking is not initialized (yummyluv)
+d darkplaces: net_slist and the server browser should show servers when they are queried, not just when they reply; which would replace the matching entry (yummyluv)
+d darkplaces: net_slist should print out "No network." if networking is not initialized (yummyluv)
 -n darkplaces: physics bug: bmodels (doors, etc) hurt player if player pushes against it, and sometimes gets stuck for a frame when falling onto it (Andrew A. Gilevsky)
 -n darkplaces: q1bsp trace bug: movetogoal is broken - monsters are not going around corners, just running into walls (scar3crow)
--n darkplaces: remove dead master server from default masters list (yummyluv)
--n darkplaces: segfault reading memory in windows when starting a new server from menu (yummyluv)
--n darkplaces: server is starting before the "port" cvar is set by commandline and scripts? (yummyluv)
--n darkplaces: typing ip in join game menu should show 'trying' and 'no response' after a while, or 'no network' if networking is not initialized (yummyluv)
+d darkplaces: remove dead master server from default masters list (yummyluv)
+d darkplaces: segfault reading memory in windows when starting a new server from menu (yummyluv)
+d darkplaces: server is starting before the "port" cvar is set by commandline and scripts? (yummyluv)
+d darkplaces: typing ip in join game menu should show 'trying' and 'no response' after a while, or 'no network' if networking is not initialized (yummyluv)
 -n darkplaces: upgrade network protocol to send precise angles, and make EF_LOWPRECISION downgrade both origin and angles (Urre, -Wazat for Battlemech, FrikaC, mashakos, RenegadeC, Sajt)
 -n darkplaces: write a readme (Antti)
 -n dpmod: make grapple off-hand (joe hill)
 -n darkplaces: add DP_SV_ROTATINGBMODEL extension to explain that MOVETYPE_PUSH/SOLID_BSP support rotation in darkplaces and a demonstration of how to use it without qc modifications (Uffe, Supajoe)
+0 dpmod: make spawning use viewzoom to start zoomed out 2.0 and then zoom in to 1.0 (Urre)
+0 darkplaces: add DP_SENSITIVITYSCALE extension which scales sensitivity on client like viewzoom does, but without affecting fov, note if this is non-zero it overrides viewzoom sensitivity entirely, it does not scale it (Urre)
+0 darkplaces: bump protocol number again and expand viewzoom to two bytes (8bit.8bit fixedpoint instead of 0.8bit like it is now) (Urre)
 0 darkplaces: fix the weird broken config parsing at startup
 0 darkplaces: make a DP_EF_NODEPTHTEST extension which causes an entity to show through walls, useful for navigation markers (Urre)
 2 darkplaces: make a DP_SV_PERCLIENTENTITYSEND extension which calls a .float customizeentityforclient() function for each client that may see the entity, the function returns TRUE if it should send, FALSE if it should not, and is fully capable of editing the entity's fields, this allows cloaked players to appear less transparent to their teammates, navigation markers to only show to their team, etc (Urre)
@@ -154,7 +157,7 @@ d darkplaces: shadow volume rendering should not unlock the arrays between rende
 0 darkplaces: optimize R_Q1BSP_BoxTouchingPVS and R_Q3BSP_BoxTouchingPVS to check pvsframe on nodes as well as leafs (Vic)
 0 darkplaces: optimize R_Q3BSP_RecursiveWorldNode to take clipflags parameter and do not cull a node against a plane if the parent node is totally on one side of the plane (Vic)
 0 darkplaces: player setup menu network speed is never applying to rate (Mitchell)
-0 darkplaces: pointcontents crash when building harvester in gvb2? (yummyluv)
+f darkplaces: pointcontents crash when building harvester in gvb2? (yummyluv)
 0 darkplaces: put patches on a delayed queue in q3bsp collision code so the trace is first clipped by brushes
 0 darkplaces: r_shadow should load .ent when importing light entities
 0 darkplaces: r_shadow_showtris messes up r_shadow_visiblevolumes color (jitspoe)