]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
implemented r_shadow_culltriangles cvar (on by default), this improves lighting perfo...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 1 Mar 2007 20:07:20 +0000 (20:07 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 1 Mar 2007 20:07:20 +0000 (20:07 +0000)
ork because similar forms of culling were already used on shadow casting triangles (which are now replaced by this), this just applies the same already computed culling to the lighting as well as the shadowing

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

client.h
gl_rsurf.c
model_shared.c
model_shared.h
portals.c
portals.h
r_shadow.c
r_shadow.h
todo

index 2af844a2b8d1d5270bcdf2a8003f4b27842d8e3c..31018173fa80ff352f1057c04876638af56c5c91 100644 (file)
--- a/client.h
+++ b/client.h
@@ -126,6 +126,19 @@ typedef struct rtlight_s
        // surfaces seen by light
        int static_numsurfaces;
        int *static_surfacelist;
+       // flag bits indicating which triangles of the world model should cast
+       // shadows, and which ones should be lit
+       //
+       // this avoids redundantly scanning the triangles in each surface twice
+       // for whether they should cast shadows, once in culling and once in the
+       // actual shadowmarklist production.
+       int static_numshadowtrispvsbytes;
+       unsigned char *static_shadowtrispvs;
+       // this allows the lighting batch code to skip backfaces andother culled
+       // triangles not relevant for lighting
+       // (important on big surfaces such as terrain)
+       int static_numlighttrispvsbytes;
+       unsigned char *static_lighttrispvs;
 }
 rtlight_t;
 
index 7b60cf6f4eec52f211ca7531fe7ff9ba83390121..3c8302ec08eae5ea980de3128cccff405b3eb94e 100644 (file)
@@ -533,6 +533,8 @@ typedef struct r_q1bsp_getlightinfo_s
        int *outsurfacelist;
        unsigned char *outsurfacepvs;
        unsigned char *tempsurfacepvs;
+       unsigned char *outshadowtrispvs;
+       unsigned char *outlighttrispvs;
        int outnumsurfaces;
        vec3_t outmins;
        vec3_t outmaxs;
@@ -653,63 +655,76 @@ void R_Q1BSP_RecursiveGetLightInfo(r_q1bsp_getlightinfo_t *info, mnode_t *node)
                                if (BoxesOverlap(info->lightmins, info->lightmaxs, surface->mins, surface->maxs)
                                 && (!info->svbsp_insertoccluder || !(surface->texture->currentframe->currentmaterialflags & MATERIALFLAG_NOSHADOW)))
                                {
-                                       if (BoxInsideBox(surface->mins, surface->maxs, info->lightmins, info->lightmaxs))
+                                       qboolean addedtris = false;
+                                       qboolean insidebox = BoxInsideBox(surface->mins, surface->maxs, info->lightmins, info->lightmaxs);
+                                       for (triangleindex = 0, t = surface->num_firstshadowmeshtriangle, e = info->model->brush.shadowmesh->element3i + t * 3;triangleindex < surface->num_triangles;triangleindex++, t++, e += 3)
                                        {
-                                               // surface is entirely inside light box
-                                               for (triangleindex = 0, t = surface->num_firstshadowmeshtriangle, e = info->model->brush.shadowmesh->element3i + t * 3;triangleindex < surface->num_triangles;triangleindex++, t++, e += 3)
+                                               v[0] = info->model->brush.shadowmesh->vertex3f + e[0] * 3;
+                                               v[1] = info->model->brush.shadowmesh->vertex3f + e[1] * 3;
+                                               v[2] = info->model->brush.shadowmesh->vertex3f + e[2] * 3;
+                                               if (insidebox || TriangleOverlapsBox(v[0], v[1], v[2], info->lightmins, info->lightmaxs))
                                                {
-                                                       v[0] = info->model->brush.shadowmesh->vertex3f + e[0] * 3;
-                                                       v[1] = info->model->brush.shadowmesh->vertex3f + e[1] * 3;
-                                                       v[2] = info->model->brush.shadowmesh->vertex3f + e[2] * 3;
-                                                       if ((!r_shadow_frontsidecasting.integer || PointInfrontOfTriangle(info->relativelightorigin, v[0], v[1], v[2])))
+                                                       if (info->svbsp_insertoccluder)
+                                                       {
+                                                               if (!(surface->texture->currentframe->currentmaterialflags & MATERIALFLAG_NOCULLFACE) && r_shadow_frontsidecasting.integer != PointInfrontOfTriangle(info->relativelightorigin, v[0], v[1], v[2]))
+                                                                       continue;
+                                                               if (surface->texture->currentframe->currentmaterialflags & MATERIALFLAG_NOSHADOW)
+                                                                       continue;
+                                                               VectorCopy(v[0], v2[0]);
+                                                               VectorCopy(v[1], v2[1]);
+                                                               VectorCopy(v[2], v2[2]);
+                                                               if (!(SVBSP_AddPolygon(&r_svbsp, 3, v2[0], true, NULL, NULL, 0) & 2))
+                                                                       continue;
+                                                               addedtris = true;
+                                                       }
+                                                       else
                                                        {
                                                                if (info->svbsp_active)
                                                                {
                                                                        VectorCopy(v[0], v2[0]);
                                                                        VectorCopy(v[1], v2[1]);
                                                                        VectorCopy(v[2], v2[2]);
-                                                                       if (!(SVBSP_AddPolygon(&r_svbsp, 3, v2[0], info->svbsp_insertoccluder, NULL, NULL, 0) & 2))
+                                                                       if (!(SVBSP_AddPolygon(&r_svbsp, 3, v2[0], false, NULL, NULL, 0) & 2))
                                                                                continue;
                                                                }
-                                                               SETPVSBIT(info->outsurfacepvs, surfaceindex);
-                                                               info->outsurfacelist[info->outnumsurfaces++] = surfaceindex;
-                                                               if (!info->svbsp_insertoccluder)
-                                                                       break;
-                                                       }
-                                               }
-                                       }
-                                       else
-                                       {
-                                               // surface is partially clipped by lightbox
-                                               // check each triangle's bounding box
-                                               for (triangleindex = 0, t = surface->num_firstshadowmeshtriangle, e = info->model->brush.shadowmesh->element3i + t * 3;triangleindex < surface->num_triangles;triangleindex++, t++, e += 3)
-                                               {
-                                                       v[0] = info->model->brush.shadowmesh->vertex3f + e[0] * 3;
-                                                       v[1] = info->model->brush.shadowmesh->vertex3f + e[1] * 3;
-                                                       v[2] = info->model->brush.shadowmesh->vertex3f + e[2] * 3;
-                                                       if ((!r_shadow_frontsidecasting.integer || PointInfrontOfTriangle(info->relativelightorigin, v[0], v[1], v[2]))
-                                                        && info->lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0]))
-                                                        && info->lightmins[0] < max(v[0][0], max(v[1][0], v[2][0]))
-                                                        && info->lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1]))
-                                                        && info->lightmins[1] < max(v[0][1], max(v[1][1], v[2][1]))
-                                                        && info->lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2]))
-                                                        && info->lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
-                                                       {
-                                                               if (info->svbsp_active)
+                                                               if (surface->texture->currentframe->currentmaterialflags & MATERIALFLAG_NOCULLFACE)
                                                                {
-                                                                       VectorCopy(v[0], v2[0]);
-                                                                       VectorCopy(v[1], v2[1]);
-                                                                       VectorCopy(v[2], v2[2]);
-                                                                       if (!(SVBSP_AddPolygon(&r_svbsp, 3, v2[0], info->svbsp_insertoccluder, NULL, NULL, 0) & 2))
+                                                                       // if the material is double sided we
+                                                                       // can't cull by direction
+                                                                       SETPVSBIT(info->outlighttrispvs, t);
+                                                                       addedtris = true;
+                                                                       if (!(surface->texture->currentframe->currentmaterialflags & MATERIALFLAG_NOSHADOW))
+                                                                               SETPVSBIT(info->outshadowtrispvs, t);
+                                                               }
+                                                               else if (r_shadow_frontsidecasting.integer)
+                                                               {
+                                                                       // front side casting occludes backfaces,
+                                                                       // so they are completely useless as both
+                                                                       // casters and lit polygons
+                                                                       if (!PointInfrontOfTriangle(info->relativelightorigin, v[0], v[1], v[2]))
                                                                                continue;
+                                                                       SETPVSBIT(info->outlighttrispvs, t);
+                                                                       addedtris = true;
+                                                                       if (!(surface->texture->currentframe->currentmaterialflags & MATERIALFLAG_NOSHADOW))
+                                                                               SETPVSBIT(info->outshadowtrispvs, t);
+                                                               }
+                                                               else
+                                                               {
+                                                                       // back side casting does not occlude
+                                                                       // anything so we can't cull lit polygons
+                                                                       SETPVSBIT(info->outlighttrispvs, t);
+                                                                       addedtris = true;
+                                                                       if (!PointInfrontOfTriangle(info->relativelightorigin, v[0], v[1], v[2]) && !(surface->texture->currentframe->currentmaterialflags & MATERIALFLAG_NOSHADOW))
+                                                                               SETPVSBIT(info->outshadowtrispvs, t);
                                                                }
-                                                               SETPVSBIT(info->outsurfacepvs, surfaceindex);
-                                                               info->outsurfacelist[info->outnumsurfaces++] = surfaceindex;
-                                                               if (!info->svbsp_insertoccluder)
-                                                                       break;
                                                        }
                                                }
                                        }
+                                       if (addedtris)
+                                       {
+                                               SETPVSBIT(info->outsurfacepvs, surfaceindex);
+                                               info->outsurfacelist[info->outnumsurfaces++] = surfaceindex;
+                                       }
                                }
                        }
                }
@@ -789,7 +804,7 @@ void R_Q1BSP_CallRecursiveGetLightInfo(r_q1bsp_getlightinfo_t *info, qboolean us
        }
 }
 
-void R_Q1BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outleaflist, unsigned char *outleafpvs, int *outnumleafspointer, int *outsurfacelist, unsigned char *outsurfacepvs, int *outnumsurfacespointer)
+void R_Q1BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outleaflist, unsigned char *outleafpvs, int *outnumleafspointer, int *outsurfacelist, unsigned char *outsurfacepvs, int *outnumsurfacespointer, unsigned char *outshadowtrispvs, unsigned char *outlighttrispvs)
 {
        r_q1bsp_getlightinfo_t info;
        VectorCopy(relativelightorigin, info.relativelightorigin);
@@ -814,11 +829,18 @@ void R_Q1BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, floa
        info.outnumleafs = 0;
        info.outsurfacelist = outsurfacelist;
        info.outsurfacepvs = outsurfacepvs;
+       info.outshadowtrispvs = outshadowtrispvs;
+       info.outlighttrispvs = outlighttrispvs;
        info.outnumsurfaces = 0;
        VectorCopy(info.relativelightorigin, info.outmins);
        VectorCopy(info.relativelightorigin, info.outmaxs);
        memset(outleafpvs, 0, (info.model->brush.num_leafs + 7) >> 3);
        memset(outsurfacepvs, 0, (info.model->nummodelsurfaces + 7) >> 3);
+       if (info.model->brush.shadowmesh)
+               memset(outshadowtrispvs, 0, (info.model->brush.shadowmesh->numtriangles + 7) >> 3);
+       else
+               memset(outshadowtrispvs, 0, (info.model->surfmesh.num_triangles + 7) >> 3);
+       memset(outlighttrispvs, 0, (info.model->surfmesh.num_triangles + 7) >> 3);
        if (info.model->brush.GetPVS && r_shadow_frontsidecasting.integer)
                info.pvs = info.model->brush.GetPVS(info.model, info.relativelightorigin);
        else
@@ -828,12 +850,12 @@ void R_Q1BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, floa
        if (r_shadow_frontsidecasting.integer && r_shadow_compilingrtlight && r_shadow_realtime_world_compileportalculling.integer)
        {
                // use portal recursion for exact light volume culling, and exact surface checking
-               Portal_Visibility(info.model, info.relativelightorigin, info.outleaflist, info.outleafpvs, &info.outnumleafs, info.outsurfacelist, info.outsurfacepvs, &info.outnumsurfaces, NULL, 0, true, info.lightmins, info.lightmaxs, info.outmins, info.outmaxs);
+               Portal_Visibility(info.model, info.relativelightorigin, info.outleaflist, info.outleafpvs, &info.outnumleafs, info.outsurfacelist, info.outsurfacepvs, &info.outnumsurfaces, NULL, 0, true, info.lightmins, info.lightmaxs, info.outmins, info.outmaxs, info.outshadowtrispvs, info.outlighttrispvs);
        }
        else if (r_shadow_frontsidecasting.integer && r_shadow_realtime_dlight_portalculling.integer)
        {
                // use portal recursion for exact light volume culling, but not the expensive exact surface checking
-               Portal_Visibility(info.model, info.relativelightorigin, info.outleaflist, info.outleafpvs, &info.outnumleafs, info.outsurfacelist, info.outsurfacepvs, &info.outnumsurfaces, NULL, 0, r_shadow_realtime_dlight_portalculling.integer >= 2, info.lightmins, info.lightmaxs, info.outmins, info.outmaxs);
+               Portal_Visibility(info.model, info.relativelightorigin, info.outleaflist, info.outleafpvs, &info.outnumleafs, info.outsurfacelist, info.outsurfacepvs, &info.outnumsurfaces, NULL, 0, r_shadow_realtime_dlight_portalculling.integer >= 2, info.lightmins, info.lightmaxs, info.outmins, info.outmaxs, info.outshadowtrispvs, info.outlighttrispvs);
        }
        else
        {
@@ -915,15 +937,14 @@ void R_Q1BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin,
        }
 }
 
-#define BATCHSIZE 256
+#define BATCHSIZE 1024
 
 static void R_Q1BSP_DrawLight_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
 {
-       int i, j, batchnumsurfaces, endsurface;
+       int i, j, endsurface;
        texture_t *t;
        msurface_t *surface;
-       msurface_t *batchsurfaces[BATCHSIZE];
-       // note: in practice this never actually batches, oh well
+       // note: in practice this never actually receives batches), oh well
        R_Shadow_RenderMode_Begin();
        R_Shadow_RenderMode_ActiveLight((rtlight_t *)rtlight);
        R_Shadow_RenderMode_Lighting(false, true);
@@ -936,49 +957,34 @@ static void R_Q1BSP_DrawLight_TransparentCallback(const entity_render_t *ent, co
                R_UpdateTextureInfo(ent, t);
                rsurface_texture = t->currentframe;
                endsurface = min(j + BATCHSIZE, numsurfaces);
-               batchnumsurfaces = 0;
-               batchsurfaces[batchnumsurfaces++] = surface;
-               for (;j < endsurface;j++)
+               for (j = i;j < endsurface;j++)
                {
                        surface = rsurface_model->data_surfaces + surfacelist[j];
                        if (t != surface->texture)
-                               continue;
-                       batchsurfaces[batchnumsurfaces++] = surface;
+                               break;
+                       RSurf_PrepareVerticesForBatch(true, true, 1, &surface);
+                       R_Shadow_RenderLighting(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, ent->model->surfmesh.data_element3i + surface->num_firsttriangle * 3);
                }
-               R_Shadow_RenderSurfacesLighting(batchnumsurfaces, batchsurfaces);
        }
        R_Shadow_RenderMode_End();
 }
 
-static void R_Q1BSP_DrawLight_TransparentBatch(int batchnumsurfaces, msurface_t **batchsurfacelist)
-{
-       int batchsurfaceindex;
-       msurface_t *batchsurface;
-       vec3_t tempcenter, center;
-       for (batchsurfaceindex = 0;batchsurfaceindex < batchnumsurfaces;batchsurfaceindex++)
-       {
-               batchsurface = batchsurfacelist[batchsurfaceindex];
-               tempcenter[0] = (batchsurface->mins[0] + batchsurface->maxs[0]) * 0.5f;
-               tempcenter[1] = (batchsurface->mins[1] + batchsurface->maxs[1]) * 0.5f;
-               tempcenter[2] = (batchsurface->mins[2] + batchsurface->maxs[2]) * 0.5f;
-               Matrix4x4_Transform(&rsurface_entity->matrix, tempcenter, center);
-               R_MeshQueue_AddTransparent(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST ? r_view.origin : center, R_Q1BSP_DrawLight_TransparentCallback, rsurface_entity, batchsurface - rsurface_model->data_surfaces, r_shadow_rtlight);
-       }
-}
-
 #define RSURF_MAX_BATCHSURFACES 1024
 
-void R_Q1BSP_DrawLight(entity_render_t *ent, int numsurfaces, const int *surfacelist)
+void R_Q1BSP_DrawLight(entity_render_t *ent, int numsurfaces, const int *surfacelist, const unsigned char *trispvs)
 {
        model_t *model = ent->model;
        msurface_t *surface;
-       int i, k, l, endsurface, batchnumsurfaces;
+       int i, k, l, m, mend, endsurface, batchnumsurfaces, batchnumtriangles, batchfirstvertex, batchlastvertex;
+       const int *element3i;
        msurface_t *batchsurfacelist[RSURF_MAX_BATCHSURFACES];
+       int batchelements[BATCHSIZE*3];
        texture_t *tex;
        CHECKGLERROR
        RSurf_ActiveModelEntity(ent, true, true);
        R_UpdateAllTextureInfo(ent);
        CHECKGLERROR
+       element3i = rsurface_model->surfmesh.data_element3i;
        // this is a double loop because non-visible surface skipping has to be
        // fast, and even if this is not the world model (and hence no visibility
        // checking) the input surface list and batch buffer are different formats
@@ -1005,14 +1011,73 @@ void R_Q1BSP_DrawLight(entity_render_t *ent, int numsurfaces, const int *surface
                        surface = batchsurfacelist[k];
                        tex = surface->texture;
                        rsurface_texture = tex->currentframe;
-                       for (l = k;l < batchnumsurfaces && tex == batchsurfacelist[l]->texture;l++)
-                               r_refdef.stats.lights_lighttriangles += batchsurfacelist[l]->num_triangles;
                        if (rsurface_texture->currentmaterialflags & (MATERIALFLAG_WALL | MATERIALFLAG_WATER))
                        {
                                if (rsurface_texture->currentmaterialflags & MATERIALFLAG_BLENDED)
-                                       R_Q1BSP_DrawLight_TransparentBatch(l - k, batchsurfacelist + k);
+                               {
+                                       vec3_t tempcenter, center;
+                                       for (l = k;l < batchnumsurfaces && tex == batchsurfacelist[l]->texture;l++)
+                                       {
+                                               surface = batchsurfacelist[l];
+                                               tempcenter[0] = (surface->mins[0] + surface->maxs[0]) * 0.5f;
+                                               tempcenter[1] = (surface->mins[1] + surface->maxs[1]) * 0.5f;
+                                               tempcenter[2] = (surface->mins[2] + surface->maxs[2]) * 0.5f;
+                                               Matrix4x4_Transform(&rsurface_entity->matrix, tempcenter, center);
+                                               R_MeshQueue_AddTransparent(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST ? r_view.origin : center, R_Q1BSP_DrawLight_TransparentCallback, rsurface_entity, surface - rsurface_model->data_surfaces, r_shadow_rtlight);
+                                       }
+                               }
                                else
-                                       R_Shadow_RenderSurfacesLighting(l - k, batchsurfacelist + k);
+                               {
+                                       batchnumtriangles = 0;
+                                       // note: this only accepts consecutive surfaces because
+                                       // non-consecutive surfaces often have extreme vertex
+                                       // ranges (due to large numbers of surfaces omitted
+                                       // between them)
+                                       surface = batchsurfacelist[k];
+                                       for (l = k;l < batchnumsurfaces && surface == batchsurfacelist[l] && tex == surface->texture;l++, surface++)
+                                       {
+                                               RSurf_PrepareVerticesForBatch(true, true, 1, &surface);
+                                               for (m = surface->num_firsttriangle, mend = m + surface->num_triangles;m < mend;m++)
+                                               {
+                                                       if (r_shadow_culltriangles.integer)
+                                                       {
+                                                               if (trispvs)
+                                                               {
+                                                                       if (!CHECKPVSBIT(trispvs, m))
+                                                                               continue;
+                                                               }
+                                                               else
+                                                               {
+                                                                       if (r_shadow_frontsidecasting.integer && !PointInfrontOfTriangle(r_shadow_entitylightorigin, rsurface_vertex3f + element3i[m*3+0]*3, rsurface_vertex3f + element3i[m*3+1]*3, rsurface_vertex3f + element3i[m*3+2]*3))
+                                                                               continue;
+                                                               }
+                                                       }
+                                                       batchelements[batchnumtriangles*3+0] = element3i[m*3+0];
+                                                       batchelements[batchnumtriangles*3+1] = element3i[m*3+1];
+                                                       batchelements[batchnumtriangles*3+2] = element3i[m*3+2];
+                                                       batchnumtriangles++;
+                                                       r_refdef.stats.lights_lighttriangles++;
+                                                       if (batchnumtriangles >= BATCHSIZE)
+                                                       {
+                                                               Mod_VertexRangeFromElements(batchnumtriangles*3, batchelements, &batchfirstvertex, &batchlastvertex);
+                                                               R_Shadow_RenderLighting(batchfirstvertex, batchlastvertex + 1 - batchfirstvertex, batchnumtriangles, batchelements);
+                                                               batchnumtriangles = 0;
+                                                       }
+                                               }
+                                               r_refdef.stats.lights_lighttriangles += batchsurfacelist[l]->num_triangles;
+                                       }
+                                       if (batchnumtriangles > 0)
+                                       {
+                                               Mod_VertexRangeFromElements(batchnumtriangles*3, batchelements, &batchfirstvertex, &batchlastvertex);
+                                               R_Shadow_RenderLighting(batchfirstvertex, batchlastvertex + 1 - batchfirstvertex, batchnumtriangles, batchelements);
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               // skip ahead to the next texture
+                               for (l = k;l < batchnumsurfaces && tex == batchsurfacelist[l]->texture;l++)
+                                       ;
                        }
                }
        }
index 1edb3fb44504470b95c0e976a8eb59beebb65d3b..cde3783b5e445156d74161dd0e79be5042e37cce 100644 (file)
@@ -1358,3 +1358,24 @@ int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int
        return outtriangles;
 }
 
+void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
+{
+       int i, e;
+       int firstvertex, lastvertex;
+       if (numelements > 0 && elements)
+       {
+               firstvertex = lastvertex = elements[0];
+               for (i = 1;i < numelements;i++)
+               {
+                       e = elements[i];
+                       firstvertex = min(firstvertex, e);
+                       lastvertex = max(lastvertex, e);
+               }
+       }
+       else
+               firstvertex = lastvertex = 0;
+       if (firstvertexpointer)
+               *firstvertexpointer = firstvertex;
+       if (lastvertexpointer)
+               *lastvertexpointer = lastvertex;
+}
index 36d72434854d8c7ebb239b82040d8ec89aef2207..c7fc0306a3125c222ab137679642e6ba33a132e0 100644 (file)
@@ -588,13 +588,13 @@ typedef struct model_s
        // 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 *outleaflist, unsigned char *outleafpvs, int *outnumleafspointer, int *outsurfacelist, unsigned char *outsurfacepvs, int *outnumsurfacespointer);
+       void(*GetLightInfo)(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outleaflist, unsigned char *outleafpvs, int *outnumleafspointer, int *outsurfacelist, unsigned char *outsurfacepvs, int *outnumsurfacespointer, unsigned char *outshadowtrispvs, unsigned char *outlighttrispvs);
        // compile a shadow volume for the model based on light source
        void(*CompileShadowVolume)(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativelightdirection, float lightradius, int numsurfaces, const int *surfacelist);
        // draw a shadow volume for the model based on light source
        void(*DrawShadowVolume)(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativelightdirection, float lightradius, int numsurfaces, const int *surfacelist, const vec3_t lightmins, const vec3_t lightmaxs);
        // draw the lighting on a model (through stencil)
-       void(*DrawLight)(struct entity_render_s *ent, int numsurfaces, const int *surfacelist);
+       void(*DrawLight)(struct entity_render_s *ent, int numsurfaces, const int *surfacelist, const unsigned char *trispvs);
        // trace a box against this model
        void (*TraceBox)(struct model_s *model, int frame, struct trace_s *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask);
        // fields belonging to some types of model
@@ -680,6 +680,7 @@ int Mod_CountSkinFiles(skinfile_t *skinfile);
 
 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap);
 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f);
+void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer);
 
 // bsp models
 void Mod_BrushInit(void);
@@ -691,10 +692,10 @@ int Mod_Q1BSP_SuperContentsFromNativeContents(struct model_s *model, int nativec
 struct entity_render_s;
 void R_Q1BSP_DrawSky(struct entity_render_s *ent);
 void R_Q1BSP_Draw(struct entity_render_s *ent);
-void R_Q1BSP_GetLightInfo(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outleaflist, unsigned char *outleafpvs, int *outnumleafspointer, int *outsurfacelist, unsigned char *outsurfacepvs, int *outnumsurfacespointer);
+void R_Q1BSP_GetLightInfo(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outleaflist, unsigned char *outleafpvs, int *outnumleafspointer, int *outsurfacelist, unsigned char *outsurfacepvs, int *outnumsurfacespointer, unsigned char *outshadowtrispvs, unsigned char *outlighttrispvs);
 void R_Q1BSP_CompileShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativelightdirection, float lightradius, int numsurfaces, const int *surfacelist);
 void R_Q1BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativelightdirection, float lightradius, int numsurfaces, const int *surfacelist, const vec3_t lightmins, const vec3_t lightmaxs);
-void R_Q1BSP_DrawLight(struct entity_render_s *ent, int numsurfaces, const int *surfacelist);
+void R_Q1BSP_DrawLight(struct entity_render_s *ent, int numsurfaces, const int *surfacelist, const unsigned char *trispvs);
 
 // alias models
 struct frameblend_s;
index f4dafb6c5232c090579eb3f3a99fd31eb025a43a..4c0bb19a569e9dbbc8deed33b2abc32377d722f4 100644 (file)
--- a/portals.c
+++ b/portals.c
@@ -275,6 +275,8 @@ typedef struct portalrecursioninfo_s
        int numleafs;
        int *leaflist;
        unsigned char *leafpvs;
+       unsigned char *shadowtrispvs;
+       unsigned char *lighttrispvs;
        model_t *model;
        vec3_t eye;
        float *updateleafsmins;
@@ -318,28 +320,31 @@ static void Portal_RecursiveFlow (portalrecursioninfo_t *info, mleaf_t *leaf, in
                                msurface_t *surface = info->model->data_surfaces + surfaceindex;
                                if (BoxesOverlap(surface->mins, surface->maxs, info->boxmins, info->boxmaxs))
                                {
-                                       if (info->exact)
+                                       qboolean insidebox = BoxInsideBox(surface->mins, surface->maxs, info->boxmins, info->boxmaxs);
+                                       qboolean addedtris = false;
+                                       int t, tend;
+                                       const int *elements;
+                                       const float *vertex3f;
+                                       float v[9];
+                                       vertex3f = info->model->surfmesh.data_vertex3f;
+                                       elements = (info->model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);
+                                       for (t = surface->num_firsttriangle, tend = t + surface->num_triangles;t < tend;t++, elements += 3)
                                        {
-                                               int j;
-                                               const int *elements;
-                                               const float *vertex3f;
-                                               float v[9];
-                                               vertex3f = info->model->surfmesh.data_vertex3f;
-                                               elements = (info->model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);
-                                               for (j = 0;j < surface->num_triangles;j++, elements += 3)
+                                               VectorCopy(vertex3f + elements[0] * 3, v + 0);
+                                               VectorCopy(vertex3f + elements[1] * 3, v + 3);
+                                               VectorCopy(vertex3f + elements[2] * 3, v + 6);
+                                               if (PointInfrontOfTriangle(info->eye, v + 0, v + 3, v + 6)
+                                                && (insidebox || TriangleOverlapsBox(v, v + 3, v + 6, info->boxmins, info->boxmaxs))
+                                                && (!info->exact || Portal_PortalThroughPortalPlanes(&portalplanes[firstclipplane], numclipplanes, v, 3, &portaltemppoints2[0][0], 256) > 0))
                                                {
-                                                       VectorCopy(vertex3f + elements[0] * 3, v + 0);
-                                                       VectorCopy(vertex3f + elements[1] * 3, v + 3);
-                                                       VectorCopy(vertex3f + elements[2] * 3, v + 6);
-                                                       if (PointInfrontOfTriangle(info->eye, v + 0, v + 3, v + 6) && TriangleOverlapsBox(v, v + 3, v + 6, info->boxmins, info->boxmaxs) && Portal_PortalThroughPortalPlanes(&portalplanes[firstclipplane], numclipplanes, v, 3, &portaltemppoints2[0][0], 256) > 0)
-                                                       {
-                                                               SETPVSBIT(info->surfacepvs, surfaceindex);
-                                                               info->surfacelist[info->numsurfaces++] = surfaceindex;
-                                                               break;
-                                                       }
+                                                       addedtris = true;
+                                                       if (info->shadowtrispvs)
+                                                               SETPVSBIT(info->shadowtrispvs, t);
+                                                       if (info->lighttrispvs)
+                                                               SETPVSBIT(info->lighttrispvs, t);
                                                }
                                        }
-                                       else
+                                       if (addedtris)
                                        {
                                                SETPVSBIT(info->surfacepvs, surfaceindex);
                                                info->surfacelist[info->numsurfaces++] = surfaceindex;
@@ -409,7 +414,7 @@ static void Portal_RecursiveFindLeafForFlow(portalrecursioninfo_t *info, mnode_t
        }
 }
 
-void Portal_Visibility(model_t *model, const vec3_t eye, int *leaflist, unsigned char *leafpvs, int *numleafspointer, int *surfacelist, unsigned char *surfacepvs, int *numsurfacespointer, const mplane_t *frustumplanes, int numfrustumplanes, int exact, const float *boxmins, const float *boxmaxs, float *updateleafsmins, float *updateleafsmaxs)
+void Portal_Visibility(model_t *model, const vec3_t eye, int *leaflist, unsigned char *leafpvs, int *numleafspointer, int *surfacelist, unsigned char *surfacepvs, int *numsurfacespointer, const mplane_t *frustumplanes, int numfrustumplanes, int exact, const float *boxmins, const float *boxmaxs, float *updateleafsmins, float *updateleafsmaxs, unsigned char *shadowtrispvs, unsigned char *lighttrispvs)
 {
        int i;
        portalrecursioninfo_t info;
@@ -451,6 +456,8 @@ void Portal_Visibility(model_t *model, const vec3_t eye, int *leaflist, unsigned
        info.numfrustumplanes = numfrustumplanes;
        info.updateleafsmins = updateleafsmins;
        info.updateleafsmaxs = updateleafsmaxs;
+       info.shadowtrispvs = shadowtrispvs;
+       info.lighttrispvs = lighttrispvs;
 
        Portal_RecursiveFindLeafForFlow(&info, model->brush.data_nodes);
 
index 1c5fecd549c896acef967350b1a2b49d95a8c5fa..29f625aea3aa78a32a251caae4974c14e9947279 100644 (file)
--- a/portals.h
+++ b/portals.h
@@ -4,7 +4,7 @@
 
 int Portal_CheckPolygon(model_t *model, vec3_t eye, float *polypoints, int numpoints);
 int Portal_CheckBox(model_t *model, vec3_t eye, vec3_t a, vec3_t b);
-void Portal_Visibility(model_t *model, const vec3_t eye, int *leaflist, unsigned char *leafpvs, int *numleafspointer, int *surfacelist, unsigned char *surfacepvs, int *numsurfacespointer, const mplane_t *frustumplanes, int numfrustumplanes, int exact, const float *boxmins, const float *boxmaxs, float *updateleafsmins, float *updateleafsmaxs);
+void Portal_Visibility(model_t *model, const vec3_t eye, int *leaflist, unsigned char *leafpvs, int *numleafspointer, int *surfacelist, unsigned char *surfacepvs, int *numsurfacespointer, const mplane_t *frustumplanes, int numfrustumplanes, int exact, const float *boxmins, const float *boxmaxs, float *updateleafsmins, float *updateleafsmaxs, unsigned char *shadowtrispvs, unsigned char *lighttrispvs);
 
 #endif
 
index 55fd1f5b18213e9bcde8b40b4846ae86535b5041..98b21ee63e05edaf51a6576db3054b185099c3b1 100644 (file)
@@ -185,6 +185,11 @@ int r_shadow_buffer_numsurfacepvsbytes;
 unsigned char *r_shadow_buffer_surfacepvs;
 int *r_shadow_buffer_surfacelist;
 
+int r_shadow_buffer_numshadowtrispvsbytes;
+unsigned char *r_shadow_buffer_shadowtrispvs;
+int r_shadow_buffer_numlighttrispvsbytes;
+unsigned char *r_shadow_buffer_lighttrispvs;
+
 // current light's cull box (copied out of an rtlight or calculated by GetLightInfo)
 vec3_t r_shadow_rtlight_cullmins;
 vec3_t r_shadow_rtlight_cullmaxs;
@@ -226,6 +231,7 @@ cvar_t r_shadow_realtime_world_compileshadow = {0, "r_shadow_realtime_world_comp
 cvar_t r_shadow_realtime_world_compilesvbsp = {0, "r_shadow_realtime_world_compilesvbsp", "1", "enables svbsp optimization during compilation"};
 cvar_t r_shadow_realtime_world_compileportalculling = {0, "r_shadow_realtime_world_compileportalculling", "1", "enables portal-based culling optimization during compilation"};
 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1", "use scissor optimization of light rendering (restricts rendering to the portion of the screen affected by the light)"};
+cvar_t r_shadow_culltriangles = {0, "r_shadow_culltriangles", "1", "performs more expensive tests to remove unnecessary triangles of lit surfaces"};
 cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
 cvar_t r_shadow_shadow_polygonoffset = {0, "r_shadow_shadow_polygonoffset", "1", "how much to push shadow volumes into the distance when rendering, to reduce chances of zfighting artifacts (should not be less than 0)"};
 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1", "use 3D voxel textures for spherical attenuation rather than cylindrical (does not affect r_glsl lighting)"};
@@ -268,7 +274,6 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
 void R_Shadow_EditLights_Reload_f(void);
 void R_Shadow_ValidateCvars(void);
 static void R_Shadow_MakeTextures(void);
-void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
 
 void r_shadow_start(void)
 {
@@ -299,6 +304,10 @@ void r_shadow_start(void)
        r_shadow_buffer_numsurfacepvsbytes = 0;
        r_shadow_buffer_surfacepvs = NULL;
        r_shadow_buffer_surfacelist = NULL;
+       r_shadow_buffer_numshadowtrispvsbytes = 0;
+       r_shadow_buffer_shadowtrispvs = NULL;
+       r_shadow_buffer_numlighttrispvsbytes = 0;
+       r_shadow_buffer_lighttrispvs = NULL;
 }
 
 void r_shadow_shutdown(void)
@@ -347,6 +356,12 @@ void r_shadow_shutdown(void)
        if (r_shadow_buffer_surfacelist)
                Mem_Free(r_shadow_buffer_surfacelist);
        r_shadow_buffer_surfacelist = NULL;
+       r_shadow_buffer_numshadowtrispvsbytes = 0;
+       if (r_shadow_buffer_shadowtrispvs)
+               Mem_Free(r_shadow_buffer_shadowtrispvs);
+       r_shadow_buffer_numlighttrispvsbytes = 0;
+       if (r_shadow_buffer_lighttrispvs)
+               Mem_Free(r_shadow_buffer_lighttrispvs);
 }
 
 void r_shadow_newmap(void)
@@ -418,6 +433,7 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_realtime_world_compilesvbsp);
        Cvar_RegisterVariable(&r_shadow_realtime_world_compileportalculling);
        Cvar_RegisterVariable(&r_shadow_scissor);
+       Cvar_RegisterVariable(&r_shadow_culltriangles);
        Cvar_RegisterVariable(&r_shadow_shadow_polygonfactor);
        Cvar_RegisterVariable(&r_shadow_shadow_polygonoffset);
        Cvar_RegisterVariable(&r_shadow_texture3d);
@@ -450,6 +466,8 @@ void R_Shadow_Init(void)
        r_shadow_buffer_numsurfacepvsbytes = 0;
        r_shadow_buffer_surfacepvs = NULL;
        r_shadow_buffer_surfacelist = NULL;
+       r_shadow_buffer_shadowtrispvs = NULL;
+       r_shadow_buffer_lighttrispvs = NULL;
        R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
 }
 
@@ -493,10 +511,12 @@ void R_Shadow_ResizeShadowArrays(int numvertices, int numtriangles)
        }
 }
 
-static void R_Shadow_EnlargeLeafSurfaceBuffer(int numleafs, int numsurfaces)
+static void R_Shadow_EnlargeLeafSurfaceTrisBuffer(int numleafs, int numsurfaces, int numshadowtriangles, int numlighttriangles)
 {
        int numleafpvsbytes = (((numleafs + 7) >> 3) + 255) & ~255;
        int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
+       int numshadowtrispvsbytes = (((numshadowtriangles + 7) >> 3) + 255) & ~255;
+       int numlighttrispvsbytes = (((numlighttriangles + 7) >> 3) + 255) & ~255;
        if (r_shadow_buffer_numleafpvsbytes < numleafpvsbytes)
        {
                if (r_shadow_buffer_leafpvs)
@@ -517,6 +537,20 @@ static void R_Shadow_EnlargeLeafSurfaceBuffer(int numleafs, int numsurfaces)
                r_shadow_buffer_surfacepvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes);
                r_shadow_buffer_surfacelist = (int *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
        }
+       if (r_shadow_buffer_numshadowtrispvsbytes < numshadowtrispvsbytes)
+       {
+               if (r_shadow_buffer_shadowtrispvs)
+                       Mem_Free(r_shadow_buffer_shadowtrispvs);
+               r_shadow_buffer_numshadowtrispvsbytes = numshadowtrispvsbytes;
+               r_shadow_buffer_shadowtrispvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numshadowtrispvsbytes);
+       }
+       if (r_shadow_buffer_numlighttrispvsbytes < numlighttrispvsbytes)
+       {
+               if (r_shadow_buffer_lighttrispvs)
+                       Mem_Free(r_shadow_buffer_lighttrispvs);
+               r_shadow_buffer_numlighttrispvsbytes = numlighttrispvsbytes;
+               r_shadow_buffer_lighttrispvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numlighttrispvsbytes);
+       }
 }
 
 void R_Shadow_PrepareShadowMark(int numtris)
@@ -1214,12 +1248,11 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
        return false;
 }
 
-static void R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(const msurface_t *surface, const float *diffusecolor, const float *ambientcolor)
+static void R_Shadow_RenderLighting_Light_Vertex_Shading(int firstvertex, int numverts, int numtriangles, const int *element3i, const float *diffusecolor, const float *ambientcolor)
 {
-       int numverts = surface->num_vertices;
-       float *vertex3f = rsurface_vertex3f + 3 * surface->num_firstvertex;
-       float *normal3f = rsurface_normal3f + 3 * surface->num_firstvertex;
-       float *color4f = rsurface_array_color4f + 4 * surface->num_firstvertex;
+       float *vertex3f = rsurface_vertex3f + 3 * firstvertex;
+       float *normal3f = rsurface_normal3f + 3 * firstvertex;
+       float *color4f = rsurface_array_color4f + 4 * firstvertex;
        float dist, dot, distintensity, shadeintensity, v[3], n[3];
        if (r_textureunits.integer >= 3)
        {
@@ -1315,73 +1348,60 @@ static void R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(const msurface_
 
 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
 
-static void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(int numsurfaces, msurface_t **surfacelist)
+static void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
 {
-       int surfacelistindex;
-       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+       int i;
+       float       *out3f     = rsurface_array_texcoord3f + 3 * firstvertex;
+       const float *vertex3f  = rsurface_vertex3f         + 3 * firstvertex;
+       const float *svector3f = rsurface_svector3f        + 3 * firstvertex;
+       const float *tvector3f = rsurface_tvector3f        + 3 * firstvertex;
+       const float *normal3f  = rsurface_normal3f         + 3 * firstvertex;
+       float lightdir[3];
+       for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
        {
-               const msurface_t *surface = surfacelist[surfacelistindex];
-               int i;
-               float *out3f = rsurface_array_texcoord3f + 3 * surface->num_firstvertex;
-               const float *vertex3f = rsurface_vertex3f + 3 * surface->num_firstvertex;
-               const float *svector3f = rsurface_svector3f + 3 * surface->num_firstvertex;
-               const float *tvector3f = rsurface_tvector3f + 3 * surface->num_firstvertex;
-               const float *normal3f = rsurface_normal3f + 3 * surface->num_firstvertex;
-               float lightdir[3];
-               for (i = 0;i < surface->num_vertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
-               {
-                       VectorSubtract(r_shadow_entitylightorigin, vertex3f, lightdir);
-                       // the cubemap normalizes this for us
-                       out3f[0] = DotProduct(svector3f, lightdir);
-                       out3f[1] = DotProduct(tvector3f, lightdir);
-                       out3f[2] = DotProduct(normal3f, lightdir);
-               }
+               VectorSubtract(r_shadow_entitylightorigin, vertex3f, lightdir);
+               // the cubemap normalizes this for us
+               out3f[0] = DotProduct(svector3f, lightdir);
+               out3f[1] = DotProduct(tvector3f, lightdir);
+               out3f[2] = DotProduct(normal3f, lightdir);
        }
 }
 
-static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(int numsurfaces, msurface_t **surfacelist)
+static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
 {
-       int surfacelistindex;
-       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+       int i;
+       float       *out3f     = rsurface_array_texcoord3f + 3 * firstvertex;
+       const float *vertex3f  = rsurface_vertex3f         + 3 * firstvertex;
+       const float *svector3f = rsurface_svector3f        + 3 * firstvertex;
+       const float *tvector3f = rsurface_tvector3f        + 3 * firstvertex;
+       const float *normal3f  = rsurface_normal3f         + 3 * firstvertex;
+       float lightdir[3], eyedir[3], halfdir[3];
+       for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
        {
-               const msurface_t *surface = surfacelist[surfacelistindex];
-               int i;
-               float *out3f = rsurface_array_texcoord3f + 3 * surface->num_firstvertex;
-               const float *vertex3f = rsurface_vertex3f + 3 * surface->num_firstvertex;
-               const float *svector3f = rsurface_svector3f + 3 * surface->num_firstvertex;
-               const float *tvector3f = rsurface_tvector3f + 3 * surface->num_firstvertex;
-               const float *normal3f = rsurface_normal3f + 3 * surface->num_firstvertex;
-               float lightdir[3], eyedir[3], halfdir[3];
-               for (i = 0;i < surface->num_vertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
-               {
-                       VectorSubtract(r_shadow_entitylightorigin, vertex3f, lightdir);
-                       VectorNormalize(lightdir);
-                       VectorSubtract(rsurface_modelorg, vertex3f, eyedir);
-                       VectorNormalize(eyedir);
-                       VectorAdd(lightdir, eyedir, halfdir);
-                       // the cubemap normalizes this for us
-                       out3f[0] = DotProduct(svector3f, halfdir);
-                       out3f[1] = DotProduct(tvector3f, halfdir);
-                       out3f[2] = DotProduct(normal3f, halfdir);
-               }
+               VectorSubtract(r_shadow_entitylightorigin, vertex3f, lightdir);
+               VectorNormalize(lightdir);
+               VectorSubtract(rsurface_modelorg, vertex3f, eyedir);
+               VectorNormalize(eyedir);
+               VectorAdd(lightdir, eyedir, halfdir);
+               // the cubemap normalizes this for us
+               out3f[0] = DotProduct(svector3f, halfdir);
+               out3f[1] = DotProduct(tvector3f, halfdir);
+               out3f[2] = DotProduct(normal3f, halfdir);
        }
 }
 
-static void R_Shadow_RenderSurfacesLighting_VisibleLighting(int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
+static void R_Shadow_RenderLighting_VisibleLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
 {
        // used to display how many times a surface is lit for level design purposes
        GL_Color(0.1 * r_view.colorscale, 0.025 * r_view.colorscale, 0, 1);
        R_Mesh_ColorPointer(NULL);
        R_Mesh_ResetTextureState();
-       RSurf_PrepareVerticesForBatch(false, false, numsurfaces, surfacelist);
-       RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-       GL_LockArrays(0, 0);
+       R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 }
 
-static void R_Shadow_RenderSurfacesLighting_Light_GLSL(int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
+static void R_Shadow_RenderLighting_Light_GLSL(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
 {
        // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
-       RSurf_PrepareVerticesForBatch(true, true, numsurfaces, surfacelist);
        R_SetupSurfaceShader(lightcolorbase, false);
        R_Mesh_TexCoordPointer(0, 2, rsurface_model->surfmesh.data_texcoordtexture2f);
        R_Mesh_TexCoordPointer(1, 3, rsurface_svector3f);
@@ -1391,15 +1411,14 @@ static void R_Shadow_RenderSurfacesLighting_Light_GLSL(int numsurfaces, msurface
        {
                qglDepthFunc(GL_EQUAL);CHECKGLERROR
        }
-       RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-       GL_LockArrays(0, 0);
+       R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
        if (rsurface_texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
        {
                qglDepthFunc(GL_LEQUAL);CHECKGLERROR
        }
 }
 
-static void R_Shadow_RenderSurfacesLighting_Light_Dot3_Finalize(int numsurfaces, msurface_t **surfacelist, float r, float g, float b)
+static void R_Shadow_RenderLighting_Light_Dot3_Finalize(int firstvertex, int numvertices, int numtriangles, const int *element3i, float r, float g, float b)
 {
        // shared final code for all the dot3 layers
        int renders;
@@ -1407,12 +1426,11 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_Finalize(int numsurfaces,
        for (renders = 0;renders < 64 && (r > 0 || g > 0 || b > 0);renders++, r--, g--, b--)
        {
                GL_Color(bound(0, r, 1), bound(0, g, 1), bound(0, b, 1), 1);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
        }
 }
 
-static void R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, rtexture_t *basetexture, float colorscale)
+static void R_Shadow_RenderLighting_Light_Dot3_AmbientPass(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, rtexture_t *basetexture, float colorscale)
 {
        rmeshstate_t m;
        // colorscale accounts for how much we multiply the brightness
@@ -1499,8 +1517,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(int numsurfac
                R_Mesh_TextureState(&m);
                GL_ColorMask(0,0,0,1);
                GL_BlendFunc(GL_ONE, GL_ZERO);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // second pass
                memset(&m, 0, sizeof(m));
@@ -1517,10 +1534,10 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(int numsurfac
        }
        // this final code is shared
        R_Mesh_TextureState(&m);
-       R_Shadow_RenderSurfacesLighting_Light_Dot3_Finalize(numsurfaces, surfacelist, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
+       R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
 }
 
-static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, rtexture_t *basetexture, rtexture_t *normalmaptexture, float colorscale)
+static void R_Shadow_RenderLighting_Light_Dot3_DiffusePass(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, rtexture_t *basetexture, rtexture_t *normalmaptexture, float colorscale)
 {
        rmeshstate_t m;
        // colorscale accounts for how much we multiply the brightness
@@ -1532,7 +1549,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(int numsurfac
        // Limit mult to 64 for sanity sake.
        GL_Color(1,1,1,1);
        // generate normalization cubemap texcoords
-       R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(numsurfaces, surfacelist);
+       R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
        if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
        {
                // 3/2 3D combine path (Geforce3, Radeon 8500)
@@ -1550,8 +1567,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(int numsurfac
                R_Mesh_TextureState(&m);
                GL_ColorMask(0,0,0,1);
                GL_BlendFunc(GL_ONE, GL_ZERO);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // second pass
                memset(&m, 0, sizeof(m));
@@ -1576,8 +1592,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(int numsurfac
                R_Mesh_TextureState(&m);
                GL_ColorMask(0,0,0,1);
                GL_BlendFunc(GL_ONE, GL_ZERO);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // second pass
                memset(&m, 0, sizeof(m));
@@ -1590,8 +1605,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(int numsurfac
                m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
                R_Mesh_TextureState(&m);
                GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // second pass
                memset(&m, 0, sizeof(m));
@@ -1620,8 +1634,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(int numsurfac
                R_Mesh_TextureState(&m);
                GL_ColorMask(0,0,0,1);
                GL_BlendFunc(GL_ONE, GL_ZERO);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // second pass
                memset(&m, 0, sizeof(m));
@@ -1653,8 +1666,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(int numsurfac
                R_Mesh_TextureState(&m);
                GL_ColorMask(0,0,0,1);
                GL_BlendFunc(GL_ONE, GL_ZERO);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // second pass
                memset(&m, 0, sizeof(m));
@@ -1682,8 +1694,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(int numsurfac
                R_Mesh_TextureState(&m);
                GL_ColorMask(0,0,0,1);
                GL_BlendFunc(GL_ONE, GL_ZERO);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // second pass
                memset(&m, 0, sizeof(m));
@@ -1696,8 +1707,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(int numsurfac
                m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
                R_Mesh_TextureState(&m);
                GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // second pass
                memset(&m, 0, sizeof(m));
@@ -1714,10 +1724,10 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(int numsurfac
        }
        // this final code is shared
        R_Mesh_TextureState(&m);
-       R_Shadow_RenderSurfacesLighting_Light_Dot3_Finalize(numsurfaces, surfacelist, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
+       R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
 }
 
-static void R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, rtexture_t *glosstexture, rtexture_t *normalmaptexture, float colorscale)
+static void R_Shadow_RenderLighting_Light_Dot3_SpecularPass(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, rtexture_t *glosstexture, rtexture_t *normalmaptexture, float colorscale)
 {
        float glossexponent;
        rmeshstate_t m;
@@ -1726,7 +1736,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(int numsurfa
        //      return;
        GL_Color(1,1,1,1);
        // generate normalization cubemap texcoords
-       R_Shadow_GenTexCoords_Specular_NormalCubeMap(numsurfaces, surfacelist);
+       R_Shadow_GenTexCoords_Specular_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
        if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
        {
                // 2/0/0/1/2 3D combine blendsquare path
@@ -1741,16 +1751,14 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(int numsurfa
                GL_ColorMask(0,0,0,1);
                // this squares the result
                GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // second and third pass
                R_Mesh_ResetTextureState();
                // square alpha in framebuffer a few times to make it shiny
                GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
                for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
-                       RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // fourth pass
                memset(&m, 0, sizeof(m));
@@ -1759,8 +1767,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(int numsurfa
                m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
                R_Mesh_TextureState(&m);
                GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // fifth pass
                memset(&m, 0, sizeof(m));
@@ -1789,16 +1796,14 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(int numsurfa
                GL_ColorMask(0,0,0,1);
                // this squares the result
                GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // second and third pass
                R_Mesh_ResetTextureState();
                // square alpha in framebuffer a few times to make it shiny
                GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
                for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
-                       RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // fourth pass
                memset(&m, 0, sizeof(m));
@@ -1824,16 +1829,14 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(int numsurfa
                GL_ColorMask(0,0,0,1);
                // this squares the result
                GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // second and third pass
                R_Mesh_ResetTextureState();
                // square alpha in framebuffer a few times to make it shiny
                GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
                for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
-                       RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // fourth pass
                memset(&m, 0, sizeof(m));
@@ -1845,8 +1848,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(int numsurfa
                m.texmatrix[1] = r_shadow_entitytoattenuationz;
                R_Mesh_TextureState(&m);
                GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
-               GL_LockArrays(0, 0);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 
                // fifth pass
                memset(&m, 0, sizeof(m));
@@ -1863,10 +1865,10 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(int numsurfa
        }
        // this final code is shared
        R_Mesh_TextureState(&m);
-       R_Shadow_RenderSurfacesLighting_Light_Dot3_Finalize(numsurfaces, surfacelist, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
+       R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
 }
 
-static void R_Shadow_RenderSurfacesLighting_Light_Dot3(int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
+static void R_Shadow_RenderLighting_Light_Dot3(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
 {
        // ARB path (any Geforce, any Radeon)
        qboolean doambient = r_shadow_rtlight->ambientscale > 0;
@@ -1874,104 +1876,91 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3(int numsurfaces, msurface
        qboolean dospecular = specularscale > 0;
        if (!doambient && !dodiffuse && !dospecular)
                return;
-       RSurf_PrepareVerticesForBatch(true, true, numsurfaces, surfacelist);
        R_Mesh_ColorPointer(NULL);
        if (doambient)
-               R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorbase, basetexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
+               R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, basetexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
        if (dodiffuse)
-               R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorbase, basetexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
+               R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, basetexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
        if (dopants)
        {
                if (doambient)
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorpants, pantstexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
+                       R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorpants, pantstexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
                if (dodiffuse)
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorpants, pantstexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
+                       R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorpants, pantstexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
        }
        if (doshirt)
        {
                if (doambient)
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorshirt, shirttexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
+                       R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorshirt, shirttexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
                if (dodiffuse)
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorshirt, shirttexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
+                       R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorshirt, shirttexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
        }
        if (dospecular)
-               R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(numsurfaces, surfacelist, lightcolorbase, glosstexture, normalmaptexture, specularscale * r_view.colorscale);
+               R_Shadow_RenderLighting_Light_Dot3_SpecularPass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, glosstexture, normalmaptexture, specularscale * r_view.colorscale);
 }
 
-void R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(const model_t *model, int numsurfaces, msurface_t **surfacelist, vec3_t diffusecolor2, vec3_t ambientcolor2)
+void R_Shadow_RenderLighting_Light_Vertex_Pass(const model_t *model, int firstvertex, int numvertices, int numtriangles, const int *element3i, vec3_t diffusecolor2, vec3_t ambientcolor2)
 {
-       int surfacelistindex;
        int renders;
-       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
-       {
-               const msurface_t *surface = surfacelist[surfacelistindex];
-               R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(surface, diffusecolor2, ambientcolor2);
-       }
+       int i;
+       int stop;
+       int newfirstvertex;
+       int newlastvertex;
+       int newnumtriangles;
+       int *newe;
+       const int *e;
+       float *c;
+       int newelements[3072];
+       R_Shadow_RenderLighting_Light_Vertex_Shading(firstvertex, numvertices, numtriangles, element3i, diffusecolor2, ambientcolor2);
        for (renders = 0;renders < 64;renders++)
        {
-               const int *e;
-               int stop;
-               int firstvertex;
-               int lastvertex;
-               int newnumtriangles;
-               int *newe;
-               int newelements[3072];
                stop = true;
-               firstvertex = 0;
-               lastvertex = 0;
+               newfirstvertex = 0;
+               newlastvertex = 0;
                newnumtriangles = 0;
                newe = newelements;
-               for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
-               {
-                       const msurface_t *surface = surfacelist[surfacelistindex];
-                       const int *elements = rsurface_model->surfmesh.data_element3i + surface->num_firsttriangle * 3;
-                       int i;
-                       // due to low fillrate on the cards this vertex lighting path is
-                       // designed for, we manually cull all triangles that do not
-                       // contain a lit vertex
-                       // this builds batches of triangles from multiple surfaces and
-                       // renders them at once
-                       for (i = 0, e = elements;i < surface->num_triangles;i++, e += 3)
+               // due to low fillrate on the cards this vertex lighting path is
+               // designed for, we manually cull all triangles that do not
+               // contain a lit vertex
+               // this builds batches of triangles from multiple surfaces and
+               // renders them at once
+               for (i = 0, e = element3i;i < numtriangles;i++, e += 3)
+               {
+                       if (VectorLength2(rsurface_array_color4f + e[0] * 4) + VectorLength2(rsurface_array_color4f + e[1] * 4) + VectorLength2(rsurface_array_color4f + e[2] * 4) >= 0.01)
                        {
-                               if (VectorLength2(rsurface_array_color4f + e[0] * 4) + VectorLength2(rsurface_array_color4f + e[1] * 4) + VectorLength2(rsurface_array_color4f + e[2] * 4) >= 0.01)
+                               if (newnumtriangles)
                                {
-                                       if (newnumtriangles)
-                                       {
-                                               firstvertex = min(firstvertex, e[0]);
-                                               lastvertex = max(lastvertex, e[0]);
-                                       }
-                                       else
-                                       {
-                                               firstvertex = e[0];
-                                               lastvertex = e[0];
-                                       }
-                                       firstvertex = min(firstvertex, e[1]);
-                                       lastvertex = max(lastvertex, e[1]);
-                                       firstvertex = min(firstvertex, e[2]);
-                                       lastvertex = max(lastvertex, e[2]);
-                                       newe[0] = e[0];
-                                       newe[1] = e[1];
-                                       newe[2] = e[2];
-                                       newnumtriangles++;
-                                       newe += 3;
-                                       if (newnumtriangles >= 1024)
-                                       {
-                                               GL_LockArrays(firstvertex, lastvertex - firstvertex + 1);
-                                               R_Mesh_Draw(firstvertex, lastvertex - firstvertex + 1, newnumtriangles, newelements);
-                                               newnumtriangles = 0;
-                                               newe = newelements;
-                                               stop = false;
-                                       }
+                                       newfirstvertex = min(newfirstvertex, e[0]);
+                                       newlastvertex  = max(newlastvertex, e[0]);
+                               }
+                               else
+                               {
+                                       newfirstvertex = e[0];
+                                       newlastvertex = e[0];
+                               }
+                               newfirstvertex = min(newfirstvertex, e[1]);
+                               newlastvertex  = max(newlastvertex, e[1]);
+                               newfirstvertex = min(newfirstvertex, e[2]);
+                               newlastvertex  = max(newlastvertex, e[2]);
+                               newe[0] = e[0];
+                               newe[1] = e[1];
+                               newe[2] = e[2];
+                               newnumtriangles++;
+                               newe += 3;
+                               if (newnumtriangles >= 1024)
+                               {
+                                       R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements);
+                                       newnumtriangles = 0;
+                                       newe = newelements;
+                                       stop = false;
                                }
                        }
                }
                if (newnumtriangles >= 1)
                {
-                       GL_LockArrays(firstvertex, lastvertex - firstvertex + 1);
-                       R_Mesh_Draw(firstvertex, lastvertex - firstvertex + 1, newnumtriangles, newelements);
+                       R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements);
                        stop = false;
                }
-               GL_LockArrays(0, 0);
                // if we couldn't find any lit triangles, exit early
                if (stop)
                        break;
@@ -1980,23 +1969,17 @@ void R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(const model_t *model, int
                // handling of negative colors
                // (some old drivers even have improper handling of >1 color)
                stop = true;
-               for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+               for (i = 0, c = rsurface_array_color4f + 4 * firstvertex;i < numvertices;i++, c += 4)
                {
-                       int i;
-                       float *c;
-                       const msurface_t *surface = surfacelist[surfacelistindex];
-                       for (i = 0, c = rsurface_array_color4f + 4 * surface->num_firstvertex;i < surface->num_vertices;i++, c += 4)
+                       if (c[0] > 1 || c[1] > 1 || c[2] > 1)
                        {
-                               if (c[0] > 1 || c[1] > 1 || c[2] > 1)
-                               {
-                                       c[0] = max(0, c[0] - 1);
-                                       c[1] = max(0, c[1] - 1);
-                                       c[2] = max(0, c[2] - 1);
-                                       stop = false;
-                               }
-                               else
-                                       VectorClear(c);
+                               c[0] = max(0, c[0] - 1);
+                               c[1] = max(0, c[1] - 1);
+                               c[2] = max(0, c[2] - 1);
+                               stop = false;
                        }
+                       else
+                               VectorClear(c);
                }
                // another check...
                if (stop)
@@ -2004,7 +1987,7 @@ void R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(const model_t *model, int
        }
 }
 
-static void R_Shadow_RenderSurfacesLighting_Light_Vertex(int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
+static void R_Shadow_RenderLighting_Light_Vertex(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
 {
        // OpenGL 1.1 path (anything)
        model_t *model = rsurface_entity->model;
@@ -2039,22 +2022,21 @@ static void R_Shadow_RenderSurfacesLighting_Light_Vertex(int numsurfaces, msurfa
                }
        }
        R_Mesh_TextureState(&m);
-       RSurf_PrepareVerticesForBatch(true, false, numsurfaces, surfacelist);
        R_Mesh_TexBind(0, R_GetTexture(basetexture));
-       R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(model, numsurfaces, surfacelist, diffusecolorbase, ambientcolorbase);
+       R_Shadow_RenderLighting_Light_Vertex_Pass(model, firstvertex, numvertices, numtriangles, element3i, diffusecolorbase, ambientcolorbase);
        if (dopants)
        {
                R_Mesh_TexBind(0, R_GetTexture(pantstexture));
-               R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(model, numsurfaces, surfacelist, diffusecolorpants, ambientcolorpants);
+               R_Shadow_RenderLighting_Light_Vertex_Pass(model, firstvertex, numvertices, numtriangles, element3i, diffusecolorpants, ambientcolorpants);
        }
        if (doshirt)
        {
                R_Mesh_TexBind(0, R_GetTexture(shirttexture));
-               R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(model, numsurfaces, surfacelist, diffusecolorshirt, ambientcolorshirt);
+               R_Shadow_RenderLighting_Light_Vertex_Pass(model, firstvertex, numvertices, numtriangles, element3i, diffusecolorshirt, ambientcolorshirt);
        }
 }
 
-void R_Shadow_RenderSurfacesLighting(int numsurfaces, msurface_t **surfacelist)
+void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i)
 {
        // FIXME: support MATERIALFLAG_NODEPTHTEST
        vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
@@ -2090,19 +2072,19 @@ void R_Shadow_RenderSurfacesLighting(int numsurfaces, msurface_t **surfacelist)
                {
                case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
                        GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
-                       R_Shadow_RenderSurfacesLighting_VisibleLighting(numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
+                       R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_GLSL:
-                       R_Shadow_RenderSurfacesLighting_Light_GLSL(numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
+                       R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_DOT3:
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3(numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
+                       R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
-                       R_Shadow_RenderSurfacesLighting_Light_Vertex(numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
+                       R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
                        break;
                default:
-                       Con_Printf("R_Shadow_RenderSurfacesLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
+                       Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
                        break;
                }
        }
@@ -2112,19 +2094,19 @@ void R_Shadow_RenderSurfacesLighting(int numsurfaces, msurface_t **surfacelist)
                {
                case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
                        GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
-                       R_Shadow_RenderSurfacesLighting_VisibleLighting(numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
+                       R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_GLSL:
-                       R_Shadow_RenderSurfacesLighting_Light_GLSL(numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
+                       R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_DOT3:
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3(numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
+                       R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
-                       R_Shadow_RenderSurfacesLighting_Light_Vertex(numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
+                       R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
                        break;
                default:
-                       Con_Printf("R_Shadow_RenderSurfacesLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
+                       Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
                        break;
                }
        }
@@ -2174,7 +2156,9 @@ void R_RTLight_Update(rtlight_t *rtlight, int isstatic, matrix4x4_t *matrix, vec
 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
 void R_RTLight_Compile(rtlight_t *rtlight)
 {
-       int shadowmeshes, shadowtris, numleafs, numleafpvsbytes, numsurfaces;
+       int i;
+       int numsurfaces, numleafs, numleafpvsbytes, numshadowtrispvsbytes, numlighttrispvsbytes;
+       int lighttris, shadowtris, shadowmeshes, shadowmeshtris;
        entity_render_t *ent = r_refdef.worldentity;
        model_t *model = r_refdef.worldmodel;
        unsigned char *data;
@@ -2198,22 +2182,32 @@ void R_RTLight_Compile(rtlight_t *rtlight)
        {
                // this variable must be set for the CompileShadowVolume code
                r_shadow_compilingrtlight = rtlight;
-               R_Shadow_EnlargeLeafSurfaceBuffer(model->brush.num_leafs, model->num_surfaces);
-               model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
+               R_Shadow_EnlargeLeafSurfaceTrisBuffer(model->brush.num_leafs, model->num_surfaces, model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles, model->surfmesh.num_triangles);
+               model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs);
                numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
-               data = (unsigned char *)Mem_Alloc(r_main_mempool, sizeof(int) * numleafs + numleafpvsbytes + sizeof(int) * numsurfaces);
+               numshadowtrispvsbytes = ((model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles) + 7) >> 3;
+               numlighttrispvsbytes = (model->surfmesh.num_triangles + 7) >> 3;
+               data = (unsigned char *)Mem_Alloc(r_main_mempool, sizeof(int) * numsurfaces + sizeof(int) * numleafs + numleafpvsbytes + numshadowtrispvsbytes + numlighttrispvsbytes);
+               rtlight->static_numsurfaces = numsurfaces;
+               rtlight->static_surfacelist = (int *)data;data += sizeof(int) * numsurfaces;
                rtlight->static_numleafs = numleafs;
-               rtlight->static_numleafpvsbytes = numleafpvsbytes;
                rtlight->static_leaflist = (int *)data;data += sizeof(int) * numleafs;
+               rtlight->static_numleafpvsbytes = numleafpvsbytes;
                rtlight->static_leafpvs = (unsigned char *)data;data += numleafpvsbytes;
-               rtlight->static_numsurfaces = numsurfaces;
-               rtlight->static_surfacelist = (int *)data;data += sizeof(int) * numsurfaces;
-               if (numleafs)
+               rtlight->static_numshadowtrispvsbytes = numshadowtrispvsbytes;
+               rtlight->static_shadowtrispvs = (unsigned char *)data;data += numshadowtrispvsbytes;
+               rtlight->static_numlighttrispvsbytes = numlighttrispvsbytes;
+               rtlight->static_lighttrispvs = (unsigned char *)data;data += numlighttrispvsbytes;
+               if (rtlight->static_numsurfaces)
+                       memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
+               if (rtlight->static_numleafs)
                        memcpy(rtlight->static_leaflist, r_shadow_buffer_leaflist, rtlight->static_numleafs * sizeof(*rtlight->static_leaflist));
-               if (numleafpvsbytes)
+               if (rtlight->static_numleafpvsbytes)
                        memcpy(rtlight->static_leafpvs, r_shadow_buffer_leafpvs, rtlight->static_numleafpvsbytes);
-               if (numsurfaces)
-                       memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
+               if (rtlight->static_numshadowtrispvsbytes)
+                       memcpy(rtlight->static_shadowtrispvs, r_shadow_buffer_shadowtrispvs, rtlight->static_numshadowtrispvsbytes);
+               if (rtlight->static_numlighttrispvsbytes)
+                       memcpy(rtlight->static_lighttrispvs, r_shadow_buffer_lighttrispvs, rtlight->static_numlighttrispvsbytes);
                if (model->CompileShadowVolume && rtlight->shadow)
                        model->CompileShadowVolume(ent, rtlight->shadoworigin, NULL, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
                // now we're done compiling the rtlight
@@ -2226,19 +2220,31 @@ void R_RTLight_Compile(rtlight_t *rtlight)
        //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
 
        shadowmeshes = 0;
-       shadowtris = 0;
+       shadowmeshtris = 0;
        if (rtlight->static_meshchain_shadow)
        {
                shadowmesh_t *mesh;
                for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
                {
                        shadowmeshes++;
-                       shadowtris += mesh->numtriangles;
+                       shadowmeshtris += mesh->numtriangles;
                }
        }
 
+       lighttris = 0;
+       if (rtlight->static_numlighttrispvsbytes)
+               for (i = 0;i < rtlight->static_numlighttrispvsbytes*8;i++)
+                       if (CHECKPVSBIT(rtlight->static_lighttrispvs, i))
+                               lighttris++;
+
+       shadowtris = 0;
+       if (rtlight->static_numlighttrispvsbytes)
+               for (i = 0;i < rtlight->static_numshadowtrispvsbytes*8;i++)
+                       if (CHECKPVSBIT(rtlight->static_shadowtrispvs, i))
+                               shadowtris++;
+
        if (developer.integer >= 10)
-               Con_Printf("static light built: %f %f %f : %f %f %f box, %i shadow volume 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);
+               Con_Printf("static light built: %f %f %f : %f %f %f box, %i light triangles, %i shadow triangles, %i compiled shadow volume triangles (in %i meshes)\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], lighttris, shadowtris, shadowmeshtris, shadowmeshes);
 }
 
 void R_RTLight_Uncompile(rtlight_t *rtlight)
@@ -2249,14 +2255,18 @@ void R_RTLight_Uncompile(rtlight_t *rtlight)
                        Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
                rtlight->static_meshchain_shadow = NULL;
                // these allocations are grouped
-               if (rtlight->static_leaflist)
-                       Mem_Free(rtlight->static_leaflist);
+               if (rtlight->static_surfacelist)
+                       Mem_Free(rtlight->static_surfacelist);
                rtlight->static_numleafs = 0;
                rtlight->static_numleafpvsbytes = 0;
                rtlight->static_leaflist = NULL;
                rtlight->static_leafpvs = NULL;
                rtlight->static_numsurfaces = 0;
                rtlight->static_surfacelist = NULL;
+               rtlight->static_numshadowtrispvsbytes = 0;
+               rtlight->static_shadowtrispvs = NULL;
+               rtlight->static_numlighttrispvsbytes = 0;
+               rtlight->static_lighttrispvs = NULL;
                rtlight->compiled = false;
        }
 }
@@ -2268,63 +2278,72 @@ void R_Shadow_UncompileWorldLights(void)
                R_RTLight_Uncompile(&light->rtlight);
 }
 
-void R_Shadow_DrawEntityShadow(entity_render_t *ent, int numsurfaces, int *surfacelist)
+void R_Shadow_DrawWorldShadow(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
 {
-       model_t *model = ent->model;
-       vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
-       vec_t relativeshadowradius;
-       if (ent == r_refdef.worldentity)
+       RSurf_ActiveWorldEntity();
+       if (r_shadow_rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
        {
-               RSurf_ActiveWorldEntity();
-               if (r_shadow_rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
+               shadowmesh_t *mesh;
+               CHECKGLERROR
+               for (mesh = r_shadow_rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
                {
-                       shadowmesh_t *mesh;
-                       CHECKGLERROR
-                       for (mesh = r_shadow_rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
+                       r_refdef.stats.lights_shadowtriangles += mesh->numtriangles;
+                       R_Mesh_VertexPointer(mesh->vertex3f);
+                       GL_LockArrays(0, mesh->numverts);
+                       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
                        {
-                               r_refdef.stats.lights_shadowtriangles += mesh->numtriangles;
-                               R_Mesh_VertexPointer(mesh->vertex3f);
-                               GL_LockArrays(0, mesh->numverts);
-                               if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
-                               {
-                                       // decrement stencil if backface is behind depthbuffer
-                                       GL_CullFace(GL_BACK); // quake is backwards, this culls front faces
-                                       qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
-                                       R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
-                                       // increment stencil if frontface is behind depthbuffer
-                                       GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
-                                       qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
-                               }
+                               // decrement stencil if backface is behind depthbuffer
+                               GL_CullFace(GL_BACK); // quake is backwards, this culls front faces
+                               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
                                R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
-                               GL_LockArrays(0, 0);
+                               // increment stencil if frontface is behind depthbuffer
+                               GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
+                               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
                        }
-                       CHECKGLERROR
+                       R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
+                       GL_LockArrays(0, 0);
                }
-               else if (numsurfaces)
-                       model->DrawShadowVolume(ent, r_shadow_rtlight->shadoworigin, NULL, r_shadow_rtlight->radius, numsurfaces, surfacelist, r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs);
+               CHECKGLERROR
        }
-       else
+       else if (numsurfaces && r_refdef.worldmodel->brush.shadowmesh && r_shadow_culltriangles.integer)
        {
-               RSurf_ActiveModelEntity(ent, false, false);
-               Matrix4x4_Transform(&ent->inversematrix, r_shadow_rtlight->shadoworigin, relativeshadoworigin);
-               relativeshadowradius = r_shadow_rtlight->radius / ent->scale;
-               relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
-               relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
-               relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
-               relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
-               relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
-               relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
-               model->DrawShadowVolume(ent, relativeshadoworigin, NULL, relativeshadowradius, model->nummodelsurfaces, model->surfacelist, relativeshadowmins, relativeshadowmaxs);
+               int t, tend;
+               int surfacelistindex;
+               msurface_t *surface;
+               R_Shadow_PrepareShadowMark(r_refdef.worldmodel->brush.shadowmesh->numtriangles);
+               for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+               {
+                       surface = r_refdef.worldmodel->data_surfaces + surfacelist[surfacelistindex];
+                       for (t = surface->num_firstshadowmeshtriangle, tend = t + surface->num_triangles;t < tend;t++)
+                               if (CHECKPVSBIT(trispvs, t))
+                                       shadowmarklist[numshadowmark++] = t;
+               }
+               R_Shadow_VolumeFromList(r_refdef.worldmodel->brush.shadowmesh->numverts, r_refdef.worldmodel->brush.shadowmesh->numtriangles, r_refdef.worldmodel->brush.shadowmesh->vertex3f, r_refdef.worldmodel->brush.shadowmesh->element3i, r_refdef.worldmodel->brush.shadowmesh->neighbor3i, r_shadow_rtlight->shadoworigin, NULL, r_shadow_rtlight->radius + r_refdef.worldmodel->radius*2 + r_shadow_projectdistance.value, numshadowmark, shadowmarklist);
        }
+       else if (numsurfaces)
+               r_refdef.worldmodel->DrawShadowVolume(r_refdef.worldentity, r_shadow_rtlight->shadoworigin, NULL, r_shadow_rtlight->radius, numsurfaces, surfacelist, r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs);
+}
+
+void R_Shadow_DrawEntityShadow(entity_render_t *ent)
+{
+       vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
+       vec_t relativeshadowradius;
+       RSurf_ActiveModelEntity(ent, false, false);
+       Matrix4x4_Transform(&ent->inversematrix, r_shadow_rtlight->shadoworigin, relativeshadoworigin);
+       relativeshadowradius = r_shadow_rtlight->radius / ent->scale;
+       relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
+       relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
+       relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
+       relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
+       relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
+       relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
+       ent->model->DrawShadowVolume(ent, relativeshadoworigin, NULL, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
 }
 
 void R_Shadow_SetupEntityLight(const entity_render_t *ent)
 {
        // set up properties for rendering light onto this entity
-       if (ent == r_refdef.worldentity)
-               RSurf_ActiveWorldEntity();
-       else
-               RSurf_ActiveModelEntity(ent, true, true);
+       RSurf_ActiveModelEntity(ent, true, true);
        Matrix4x4_Concat(&r_shadow_entitytolight, &r_shadow_rtlight->matrix_worldtolight, &ent->matrix);
        Matrix4x4_Concat(&r_shadow_entitytoattenuationxyz, &matrix_attenuationxyz, &r_shadow_entitytolight);
        Matrix4x4_Concat(&r_shadow_entitytoattenuationz, &matrix_attenuationz, &r_shadow_entitytolight);
@@ -2333,16 +2352,32 @@ void R_Shadow_SetupEntityLight(const entity_render_t *ent)
                R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
 }
 
+void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
+{
+       if (!r_refdef.worldmodel->DrawLight)
+               return;
+
+       // set up properties for rendering light onto this entity
+       RSurf_ActiveWorldEntity();
+       r_shadow_entitytolight = r_shadow_rtlight->matrix_worldtolight;
+       Matrix4x4_Concat(&r_shadow_entitytoattenuationxyz, &matrix_attenuationxyz, &r_shadow_entitytolight);
+       Matrix4x4_Concat(&r_shadow_entitytoattenuationz, &matrix_attenuationz, &r_shadow_entitytolight);
+       VectorCopy(r_shadow_rtlight->shadoworigin, r_shadow_entitylightorigin);
+       if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
+               R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
+
+       r_refdef.worldmodel->DrawLight(r_refdef.worldentity, numsurfaces, surfacelist, trispvs);
+}
+
 void R_Shadow_DrawEntityLight(entity_render_t *ent, int numsurfaces, int *surfacelist)
 {
        model_t *model = ent->model;
        if (!model->DrawLight)
                return;
+
        R_Shadow_SetupEntityLight(ent);
-       if (ent == r_refdef.worldentity)
-               model->DrawLight(ent, numsurfaces, surfacelist);
-       else
-               model->DrawLight(ent, model->nummodelsurfaces, model->surfacelist);
+
+       model->DrawLight(ent, model->nummodelsurfaces, model->surfacelist, NULL);
 }
 
 void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
@@ -2351,7 +2386,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        float f;
        int numleafs, numsurfaces;
        int *leaflist, *surfacelist;
-       unsigned char *leafpvs;
+       unsigned char *leafpvs, *shadowtrispvs, *lighttrispvs;
        int numlightentities;
        int numshadowentities;
        entity_render_t *lightentities[MAX_EDICTS];
@@ -2402,16 +2437,20 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                leafpvs = rtlight->static_leafpvs;
                numsurfaces = rtlight->static_numsurfaces;
                surfacelist = rtlight->static_surfacelist;
+               shadowtrispvs = rtlight->static_shadowtrispvs;
+               lighttrispvs = rtlight->static_lighttrispvs;
        }
        else if (r_refdef.worldmodel && r_refdef.worldmodel->GetLightInfo)
        {
                // dynamic light, world available and can receive realtime lighting
                // calculate lit surfaces and leafs
-               R_Shadow_EnlargeLeafSurfaceBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->num_surfaces);
-               r_refdef.worldmodel->GetLightInfo(r_refdef.worldentity, rtlight->shadoworigin, rtlight->radius, r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
+               R_Shadow_EnlargeLeafSurfaceTrisBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->num_surfaces, r_refdef.worldmodel->brush.shadowmesh ? r_refdef.worldmodel->brush.shadowmesh->numtriangles : r_refdef.worldmodel->surfmesh.num_triangles, r_refdef.worldmodel->surfmesh.num_triangles);
+               r_refdef.worldmodel->GetLightInfo(r_refdef.worldentity, rtlight->shadoworigin, rtlight->radius, r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs);
                leaflist = r_shadow_buffer_leaflist;
                leafpvs = r_shadow_buffer_leafpvs;
                surfacelist = r_shadow_buffer_surfacelist;
+               shadowtrispvs = r_shadow_buffer_shadowtrispvs;
+               lighttrispvs = r_shadow_buffer_lighttrispvs;
                // if the reduced leaf bounds are offscreen, skip it
                if (R_CullBox(r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs))
                        return;
@@ -2424,6 +2463,8 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                leafpvs = NULL;
                numsurfaces = 0;
                surfacelist = NULL;
+               shadowtrispvs = NULL;
+               lighttrispvs = NULL;
        }
        // check if light is illuminating any visible leafs
        if (numleafs)
@@ -2441,12 +2482,6 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        // make a list of lit entities and shadow casting entities
        numlightentities = 0;
        numshadowentities = 0;
-       // don't count the world unless some surfaces are actually lit
-       if (numsurfaces)
-       {
-               lightentities[numlightentities++] = r_refdef.worldentity;
-               shadowentities[numshadowentities++] = r_refdef.worldentity;
-       }
        // add dynamic entities that are lit by the light
        if (r_drawentities.integer)
        {
@@ -2491,7 +2526,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        }
 
        // return if there's nothing at all to light
-       if (!numlightentities)
+       if (!numlightentities && !numsurfaces)
                return;
 
        // don't let sound skip if going slow
@@ -2504,7 +2539,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        r_refdef.stats.lights++;
 
        usestencil = false;
-       if (numshadowentities && rtlight->shadow && (rtlight->isstatic ? r_refdef.rtworldshadows : r_refdef.rtdlightshadows))
+       if (numsurfaces + numshadowentities && rtlight->shadow && (rtlight->isstatic ? r_refdef.rtworldshadows : r_refdef.rtdlightshadows))
        {
                // draw stencil shadow volumes to mask off pixels that are in shadow
                // so that they won't receive lighting
@@ -2512,8 +2547,10 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                {
                        usestencil = true;
                        R_Shadow_RenderMode_StencilShadowVolumes();
+                       if (numsurfaces)
+                               R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
                        for (i = 0;i < numshadowentities;i++)
-                               R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
+                               R_Shadow_DrawEntityShadow(shadowentities[i]);
                }
 
                // optionally draw visible shape of the shadow volumes
@@ -2521,15 +2558,19 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                if (r_showshadowvolumes.integer)
                {
                        R_Shadow_RenderMode_VisibleShadowVolumes();
+                       if (numsurfaces)
+                               R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
                        for (i = 0;i < numshadowentities;i++)
-                               R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
+                               R_Shadow_DrawEntityShadow(shadowentities[i]);
                }
        }
 
-       if (numlightentities)
+       if (numsurfaces + numlightentities)
        {
                // draw lighting in the unmasked areas
                R_Shadow_RenderMode_Lighting(usestencil, false);
+               if (numsurfaces)
+                       R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
                for (i = 0;i < numlightentities;i++)
                        R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
 
@@ -2538,6 +2579,8 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                if (r_showlighting.integer)
                {
                        R_Shadow_RenderMode_VisibleLighting(usestencil && !r_showdisabledepthtest.integer, false);
+                       if (numsurfaces)
+                               R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
                        for (i = 0;i < numlightentities;i++)
                                R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
                }
index 164845900202604ced6c5b5acd8e8fbee1ddabc4..1c4aeb2ebb8be27a9ce0d8ad4b39463d5027ade9 100644 (file)
@@ -29,6 +29,7 @@ extern cvar_t r_shadow_realtime_world_compileshadow;
 extern cvar_t r_shadow_realtime_world_compilesvbsp;
 extern cvar_t r_shadow_realtime_world_compileportalculling;
 extern cvar_t r_shadow_scissor;
+extern cvar_t r_shadow_culltriangles;
 extern cvar_t r_shadow_shadow_polygonfactor;
 extern cvar_t r_shadow_shadow_polygonoffset;
 extern cvar_t r_shadow_singlepassvolumegeneration;
@@ -39,7 +40,7 @@ extern cvar_t gl_ext_stenciltwoside;
 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, const vec3_t projectdirection, float projectdistance, int nummarktris, const int *marktris);
 void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, const vec3_t projectdirection, const vec3_t lightmins, const vec3_t lightmaxs, const vec3_t surfacemins, const vec3_t surfacemaxs);
-void R_Shadow_RenderSurfacesLighting(int numsurfaces, msurface_t **surfacelist);
+void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i);
 void R_Shadow_RenderMode_Begin(void);
 void R_Shadow_RenderMode_ActiveLight(rtlight_t *rtlight);
 void R_Shadow_RenderMode_Reset(void);
diff --git a/todo b/todo
index 5df29733bfda0feb5c7df5acc5062772f98a405e..ef0ea44ce1cf2f3ce541fc01a7228a1d3fac5138 100644 (file)
--- a/todo
+++ b/todo
@@ -74,6 +74,7 @@
 0 bug darkplaces renderer: if an animated model is entirely transparent, the RSurf_ActiveModelEntity call updating vertices is completely wasted
 0 bug darkplaces server: SV_PushMove is ignoring model type in its angles_x handling, where as the renderer checks only model type to determine angles_x handling (Urre)
 0 bug darkplaces server: SV_PushMove's call to SV_ClipMoveToEntity should do a trace, not just a point test, to support hollow pusher models (Urre)
+0 bug darkplaces server: savegames do not save precaches, which means that automatic precaching frequently results in invalid modelindex values when reloading the savegame, and this bug also exists in many quake mods that randomly choose multiple variants of a monster, each with separate precaches, resulting in a different precache order when reloading the savegame
 0 bug darkplaces wgl client: during video mode setup, sometimes another application's window becomes permanently top most, not darkplaces' fullscreen window, why? (tZork)
 0 bug darkplaces windows sound: freezing on exit sometimes when freeing sound buffer during sound shutdown (Black)
 0 bug darkplaces: q1bsp loader computes wrong submodel size for submodels with no surfaces, such as a func_wall comprised entirely of SKIP or CAULK brushes (neg|ke)